\documentclass[twoside,11pt]{starlink} % ? Specify used packages % ? End of specify used packages % ----------------------------------------------------------------------------- % ? Document identification % Fixed part \stardoccategory {Starlink User Note} \stardocinitials {SUN} \stardocsource {sun\stardocnumber} % Variable part - replace [xxx] as appropriate. c+ \stardocnumber {211.28} c- f+ \stardocnumber {210.28} f- \stardocauthors {R.F. Warren-Smith \& D.S. Berry} \stardocdate {17th October 2017} \stardoctitle {AST\linebreak% A Library for Handling\linebreak% World Coordinate Systems\linebreak% in Astronomy} \stardoccopyright {Copyright (C) 2017 East Asian Observatory} \stardocversion {V} c+ \stardocmanual {Programmer's Guide\\(C Version)} \startitlepic{ \includegraphics[width=0.25\textwidth]{sun211_figures/fronta}~~~~~\hfill \includegraphics[width=0.25\textwidth]{sun211_figures/frontb}~~~~~\hfill \includegraphics[width=0.25\textwidth]{sun211_figures/frontc} } c- f+ \stardocmanual {Programmer's Guide\\(Fortran Version)} \startitlepic{ \includegraphics[width=0.25\textwidth]{sun210_figures/fronta}~~~~~\hfill \includegraphics[width=0.25\textwidth]{sun210_figures/frontb}~~~~~\hfill \includegraphics[width=0.25\textwidth]{sun210_figures/frontc} } f- \stardocabstract { The AST library provides a comprehensive range of facilities for attaching world coordinate systems to astronomical data, for retrieving and interpreting that information in a variety of formats, including FITS-WCS, and for generating graphical output based on it. This programmer's manual should be of interest to anyone writing astronomical applications which need to manipulate coordinate system data, especially celestial or spectral coordinate systems. AST is portable and environment-independent. } % ? End of document identification % ----------------------------------------------------------------------------- % ? Document specific \providecommand or \newenvironment commands. \providecommand{\appref}[1]{Appendix~\ref{#1}} \providecommand{\secref}[1]{\S\ref{#1}} \providecommand{\fitskey}[3]{{#1}&{#2}&{#3}\\} % Use {\tt ... } as \texttt{...} does not work if there are new lines in #1 \providecommand{\sstsynopsis}[1]{\sstdiytopic{Synopsis}{\tt #1}} % Format the constructor section. \providecommand{\sstconstructor}[1]{\sstdiytopic{Constructor Function}{#1}} % ? End of document specific commands % ----------------------------------------------------------------------------- % Title Page. % =========== \begin{document} \scfrontmatter \begin{center} c+ \emph{This is the C version of this document.\\ For the Fortran version, please see \xref{SUN/210}{sun210}{}.} c- f+ \emph{This is the Fortran version of this document.\\ For the C version, please see \xref{SUN/211}{sun211}{}.} f- \end{center} % Main text of document. \vspace{7mm} \section{Introduction} Welcome to the AST library. If you are writing software for astronomy and need to use celestial coordinates (\emph{e.g.}\ RA and Dec), spectral coordinates (\emph{e.g.}\ wavelength, frequency, \emph{etc.}), or other coordinate system information, then this library should be of interest. It provides solutions for most of the problems you will meet and allows you to write robust and flexible software. It is able to read and write WCS information in a variety of formats, including \htmladdnormallink{FITS-WCS}{http://fits.gsfc.nasa.gov/fits_wcs.html}. %\subsection{TBW---What is a World Coordinate System?} \subsection{What Problems Does AST Tackle?} Here are some of the main problems you may face when handling world coordinate system (WCS) information and the solutions that AST provides: \begin{description} \item[1. The Variety of Coordinate Systems]\mbox{}\\ Astronomers use a wide range of differing coordinate systems to describe positions within a variety of physical domains. For instance, there are a large number of celestial coordinate systems in use within astronomy to describe positions on the sky. Understanding these, and knowing how to convert coordinates between them, can require considerable expertise. It can also be difficult to decide which of them your software should support. The same applies to coordinate systems describing other domains, such as position within an electro-magnetic spectrum. \textbf{Solution.} AST has built-in knowledge of many coordinate systems and allows you to convert freely between them without specialist knowledge. This avoids the need to embed details of specific coordinate systems in your software. You also benefit automatically when new coordinate systems are added to AST. \item[2. Storing and Retrieving WCS Information]\mbox{}\\ Storing coordinate system information in astronomical datasets and retrieving it later can present a considerable challenge. Typically, it requires knowledge of rather complex conventions (\emph{e.g.}\ FITS) which are low-level, often mis-interpreted and may be subject to change. Exchanging information with other software systems is further complicated by the number of different conventions in use. \textbf{Solution.} AST combines a unifying high-level description of WCS information with the ability to save and restore this using a variety of formats. Details of the formats, which include FITS, are handled internally by AST. This frees you from the need to understand them or embed the details in your software. Again, you benefit automatically when new formats are added to AST. \item[3. Generating Graphical Output]\mbox{}\\ Producing graphical displays involving curvilinear coordinate systems, such as celestial coordinate grids, can be complicated. Particular difficulties arise when handling large areas of sky, the polar regions and discontinuous (\emph{e.g.}\ segmented) sky projections. Even just numbering and labelling curvilinear axes is rarely straightforward. \textbf{Solution.} AST provides plotting facilities especially designed for use with curvilinear coordinate systems. These include the plotting of axes and complete labelled coordinate grids. A large number of options are provided for tailoring the output to your specific needs. Three dimensional coordinate grids can also be produced. \item[4. Aligning Data from Different Sources]\mbox{}\\ One of the main uses of coordinate systems is to facilitate the inter-comparison of data from different sources. A typical use might be to plot (say) radio contours over an optical image. In practice, however, different celestial coordinate systems may have been used, making accurate alignment far from simple. \textbf{Solution} AST provides a one-step method of aligning datasets, searching for all possible intermediate coordinate systems. This makes it simple to directly inter-relate the pixel coordinates of different datasets. \item[5. Handling Different Types of Coordinate System]\mbox{}\\ Not all coordinate systems used in astronomy are celestial ones, so if you are writing general-purpose software such as (say) a display tool, you may also need to handle axes representing wavelength, distance, time or whatever else comes along. Obviously, you would prefer not to handle each one as a special case. \textbf{Solution} AST uses the same flexible high-level model to describe all types of coordinate system. This allows you to write software that handles different kinds of coordinate axis without introducing special cases. \end{description} \subsection{Other Design Objectives} As well as its scientific objectives, the AST library's design includes a number of technical criteria intended to make it applicable to as wide a range of projects as possible. The main considerations are described here: \begin{enumerate} \item {\bf{Minimum Software Dependencies.}} The AST library depends on no other other software\footnote{It comes with bundled copies of the ERFA and \xref{Starlink PAL libraries}{sun268}{} which are built at the same time as the other AST internal libraries. Alternatively, external PAL and ERFA libraries may be used by specifying the ``\texttt{--with-external\_pal}'' option when configuring AST}. \item {\bf{Environment Independence.}} AST is designed so that it can operate in a variety of ``programming environments'' and is not tied to any particular one. To allow this, it uses simple, flexible interfaces to obtain the following services: \begin{itemize} \item {\bf{Data Storage.}} Data I/O operations are based on text and/or FITS headers. This makes it easy to interface to a wide variety of astronomical data formats in a machine-independent way. \item {\bf{Graphics.}} Graphical output is produced \emph{via} a simple generic graphics interface, which may easily be re-implemented over different graphics systems. AST provides a default implementation based on the widely-used PGPLOT graphics system (\xref{SUN/15}{sun15}{}). \item {\bf{Error Handling.}} Error messages are written to standard error by default, but go through a simple generic interface similar to that used for graphics (above). This permits error message delivery \emph{via} other routes when necessary (\emph{e.g.} in a graphical interface). \end{itemize} \item {\bf{Multiple Language Support.}} AST has been designed to be called from more than one language. c+ Both C and Fortran interfaces are available (see \xref{SUN/210}{sun210}{} for the Fortran version) c- f+ Both Fortran and C interfaces are available (see \xref{SUN/211}{sun211}{} for the C version) f- and use from C$++$ is also straightforward if the C interface is included using: \begin{small} \begin{terminalv} extern "C" { #include "ast.h" } \end{terminalv} \end{small} A JNI interface (known as ``JNIAST'' - see \url{http://www.starlink.ac.uk/jniast/}) has also been developed by Starlink which allows AST to be used from Java. \item {\bf{Object Oriented Design.}} AST uses ``object oriented'' techniques internally in order to provide a flexible and easily-extended programming model. A fairly traditional calling interface is provided, however, so that the library's facilities are easily accessible to programmers using c+ C and Fortran. c- f+ Fortran and C. f- \item {\bf{Portability.}} AST is implemented entirely in ANSI standard C and, when called \emph{via} its C interface, makes no explicit use of any machine-dependent facilities. The Fortran interface is, unavoidably, machine dependent. However, the potential for problems has been minimised by encapsulating the interface layer in a compact set of C macros which facilitate its transfer to other platforms. No Fortran compiler is needed to build the library. Currently, AST is supported by Starlink on PC~Linux, Sun~Solaris and Tru64~Unix (formerly DEC~UNIX) platforms. \end{enumerate} \subsection{What Does ``AST'' Stand For?} The library name ``AST'' stands for ``ASTrometry Library''. The name arose when it was thought that knowledge of ``astrometry'' (\emph{i.e.}\ celestial coordinate systems) would form the bulk of the library. In fact, it turns out that astrometry forms only a minor component, but the name AST has stuck. \cleardoublepage \section{Overview of AST Concepts} This section presents a brief overview of AST concepts. It is intended as a basic orientation course before you move on to the more technical considerations in subsequent sections. \subsection{\label{ss:mappingoverview}Relationships Between Coordinate Systems} The relationships between coordinate systems are represented in AST by Objects called Mappings. A Mapping does not represent a coordinate system itself, but merely the process by which you move from one coordinate system to another related one. A convenient picture of a Mapping is as a ``black box'' (Figure~\ref{fig:mapping}) into which you can feed sets of coordinates. \begin{figure}[bhtp] \begin{center} c+ \includegraphics[width=0.7\textwidth]{sun211_figures/mapping} c- f+ \includegraphics[width=0.7\textwidth]{sun210_figures/mapping} f- \caption{A Mapping viewed as a ``black box'' for transforming coordinates.} \label{fig:mapping} \end{center} \end{figure} For each set you feed in, the Mapping returns a corresponding set of transformed coordinates. Since each set of coordinates represents a point in a coordinate space, the Mapping acts to inter-relate corresponding positions in the two spaces, although what these spaces represent is unspecified. Notice that a Mapping need not have the same number of input and output coordinates. That is, the two coordinate spaces which it inter-relates need not have the same number of dimensions. In many cases, the transformation can, in principle, be performed in either direction: either from the \emph{input} coordinate space to the \emph{output}, or \emph{vice versa}. The first of these is termed the \emph{forward} transformation and the other the \emph{inverse} transformation. \textbf{Further reading:} For a more complete discussion of Mappings, see~\secref{ss:mappings}. \subsection{\label{ss:mappingselection}Mappings Available} The basic concept of a Mapping (\secref{ss:mappingoverview}) is rather generic and obviously it is necessary to have specific Mappings that implement specific relationships between coordinate systems. AST provides a range of these, to perform transformations such as the following and, where appropriate, their inverses: \begin{itemize} \item Conversions between various celestial coordinate systems (the SlaMap). \item Conversions between various spectral coordinate systems (the SpecMap and GrismMap). \item Conversions between various time systems (the TimeMap). \item Conversion between 2-dimensional spherical celestial coordinates (longitude and latitude) and a 3-dimensional vectorial positions (the SphMap). \item Various projections of the celestial sphere on to 2-dimensional coordinate spaces---\emph{i.e.}\ map projections (the DssMap and WcsMap). \item Permutation, introduction and elimination of coordinates (the PermMap). \item Various linear coordinate transformations (the MatrixMap, WinMap, ShiftMap and ZoomMap). \item General N-dimensional polynomial transformations (the PolyMap and ChebyMap). \item Lookup tables (the LutMap). c+ \item General-purpose transformations expressed using arithmetic operations and functions similar to those available in C (the MathMap). c- f+ \item General-purpose transformations expressed using arithmetic operations and functions similar to those available in Fortran (the MathMap). f- c+ \item Transformations for internal use within a program, based on private transformation functions which you write yourself in C (the IntraMap). c- f+ \item Transformations for internal use within a program, based on private transformation routines which you write yourself in Fortran (the IntraMap). f- \end{itemize} \textbf{Further reading:} For a more complete description of each of the Mappings mentioned above, see its entry in \appref{ss:classdescriptions}. In addition, see the discussion of the PermMap in \secref{ss:permmapexample}, the UnitMap in \secref{ss:unitmapexample} and the IntraMap in \secref{ss:intramaps}. The ZoomMap is used as an example throughout \secref{ss:primer}. \subsection{\label{ss:cmpmapoverview}Compound Mappings} The Mappings described in \secref{ss:mappingselection} provide a set of basic building blocks from which more complex Mappings may be constructed. The key to doing this is a type of Mapping called a CmpMap, or compound Mapping. A CmpMap's role is, in principle, very simple: it allows any other pair of Mappings to be joined together into a single entity which behaves as if it were a single Mapping. A CmpMap is therefore a container for another pair of Mappings. A pair of Mappings may be combined using a CmpMap in either of two ways. The first of these, \emph{in series}, is illustrated in Figure~\ref{fig:seriescmpmap}. \begin{figure} \begin{center} c+ \includegraphics[width=0.7\textwidth]{sun211_figures/series} c- f+ \includegraphics[width=0.7\textwidth]{sun210_figures/series} f- \caption[A CmpMap composed of two component Mappings joined in series]{A CmpMap (compound Mapping) composed of two component Mappings joined in series. The output coordinates of the first Mapping feed into the input coordinates of the second one, so that the whole entity behaves like a single Mapping.} \label{fig:seriescmpmap} \end{center} \end{figure} Here, the transformations implemented by each component Mapping are performed one after the other, with the output from the first Mapping feeding into the second. The second way, \emph{in parallel}, is shown in Figure~\ref{fig:parallelcmpmap}. \begin{figure} \begin{center} c+ \includegraphics[width=0.5\textwidth]{sun211_figures/parallel} c- f+ \includegraphics[width=0.5\textwidth]{sun210_figures/parallel} f- \caption[A CmpMap composed of two Mappings joined in parallel.]{A CmpMap composed of two Mappings joined in parallel. Each component Mapping acts on a complementary subset of the input and output coordinates.} \label{fig:parallelcmpmap} \end{center} \end{figure} In this case, each Mapping acts on a complementary subset of the input and output coordinates.\footnote{A pair of Mappings can be combined in a third way using a TranMap. A TranMap allows the forward transformation of one Mapping to be combined with the inverse transformation of another to produce a single Mapping.} The CmpMap forms the key to building arbitrarily complex Mappings because it is itself a form of Mapping. This means that a CmpMap may contain other CmpMaps as components (\emph{e.g.}\ Figure~\ref{fig:complexcmpmap}). This nesting of CmpMaps can be repeated indefinitely, so that complex Mappings may be built in a hierarchical manner out of simper ones. \begin{figure} \begin{center} c+ \includegraphics[width=0.65\textwidth]{sun211_figures/complex} c- f+ \includegraphics[width=0.65\textwidth]{sun210_figures/complex} f- \caption[CmpMaps may be nested in order to construct complex Mappings out of simpler building blocks.]{CmpMaps (compound Mappings) may be nested in order to construct complex Mappings out of simpler building blocks.} \label{fig:complexcmpmap} \end{center} \end{figure} This gives AST great flexibility in the coordinate transformations it can describe. \textbf{Further reading:} For a more complete description of CmpMaps, see \secref{ss:cmpmaps}. Also see the CmpMap entry in \appref{ss:classdescriptions}. \subsection{Representing Coordinate Systems} While Mappings (\secref{ss:mappingoverview}) represent the relationships between coordinate systems in AST, the coordinate systems themselves are represented by Objects called Frames (Figure~\ref{fig:frames}). \begin{figure} \begin{center} c+ \includegraphics[width=0.55\textwidth]{sun211_figures/frames} c- f+ \includegraphics[width=0.55\textwidth]{sun210_figures/frames} f- \caption[Representing coordinate systems as Frames.]{(a) A basic Frame is used to represent a Cartesian coordinate system, here 2-dimensional. (b) A SkyFrame represents a (spherical) celestial coordinate system. (c) The axis order of any Frame may be permuted to match the coordinate space it describes.} \label{fig:frames} \end{center} \end{figure} A Frame is similar in concept to the frame you might draw around a graph. It contains information about the labels which appear on the axes, the axis units, a title, knowledge of how to format the coordinate values on each axis, \emph{etc.} An AST Frame is not, however, restricted to two dimensions and may have any number of axes. c+ A basic Frame may be used to represent a Cartesian coordinate system by setting values for its \emph{attributes} (all AST Objects have values associated with them called attributes, which may be set and enquired). Usually, this would involve setting appropriate axis labels and units, for example. Functions are provided for use with Frames to perform operations such as formatting coordinate values as text, calculating distances between points, interchanging axes, \emph{etc.} c- f+ A basic Frame may be used to represent a Cartesian coordinate system by setting values for its \emph{attributes} (all AST Objects have values associated with them called attributes, which may be set and enquired). Usually, this would involve setting appropriate axis labels and units, for example. Routines are provided for use with Frames to perform operations such as formatting coordinate values as text, calculating distances between points, interchanging axes, \emph{etc.} f- There are several more specialised forms of Frame, which provide the additional functionality required when handling coordinates within some specific physical domain. This ranges from tasks such as formatting axis values, to complex tasks such as determining the transformation between any pair of related coordinate systems. For instance, the SkyFrame (Figure~\ref{fig:frames}b,c), represents celestial coordinate systems, the SpecFrame represents spectral coordinate systems, and the TimeFrame represents time coordinate systems. All these provide a wide range of different systems for describing positions within their associated physical domain, and these may be selected by setting appropriate attributes. As with compound Mappings (\secref{ss:cmpmapoverview}), it is possible to merge two Frames together to form a compound Frame, or CmpFrame, in which both sets of axes are combined. One could, for example, have celestial coordinates on two axes and an unrelated coordinate (wavelength, perhaps) on a third (Figure~\ref{fig:cmpframe}). Knowledge of the relationships between the axes is preserved internally by the process of constructing the CmpFrame which represents them. \begin{figure} \begin{center} c+ \includegraphics[width=0.4\textwidth]{sun211_figures/cmpframe} c- f+ \includegraphics[width=0.4\textwidth]{sun210_figures/cmpframe} f- \caption[A CmpFrame (compound Frame) formed by combining two simpler Frames.]{A CmpFrame (compound Frame) formed by combining two simpler Frames. Note how the special relationship which exists between the RA and Dec axes is preserved within this data structure. As with compound Mappings (Figure~\ref{fig:complexcmpmap}), CmpFrames may be nested in order to build more complex Frames.} \label{fig:cmpframe} \end{center} \end{figure} \textbf{Further reading:} For a more complete description of Frames see \secref{ss:frames}, for SkyFrames see \secref{ss:skyframes} and for SpecFrames see \secref{ss:specframes}. Also see the Frame, SkyFrame, SpecFrame, TimeFrame and CmpFrame entries in \appref{ss:classdescriptions}. \subsection{Networks of Coordinate Systems} Mappings and Frames may be connected together to form networks called FrameSets, which are used to represent sets of inter-related coordinate systems (Figure~\ref{fig:frameset}). \begin{figure} \begin{center} c+ \includegraphics[width=0.65\textwidth]{sun211_figures/frameset} c- f+ \includegraphics[width=0.65\textwidth]{sun210_figures/frameset} f- \caption[A FrameSet is a network of Frames.]{A FrameSet is a network of Frames inter-connected by Mappings such that there is exactly one conversion path, \emph{via} Mappings, between any pair of Frames.} \label{fig:frameset} \end{center} \end{figure} A FrameSet may be extended by adding a new Frame to it, together with an associated Mapping which relates the new coordinate system to one which is already present. This process ensures that there is always exactly one path, \emph{via} Mappings, between any pair of Frames. A function is provided for identifying this path and returning the complete Mapping. One of the Frames in a FrameSet is termed its \emph{base} Frame. This underlies the FrameSet's purpose, which is to calibrate datasets and other entities by attaching coordinate systems to them. In this context, the base Frame represents the ``native'' coordinate system (for example, the pixel coordinates of an image). Similarly, one Frame is termed the \emph{current} Frame and represents the ``currently-selected'' coordinates. It might, typically, be a celestial or spectral coordinate system and would be used during interactions with a user, as when plotting axes on a graph or producing a table of results. Other Frames within the FrameSet represent a library of alternative coordinate systems which a software user can select by making them current. \textbf{Further reading:} For a more complete description of FrameSets, see \secref{ss:framesets} and \secref{ss:fshigher}. Also see the FrameSet entry in \appref{ss:classdescriptions}. \subsection{Input/Output Facilities} AST allows you to convert any kind of Object into a stream of text which contains a full description of that Object. This text may be written out by one program and read back in by another, thus allowing the original Object to be reconstructed. The filter which converts Objects into text and back again is itself a kind of Object, called a Channel. A Channel provides a number of options for controlling the information content of the text, such as the addition of comments for human interpretation. It is also possible to intercept the text being processed by a Channel so that it may be redirected to/from any chosen external data store, such as a text file, an astronomical dataset, or a network connection. The text format used by the basic Channel class is peculiar to the AST library - no other software will understand it. However, more specialised forms of Channel are provided which use text formats more widely understood. To further facilitate the storage of coordinate system information in astronomical datasets, a more specialised form of Channel called a FitsChan is provided. Instead of using free-format text, a FitsChan converts AST Objects to and from FITS header cards. It also allows the information to be encoded in the FITS cards in a number of ways (called \emph{encodings}), so that WCS information from a variety of sources can be handled. Another sub-class of Channel, called XmlChan, is a specialised form of Channel that stores the text in the form of XML markup. Currently, two markup formats are provided by the XmlChan class, one is closely related to the text format produced by the basic Channel class (currently, no schema or DTD is available describing this format). The other is a subset of an early draft of the IVOA Space-Time-Coordinates XML (STC-X) schema (V1.20) described at \url{http://www.ivoa.net/Documents/WD/STC/STC-20050225.html }\footnote{XML documents which use only the subset of the STC schema supported by AST can be read by the XmlChan class to produce corresponding AST objects (subclasses of the Stc class). However, the reverse is not possible. That is, AST objects can not currently be written out in the form of STC documents.}. The version of STC-X that has been adopted by the IVOA differs in several significant respects from V1.20, and therefore this XmlChan format is of historical interest only. Finally, the StcsChan class provides facilities for reading and writing IVOA STC-S region descriptions. STC-S (see \url{http://www.ivoa.net/Documents/latest/STC-S.html}) is a linear string syntax that allows simple specification of STC metadata. AST supports a subset of the STC-S specification, allowing an STC-S description of a region within an AST-supported astronomical coordinate system to be converted into an equivalent AST Region object, and vice-versa. \textbf{Further reading:} For a more complete description of Channels see \secref{ss:channels} and for FitsChans see \secref{ss:nativefits} and \secref{ss:foreignfits}. Also see the Channel and FitsChan entries in \appref{ss:classdescriptions} and the Encoding entry in \appref{ss:attributedescriptions}. \subsection{Producing Graphical Output} Two dimensional graphical output is supported by a specialised form of FrameSet called a Plot, whose base Frame corresponds with the native coordinates of the underlying graphics system. Plotting operations are specified in \emph{physical coordinates} which correspond with the Plot's current Frame. Typically, this might be a celestial coordinate system. Three dimensional plotting is also supported, via the Plot3D class - sub-class of Plot. Operations, such as drawing lines, are automatically transformed from physical to graphical coordinates before plotting, using an adaptive algorithm which ensures smooth curves (because the transformation is usually non-linear). ``Missing'' coordinates (\emph{e.g.}\ graphical coordinates which do not project on to the celestial sphere), discontinuities and generalised clipping are all consistently handled. It is possible, for example, to plot in equatorial coordinates and clip in galactic coordinates. The usual plotting operations are provided (text, markers), but a geodesic curve replaces the primitive straight line element. There is also a separate function for drawing axis lines, since these are normally not geodesics. In addition to drawing coordinate grids over an area of the sky, another common use of the Plot class is to produce line plots such as flux against wavelength, displacement again time, \emph{etc}. For these situations the current Frame of the Plot would be a compound Frame (CmpFrame) containing a pair of 1-dimensional Frames - the first representing the X axis quantity (wavelength, time, etc), and the second representing the Y axis quantity (flux, displacement, etc). The Plot class includes an option for axes to be plotted logarithmically. Perhaps the most useful graphics function available is for drawing fully annotated coordinate grids (\emph{e.g.}\ Figure~\ref{fig:gridplot}). \begin{figure} \begin{center} c+ \includegraphics[width=0.6\textwidth]{sun211_figures/gridplot_bw} c- f+ \includegraphics[width=0.6\textwidth]{sun210_figures/gridplot_bw} f- \caption[A labelled coordinate grid for an all-sky zenithal equal area projection in ecliptic coordinates.]{A labelled coordinate grid for an all-sky zenithal equal area projection in ecliptic coordinates. This was composed and drawn \emph{via} a Plot using a c+ single function call.} c- f+ single subroutine call.} f- \label{fig:gridplot} \end{center} \end{figure} This uses a general algorithm which does not depend on knowledge of the coordinates being represented, so can also handle programmer-defined coordinate systems. Grids for all-sky projections, including polar regions, can be drawn and most aspects of the output (colour, line style, \emph{etc.}) can be adjusted by setting appropriate Plot attributes. \textbf{Further reading:} For a more complete description of Plots and how to produce graphical output, see \secref{ss:plots}. Also see the Plot entry in \appref{ss:classdescriptions}. \cleardoublepage \section{\label{ss:howto}How To\ldots} For those of you with a plane to catch, this section provides some instant templates and recipes for performing the most commonly-required operations using AST, but without going into detail. The examples given (sort of) follow on from each other, so you should be able to construct a variety of programs by piecing them together. Note that some of them appear longer than they actually are, because we have included plenty of comments and a few options that you probably won't need. If any of this material has you completely baffled, then you may want to read the introduction to AST programming concepts in \secref{ss:primer} first. Otherwise, references to more detailed reading are given after each example, just in case they don't quite do what you want. \subsection{\ldots Obtain and Install AST} The AST library is available both as a stand-alone package and also as part of the Starlink Software Collection\footnote{The Starlink Software Collection can be downloaded from \url{http://www.starlink.ac.uk/Download/}.}. If your site has the Starlink Software Collection installed then AST should already be available. If not, you can download the AST library by itself from \url{http://www.starlink.ac.uk/ast/}. \subsection{\ldots Structure an AST Program} An AST program normally has the following structure: c+ \begin{small} \begin{terminalv} /* Include the interface to the AST library. */ #include "ast.h" /* Main program (or could be any function). */ main () { /* Enclose the parts which use AST between the astBegin and astEnd macros. */ astBegin; astEnd; } \end{terminalv} \end{small} c- f+ \small \begin{terminalv} * Include the interface to the AST library. INCLUDE 'AST_PAR' * Declare an integer status variable. INTEGER STATUS * Initialise the status to zero. STATUS = 0 * Enclose the parts which use AST between AST_BEGIN and AST_END calls. CALL AST_BEGIN( STATUS ) CALL AST_END( STATUS ) END \end{terminalv} \normalsize f- c+ The use of astBegin and astEnd is optional, but has the effect of tidying up after you have finished using AST, so is normally recommended. For more details of this, see \secref{ss:contexts}. For details of how to access the ``ast.h'' header file, see \secref{ss:accessingheaderfile}. c- f+ The use of AST\_BEGIN and AST\_END is optional, but has the effect of tidying up after you have finished using AST, so is normally recommended. For more details of this, see \secref{ss:contexts}. For details of how to access the AST\_PAR include file, see \secref{ss:accessingheaderfile}. f- \subsection{\label{ss:howtobuild}\ldots Build an AST Program} To build a simple AST program that doesn't use graphics, use: c+ \begin{small} \begin{terminalv} cc program.c -L/star/lib -I/star/include `ast_link` -o program \end{terminalv} \end{small} c- f+ \begin{small} \begin{terminalv} f77 program.f -L/star/lib -I/star/include `ast_link` -o program \end{terminalv} \end{small} On Linux systems you should usually use \verb+g77 -fno-second-underscore+ in place of \verb+f77+ - see \xref{``Software development on Linux''}{sun212} {software_development_on_linux} in \xref{SUN/212}{sun212}{}. f- To build a program which uses PGPLOT for graphics, use: c+ \begin{small} \begin{terminalv} cc program.c -L/star/lib `ast_link -pgplot` -o program \end{terminalv} \end{small} c- f+ \begin{small} \begin{terminalv} f77 program.f -L/star/lib `ast_link -pgplot` -o program \end{terminalv} \end{small} again using \verb+g77 -fno-second-underscore+ in place of \verb+f77+ on Linux systems. f- c+ For more details about accessing the ``ast.h'' header file, see \secref{ss:accessingheaderfile}. For more details about linking programs, see \secref{ss:linking} and the description of the ``ast\_link'' command in \appref{ss:commanddescriptions}. c- f+ For more details about accessing AST include files, see \secref{ss:accessingheaderfile}. For more details about linking programs, see \secref{ss:linking} and the description of the ``ast\_link'' command in \appref{ss:commanddescriptions}. f- \subsection{\label{ss:howtoreadwcs}\ldots Read a WCS Calibration from a Dataset} c+ Precisely how you extract world coordinate system (WCS) information from a dataset obviously depends on what type of dataset it is. Usually, however, you should be able to obtain a set of FITS header cards which contain the WCS information (and probably much more besides). Suppose that ``cards'' is a pointer to a string containing a complete set of concatenated FITS header cards (such as produced by the CFITSIO function fits\_hdr2str). Then proceed as follows: \begin{small} \begin{terminalv} fitsfile *fptr; AstFitsChan *fitschan; AstFrameSet *wcsinfo; char *header; int nkeys, status; ... /* Obtain all the cards in the header concatenated into a single dynamically allocated null-terminated character string. Note, we do not exclude any cards since we may later modify the WCS information within the header and consequently want to write the entire header out again. */ if( fits_hdr2str( fptr, 0, NULL, 0, &header, &nkeys, &status ) ) printf(" Error getting header\n"); ... /* Header obtained succesfully... */ } else { /* Create a FitsChan and fill it with FITS header cards. */ fitschan = astFitsChan( NULL, NULL, "" ); astPutCards( fitschan, header ); /* Free the memory holding the concatenated header cards. */ header = free( header ); /* Read WCS information from the FitsChan. */ wcsinfo = astRead( fitschan ); ... \end{terminalv} \end{small} c- f+ Precisely how you extract world coordinate system (WCS) information from a dataset obviously depends on what type of dataset it is. Usually, however, you should be able to obtain a set of FITS header cards which contain the WCS information (and probably much more besides). Suppose that CARDS is an array of character strings containing a complete set of FITS header cards and NCARD is the number of cards. Then proceed as follows: \small \begin{terminalv} INTEGER FITSCHAN, ICARD, NCARD, WCSINFO CHARACTER * ( 80 ) CARDS( NCARD ) ... * Create a FitsChan and fill it with FITS header cards. FITSCHAN = AST_FITSCHAN( AST_NULL, AST_NULL, ' ', STATUS ) DO 1 ICARD = 1, NCARD CALL AST_PUTFITS( FITSCHAN, CARDS( ICARD ), .FALSE., STATUS ) 1 CONTINUE * Rewind the FitsChan and read WCS information from it. CALL AST_CLEAR( FITSCHAN, 'Card', STATUS ) WCSINFO = AST_READ( FITSCHAN, STATUS ) \end{terminalv} \normalsize f- c+ The result should be a pointer, ``wcsinfo'', to a FrameSet which contains the WCS information. This pointer can now be used to perform many useful tasks, some of which are illustrated in the following recipes. c- f+ The result should be a pointer, WCSINFO, to a FrameSet which contains the WCS information. This pointer can now be used to perform many useful tasks, some of which are illustrated in the following recipes. f- c+ Some datasets which do not easily yield FITS header cards may require a different approach, possibly involving use of a Channel or XmlChan (\secref{ss:channels}) rather than a FitsChan. In the case of the Starlink NDF data format, for example, all the above may be replaced by a single call to the function \xref{ndfGtwcs}{sun33}{ndfGtwcs}---see \xref{SUN/33}{sun33}{}. The whole process can probably be encapsulated in a similar way for most data systems, whether they use FITS header cards or not. c- f+ Some datasets which do not easily yield FITS header cards may require a different approach, possibly involving use of a Channel or XmlChan (\secref{ss:channels}) rather than a FitsChan. In the case of the Starlink NDF data format, for example, all the above may be replaced by a single call to the routine \xref{NDF\_GTWCS}{sun33}{NDF_GTWCS}---see \xref{SUN/33}{sun33}{}. The whole process can probably be encapsulated in a similar way for most other data systems, whether they use FITS header cards or not. f- For more details about reading WCS information from datasets, see \secref{ss:identifyingfitsencoding} and \secref{ss:readingforeignfits}. For a more general description of FitsChans and their use with FITS header cards, see \secref{ss:nativefits} and \secref{ss:foreignfits}. For more details about FrameSets, see \secref{ss:framesets} and \secref{ss:fshigher}. \subsection{\ldots Validate WCS Information} Once you have read WCS information from a dataset, as in \secref{ss:howtoreadwcs}, you may wish to check that you have been successful. The following will detect and classify the things that might possibly go wrong: c+ \begin{small} \begin{terminalv} #include ... if ( !astOK ) { } else if ( wcsinfo == AST__NULL ) { } else if ( strcmp( astGetC( wcsinfo, "Class" ), "FrameSet" ) ) { } else { } \end{terminalv} \end{small} c- f+ \small \begin{terminalv} IF ( STATUS .NE. 0 ) THEN ELSE IF ( WCSINFO .EQ. AST__NULL ) THEN ELSE IF ( AST_GETC( WCSINFO, 'Class', STATUS ) .NE. 'FrameSet' ) THEN ELSE END IF \end{terminalv} \normalsize f- c+ For more information about detecting errors in AST functions, see \secref{ss:errordetection}. For details of how to validate input data read by AST, see \secref{ss:validatinginput} and \secref{ss:readingforeignfits}. c- f+ For more information about detecting errors in AST routines, see \secref{ss:errordetection}. For details of how to validate input data read by AST, see \secref{ss:validatinginput} and \secref{ss:readingforeignfits}. f- \subsection{\ldots Display AST Data} If you have a pointer to any AST Object, you can display the data stored in that Object in textual form as follows: c+ \begin{small} \begin{terminalv} astShow( wcsinfo ); \end{terminalv} \end{small} c- f+ \begin{small} \begin{terminalv} CALL AST_SHOW( WCSINFO, STATUS ) \end{terminalv} \end{small} f- Here, we have used a pointer to the FrameSet which we read earlier (\secref{ss:howtoreadwcs}). The result is written to the program's standard output stream. This can be very useful during debugging. c+ For more details about using astShow, see \secref{ss:displayingobjects}. For information about interpreting the output, also see \secref{ss:textualoutputformat}. c- f+ For more details about using AST\_SHOW, see \secref{ss:displayingobjects}. For information about interpreting the output, also see \secref{ss:textualoutputformat}. f- \subsection{\label{ss:howtotransform}\ldots Convert Between Pixel and World Coordinates} You may use a pointer to a FrameSet, such as we read in \secref{ss:howtoreadwcs}, to transform a set of points between the pixel coordinates of an image and the associated world coordinates. If you are working in two dimensions, proceed as follows: c+ \begin{small} \begin{terminalv} double xpixel[ N ], ypixel[ N ]; double xworld[ N ], yworld[ N ]; ... astTran2( wcsinfo, N, xpixel, ypixel, 1, xworld, yworld ); \end{terminalv} \end{small} c- f+ \small \begin{terminalv} INTEGER N DOUBLE PRECISION XPIXEL( N ), YPIXEL( N ) DOUBLE PRECISION XWORLD( N ), YWORLD( N ) ... CALL AST_TRAN2( WCSINFO, N, XPIXEL, YPIXEL, .TRUE., : XWORLD, YWORLD, STATUS ) \end{terminalv} \normalsize f- c+ Here, N is the number of points to be transformed, ``xpixel'' and ``ypixel'' hold the pixel coordinates, and ``xworld'' and ``yworld'' receive the returned world coordinates.\footnote{By pixel coordinates, we mean a coordinate system in which the first pixel in the image is centred on (1,1) and each pixel is a unit square. Note that the world coordinates will not necessarily be celestial coordinates, but if they are, then they will be in radians.} To transform in the opposite direction, interchange the two pairs of arrays (so that the world coordinates are given as input) and change the fifth argument of astTran2 to zero. c- f+ Here, N is the number of points to be transformed, XPIXEL and YPIXEL hold the pixel coordinates, and XWORLD and YWORLD receive the returned world coordinates.\footnote{By pixel coordinates, we mean a coordinate system in which the first pixel in the image is centred on (1,1) and each pixel is a unit square. Note that the world coordinates will not necessarily be celestial coordinates, but if they are, then they will be in radians.} To transform in the opposite direction, interchange the two pairs of arrays (so that the world coordinates are given as input) and change the fifth argument of AST\_TRAN2 to .FALSE.. f- c+ To transform points in one dimension, use astTran1. In any other number of dimensions (or if the number of dimensions is initially unknown), use astTranN or astTranP. These functions are described in \appref{ss:functiondescriptions}. c- f+ To transform points in one dimension, use AST\_TRAN1. In any other number of dimensions (or if the number of dimensions is initially unknown), use AST\_TRANN. These routines are described in \appref{ss:functiondescriptions}. f- For more information about transforming coordinates, see \secref{ss:transforming} and \secref{ss:framesetasmapping}. For details of how to handle missing coordinates, see \secref{ss:badcoordinates}. \subsection{\label{ss:howtotestforcelestial}\ldots Test if a WCS is a Celestial Coordinate System} The world coordinate system (WCS) currently associated with an image may often be a celestial coordinate system, but this need not necessarily be the case. For instance, instead of right ascension and declination, an image might have a WCS with axes representing wavelength and slit position, or maybe just plain old pixels. c+ If you have obtained a WCS calibration for an image, as in \secref{ss:howtoreadwcs}, in the form of a pointer ``wcsinfo'' to a FrameSet, then you may determine if the current coordinate system is a celestial one or not, as follows: c- f+ If you have obtained a WCS calibration for an image, as in \secref{ss:howtoreadwcs}, in the form of a pointer WCSINFO to a FrameSet, then you may determine if the current coordinate system is a celestial one or not, as follows: f- c+ \begin{small} \begin{terminalv} AstFrame *frame; int issky; ... /* Obtain a pointer to the current Frame and determine if it is a SkyFrame. */ frame = astGetFrame( wcsinfo, AST__CURRENT ); issky = astIsASkyFrame( frame ); frame = astAnnul( frame ); \end{terminalv} \end{small} c- f+ \begin{small} \begin{terminalv} INTEGER FRAME LOGICAL ISSKY ... * Obtain a pointer to the current Frame and determine if it is a * SkyFrame. FRAME = AST_GETFRAME( WCSINFO, AST__CURRENT, STATUS ) ISSKY = AST_ISASKYFRAME( FRAME, STATUS ) CALL AST_ANNUL( FRAME, STATUS ) \end{terminalv} \end{small} f- c+ This will set ``issky'' to 1 if the WCS is a celestial coordinate system, and to zero otherwise. c- f+ This will set ISSKY to .TRUE.\ if the WCS is a celestial coordinate system, and to .FALSE.\ otherwise. f- \subsection{\label{ss:howtotestforspectral}\ldots Test if a WCS is a Spectral Coordinate System} Testing for a spectral coordinate system is basically the same as testing for a celestial coordinate system (see the previous section). The one difference is that you use the c+ astIsASpecFrame function c- f+ AST\_ISASPECFRAME routine f- in place of the c+ astIsASkyFrame function. c- f+ AST\_ISASKYFRAME routine. f- \subsection{\label{ss:howtoformatcoordinates}\ldots Format Coordinates for Display} c+ Once you have converted pixel coordinates into world coordinates (\secref{ss:howtotransform}), you may want to format them as text before displaying them. Typically, this would convert from (say) radians into something more comprehensible. Using the FrameSet pointer ``wcsinfo'' obtained in \secref{ss:howtoreadwcs} and a pair of world coordinates ``xw'' and ``yw'' (\emph{e.g.}\ see \secref{ss:howtotransform}), you could proceed as follows: c- f+ Once you have converted pixel coordinates into world coordinates (\secref{ss:howtotransform}), you may want to format them as text before displaying them. Typically, this would convert from (say) radians into something more comprehensible. Using the FrameSet pointer WCSINFO obtained in \secref{ss:howtoreadwcs} and a pair of world coordinates XW and YW (\emph{e.g.}\ see \secref{ss:howtotransform}), you could proceed as follows: f- c+ \begin{small} \begin{terminalv} #include const char *xtext, *ytext; double xw, yw; ... xtext = astFormat( wcsinfo, 1, xw ); ytext = astFormat( wcsinfo, 2, yw ); (void) printf( "Position = %s, %s\n", xtext, ytext ); \end{terminalv} \end{small} c- f+ \begin{small} \begin{terminalv} CHARACTER * ( 20 ) XTEXT, YTEXT DOUBLE PRECISION XW, YW ... XTEXT = AST_FORMAT( WCSINFO, 1, XW, STATUS ) YTEXT = AST_FORMAT( WCSINFO, 2, YW, STATUS ) WRITE ( *, 199 ) XTEXT, YTEXT 199 FORMAT( 'Position = ', A, ', ', A ) \end{terminalv} \end{small} f- c+ Here, the second argument to astFormat is the axis number. c- f+ Here, the second argument to AST\_FORMAT is the axis number. f- With celestial coordinates, this will usually result in sexagesimal notation, such as ``12:34:56.7''. However, the same method may be applied to any type of coordinates and appropriate formatting will be employed. For more information about formatting coordinate values and how to control the style of formatting used, see \secref{ss:formattingaxisvalues} and \secref{ss:formattingskyaxisvalues}. If necessary, also see \secref{ss:normalising} for details of how to ``normalise'' a set of coordinates so that they lie within the standard range (\emph{e.g.}\ 0 to 24 hours for right ascension and $\pm 90^\circ$ for declination). \subsection{\ldots Display Coordinates as they are Transformed} c+ In addition to formatting coordinates as part of a program's output, you may also want to examine coordinate values while debugging your program. To save time, you can ``eavesdrop'' on the coordinate values being processed every time they are transformed. For example, when using the FrameSet pointer ``wcsinfo'' obtained in \secref{ss:howtoreadwcs} to transform coordinates (\secref{ss:howtotransform}), you could inspect the coordinate values as follows: c- f+ In addition to formatting coordinates as part of a program's output, you may also want to examine coordinate values while debugging your program. To save time, you can ``eavesdrop'' on the coordinate values being processed every time they are transformed. For example, when using the FrameSet pointer WCSINFO obtained in \secref{ss:howtoreadwcs} to transform coordinates (\secref{ss:howtotransform}), you could inspect the coordinate values as follows: f- c+ \begin{small} \begin{terminalv} astSet( wcsinfo, "Report=1" ); astTran2( wcsinfo, N, xpixel, ypixel, 1, xworld, yworld ); \end{terminalv} \end{small} c- f+ \begin{small} \begin{terminalv} CALL AST_SET( WCSINFO, 'Report=1', STATUS ) CALL AST_TRAN2( WCSINFO, N, XPIXEL, YPIXEL, .TRUE., : XWORLD, YWORLD, STATUS ) \end{terminalv} \end{small} f- By setting the FrameSet's Report attribute to 1, coordinate transformations are automatically displayed on the program's standard output stream, appropriately formatted, for example: \begin{terminalv} (42.1087, 20.2717) --> (2:06:03.0, 34:22:39) (43.0197, 21.1705) --> (2:08:20.6, 35:31:24) (43.9295, 22.0716) --> (2:10:38.1, 36:40:09) (44.8382, 22.9753) --> (2:12:55.6, 37:48:55) (45.7459, 23.8814) --> (2:15:13.1, 38:57:40) (46.6528, 24.7901) --> (2:17:30.6, 40:06:25) (47.5589, 25.7013) --> (2:19:48.1, 41:15:11) (48.4644, 26.6149) --> (2:22:05.6, 42:23:56) (49.3695, 27.5311) --> (2:24:23.1, 43:32:41) (50.2742, 28.4499) --> (2:26:40.6, 44:41:27) \end{terminalv} For a complete description of the Report attribute, see its entry in \appref{ss:attributedescriptions}. For further details of how to set and enquire attribute values, see \secref{ss:settingattributes} and \secref{ss:gettingattributes}. \subsection{\ldots Read Coordinates Entered by a User} In addition to writing out coordinate values generated by your program (\secref{ss:howtoformatcoordinates}), you may also need to accept coordinates entered by a user, or perhaps read from a file. In this case, you will probably want to allow ``free-format'' input, so that the user has some flexibility in the format that can be used. You will probably also want to detect any typing errors. c+ Let's assume that you want to read a number of lines of text, each containing the world coordinates of a single point, and to split each line into individual numerical coordinate values. Using the FrameSet pointer ``wcsinfo'' obtained earlier (\secref{ss:howtoreadwcs}), you could proceed as follows: c- f+ Let's assume that you want to read a number of lines of text, each containing the world coordinates of a single point, and to split each line into individual numerical coordinate values. Using the FrameSet pointer WCSINFO obtained earlier (\secref{ss:howtoreadwcs}), you could proceed as follows: f- c+ \begin{small} \begin{terminalv} #include char *t; char text[ MAXCHARS + 2 ]; double coord[ 10 ]; int iaxis, n, naxes; ... /* Obtain the number of coordinate axes (if not already known). */ naxes = astGetI( wcsinfo, "Naxes" ); /* Loop to read each line of input text, in this case from the standard input stream (your programming environment will probably provide a better way of reading text than this). Set the pointer "t" to the start of each line read. */ while ( t = fgets( text, MAXCHARS + 2, stdin ) ) { /* Attempt to read a coordinate for each axis. */ for ( iaxis = 1; iaxis <= naxes; iaxis++ ) { n = astUnformat( wcsinfo, iaxis, t, &coord[ iaxis - 1 ] ); /* If nothing was read and this is not the first axis or the end-of-string, try stepping over a separator and reading again. */ if ( !n && ( iaxis > 1 ) && *t ) n = astUnformat( wcsinfo, iaxis, ++t, &coord[ iaxis - 1 ] ); /* Quit if nothing was read, otherwise move on to the next coordinate. */ if ( !n ) break; t += n; } /* Test for the possible errors that may occur... */ /* Error detected by AST (a message will have been issued). */ if ( !astOK ) { break; /* Error in input data at character t[n]. */ } else if ( *t || !n ) { break; } else { } } \end{terminalv} \end{small} c- f+ \begin{small} \begin{terminalv} CHARACTER TEXT * ( 80 ) DOUBLE PRECISION COORD( 10 ) INTEGER IAXIS, N, NAXES, T ... * Obtain the number of coordinate axes (if not already known). NAXES = AST_GETI( WCSINFO, 'Naxes', STATUS ) * Loop to read each line of input text, in this case from the * standard input channel (your programming environment will probably * provide a better way of reading text than this). Set the index T to * the start of each line read. 2 CONTINUE READ( *, '(A)', END=99 ) TEXT T = 1 * Attempt to read a coordinate for each axis. DO 3 IAXIS = 1, NAXES N = AST_UNFORMAT( WCSINFO, IAXIS, TEXT( T : ), COORD( IAXIS ), : STATUS ) * If nothing was read and this is not the first axis and the end of * the text has not been reached, try stepping over a separator and * reading again. IF ( ( N .EQ. 0 ) .AND. ( IAXIS .GT. 1 ) .AND. : ( T .LT. LEN( STRING ) ) ) THEN T = T + 1 N = AST_UNFORMAT( WCSINFO, IAXIS, TEXT( T : ), COORD( IAXIS ), STATUS ) END IF * Quit if nothing was read, otherwise move on to the next coordinate. IF ( N .EQ. 0 ) GO TO 4 T = T + N 3 CONTINUE 4 CONTINUE * Test for the possible errors that may occur... * Error detected by AST (a message will have been issued). IF ( STATUS .NE. 0 ) THEN GO TO 99 * Error in input data at character TEXT( T + N : T + N ). ELSE IF ( ( T .LT. LEN( STRING ) ) .OR. ( N .EQ. 0 ) ) THEN GO TO 99 ELSE END IF * Return to read the next input line. GO TO 2 99 CONTINUE \end{terminalv} \end{small} f- This algorithm has the advantage of accepting free-format input in whatever style is appropriate for the world coordinates in use (under the control of the FrameSet whose pointer you provide). For example, wavelength values might be read as floating point numbers (\emph{e.g.}\ ``1.047'' or ``4787''), whereas celestial positions could be given in sexagesimal format (\emph{e.g.}\ ``12:34:56'' or ``12~34.5'') and would be converted into radians. Individual coordinate values may be separated by white space and/or any non-ambiguous separator character, such as a comma. c+ For more information on reading coordinate values using the astUnformat function, see \secref{ss:unformattingaxisvalues}. For details of how sexagesimal formats are handled, and the forms of input that may be used for celestial coordinates, see \secref{ss:unformattingskyaxisvalues}. c- f+ For more information on reading coordinate values using the AST\_UNFORMAT function, see \secref{ss:unformattingaxisvalues}. For details of how sexagesimal formats are handled, and the forms of input that may be used for for celestial coordinates, see \secref{ss:unformattingskyaxisvalues}. f- \subsection{\label{ss:howtocreatenewwcs}\ldots Create a New WCS Calibration} This section describes how to add a WCS calibration to a data set which you are creating from scratch, rather than modifying an existing data set. In most common cases, the simplest way to create a new WCS calibration from scratch is probably to create a set of strings describing the required calibration in terms of the keywords used by the FITS WCS standard, and then convert these strings into an AST FrameSet describing the calibration. This FrameSet can then be used for many other purposes, or simply stored in the data set. The full FITS-WCS standard is quite involved, currently running to four separate papers, but the basic kernel is quite simple, involving the following keywords (all of which end with an integer axis index, indicated below by $$): \begin{description} \item[CRPIX]\mbox{}\\ hold the pixel coordinates at a reference point \item[CRVAL]\mbox{}\\ hold the corresponding WCS coordinates at the reference point \item[CTYPE]\mbox{}\\ name the quantity represented by the WCS axes, together with the projection algorithm used to convert the scaled and rotated pixel coordinates to WCS coordinates. \item[CD\_]\mbox{}\\ a set of keywords which specify the elements of a matrix. This matrix scales pixel offsets from the reference point into the offsets required as input by the projection algorithm specified by the CTYPE keywords. This matrix specifies the scale and rotation of the image. If there is no rotation the off-diagonal elements of the matrix (\emph{e.g.} CD1\_2 and CD2\_1) can be omitted. \end{description} As an example consider the common case of a simple 2D image of the sky in which north is parallel to the second pixel axis and east parallel to the (negative) first pixel axis. The image scale is 1.2 arc-seconds per pixel on both axes, and the image is presumed to have been obtained with a tangent plane projection. Furthermore, it is known that pixel coordinates (100.5,98.4) correspond to an RA of 11:00:10 and a Dec. of -23:26:02. A suitable set of FITS-WCS header cards could be: \begin{small} \begin{terminalv} CTYPE1 = 'RA---TAN' / Axis 1 represents RA with a tan projection CTYPE2 = 'DEC--TAN' / Axis 2 represents Dec with a tan projection CRPIX1 = 100.5 / Pixel coordinates of reference point CRPIX2 = 98.4 / Pixel coordinates of reference point CRVAL1 = 165.04167 / Degrees equivalent of "11:00:10" hours CRVAL2 = -23.433889 / Decimal equivalent of "-23:26:02" degrees CD1_1 = -0.0003333333 / Decimal degrees equivalent of -1.2 arc-seconds CD2_2 = 0.0003333333 / Decimal degrees equivalent of 1.2 arc-seconds \end{terminalv} \end{small} Notes: \begin{itemize} \item a FITS header card begins with the keyword name starting at column 1, has an equals sign in column 9, and the keyword value in columns 11 to 80. \item string values must be enclosed in single quotes. \item celestial longitude and latitude must both be specified in decimal degrees. \item the CD1\_1 value is negative to indicate that RA increases as the first pixel axis decreases. \item the (RA,Dec) coordinates will be taken as ICRS coordinates. For FK5 you should add: \begin{small} \begin{terminalv} RADESYS = 'FK5' EQUINOX = 2005.6 \end{terminalv} \end{small} The EQUINOX value defaults to J2000.0 if omitted. FK4 can also be used in place of FK5, in which case EQUINOX defaults to B1950.0. \end{itemize} Once you have created these FITS-WCS header card strings, you should store them in a FitsChan and then read the corresponding FrameSet from the FitsChan. How to do this is described in \secref{ss:howtoreadwcs}. Having created the WCS calibration, you may want to store it in a data file. How to do this is described in \secref{ss:howtowritewcs}).\footnote{If you are writing the WCS calibration to a FITS file you obviously have the choice of storing the FITS-WCS cards directly.} If the required WCS calibration cannot be described as a set of FITS-WCS headers, then a different approach is necessary. In this case, you should first create a Frame describing pixel coordinates, and store this Frame in a new FrameSet. You should then create a new Frame describing the world coordinate system. This Frame may be a specific subclass of Frame such as a SkyFrame for celestial coordinates, a SpecFrame for spectral coordinates, a Timeframe for time coordinates, or a CmpFrame for a combination of different coordinates. You also need to create a suitable Mapping which transforms pixel coordinates into world coordinates. AST provides many different types of Mappings, all of which can be combined together in arbitrary fashions to create more complicated Mappings. The WCS Frame should then be added into the FrameSet, using the Mapping to connect the WCS Frame with the pixel Frame. \subsection{\label{ss:howtomodifywcs}\ldots Modify a WCS Calibration} The usual reason for wishing to modify the WCS calibration associated with a dataset is that the data have been geometrically transformed in some way (here, we will assume a 2-dimensional image dataset). This causes the image features (stars, galaxies, \emph{etc.}) to move with respect to the grid of pixels which they occupy, so that any coordinate systems previously associated with the image become invalid. To correct for this, it is necessary to set up a Mapping which expresses the positions of image features in the new data grid in terms of their positions in the old grid. In both cases, the grid coordinates we use will have the first pixel centred at (1,1) with each pixel being a unit square. c+ AST allows you to correct for any type of geometrical transformation in this way, so long as a suitable Mapping to describe it can be constructed. For purposes of illustration, we will assume here that the new image coordinates ``xnew'' and ``ynew'' can be expressed in terms of the old coordinates ``xold'' and ``yold'' as follows: c- f+ AST allows you to correct for any type of geometrical transformation in this way, so long as a suitable Mapping to describe it can be constructed. For purposes of illustration, we will assume here that the new image coordinates XNEW and YNEW can be expressed in terms of the old coordinates XOLD and YOLD as follows: f- c+ \begin{small} \begin{terminalv} double xnew, xold, ynew, yold; double m[ 4 ], z[ 2 ]; ... xnew = xold * m[ 0 ] + yold * m[ 1 ] + z[ 0 ]; ynew = xold * m[ 2 ] + yold * m[ 3 ] + z[ 1 ]; \end{terminalv} \end{small} c- f+ \begin{small} \begin{terminalv} DOUBLE PRECISION XNEW, XOLD, YNEW, YOLD DOUBLE PRECISION M( 4 ), Z( 2 ) ... XNEW = XOLD * M( 1 ) + YOLD * M( 2 ) + Z( 1 ) YNEW = XOLD * M( 3 ) + YOLD * M( 4 ) + Z( 2 ) \end{terminalv} \end{small} f- c+ where ``m'' is a 2$\times$2 transformation matrix and ``z'' represents a shift of origin. This is therefore a general linear coordinate transformation which can represent displacement, rotation, magnification and shear. c- f+ where M is a 2$\times$2 transformation matrix and Z represents a shift of origin. This is therefore a general linear coordinate transformation which can represent displacement, rotation, magnification and shear. f- In AST, it can be represented by concatenating two Mappings. The first is a MatrixMap, which implements the matrix multiplication. The second is a WinMap, which linearly transforms one coordinate window on to another, but will be used here simply to implement the shift of origin (alternatively, a ShiftMap could have been used in place of a WinMap). These Mappings may be constructed and concatenated as follows: c+ \begin{small} \begin{terminalv} AstCmpMap *newmap; AstMatrixMap *matrixmap; AstWinMap *winmap; ... /* The MatrixMap may be constructed directly from the matrix "m". */ matrixmap = astMatrixMap( 2, 2, 0, m, "" ); /* For the WinMap, we set up the coordinates of the corners of a unit square (window) and then the same square shifted by the required amount. */ { double ina[] = { 0.0, 0.0 }; double inb[] = { 1.0, 1.0 }; double outa[] = { z[ 0 ], z[ 1 ] }; double outb[] = { 1.0 + z[ 0 ], 1.0 + z[ 1 ] }; /* The WinMap will then implement this shift. */ winmap = astWinMap( 2, ina, inb, outa, outb, "" ); } /* Join the two Mappings together, so that they are applied one after the other. */ newmap = astCmpMap( matrixmap, winmap, 1, "" ); \end{terminalv} \end{small} c- f+ \begin{small} \begin{terminalv} DOUBLE PRECISION INA( 2 ), INB( 2 ), OUTA( 2 ), OUTB( 2 ) INTEGER MATRIXMAP, WINMAP ... * Set up the corners of a unit square. DATA INA / 2 * 0.0D0 / DATA INB / 2 * 1.0D0 / * The MatrixMap may be constructed directly from the matrix M. MATRIXMAP = AST_MATRIXMAP( 2, 2, 0, M, ' ', STATUS ) * For the WinMap, we take the coordinates of the corners of a unit * square (window) and then shift them by the required amounts. OUTA( 1 ) = INA( 1 ) + Z( 1 ) OUTA( 2 ) = INA( 2 ) + Z( 2 ) OUTB( 1 ) = INB( 1 ) + Z( 1 ) OUTB( 2 ) = INB( 2 ) + Z( 2 ) * The WinMap will then implement this shift. WINMAP = AST_WINMAP( 2, INA, INB, OUTA, OUTB, ' ', STATUS ) * Join the two Mappings together, so that they are applied one after * the other. NEWMAP = AST_CMPMAP( MATRIXMAP, WINMAP, 1, ' ', STATUS ) \end{terminalv} \end{small} f- You might, of course, create any other form of Mapping depending on the type of geometrical transformation involved. For an overview of the Mappings provided by AST, see \secref{ss:mappingselection}, and for a description of the capabilities of each class of Mapping, see its entry in \appref{ss:classdescriptions}. For an overview of how individual Mappings may be combined, see \secref{ss:cmpmapoverview} (\secref{ss:cmpmaps} gives more details). c+ Assuming you have obtained a WCS calibration for your original image in the form of a pointer to a FrameSet, ``wcsinfo1'' (\secref{ss:howtoreadwcs}), the Mapping created above may be used to produce a calibration for the new image as follows: c- f+ Assuming you have obtained a WCS calibration for your original image in the form of a pointer to a FrameSet, WCSINFO1 (\secref{ss:howtoreadwcs}), the Mapping created above may be used to produce a calibration for the new image as follows: f- c+ \begin{small} \begin{terminalv} AstFrameSet *wcsinfo1, *wcsinfo2; ... /* If necessary, make a copy of the WCS calibration, since we are about to alter it. */ wcsinfo2 = astCopy( wcsinfo1 ); /* Re-map the base Frame so that it refers to the new data grid instead of the old one. */ astRemapFrame( wcsinfo2, AST__BASE, newmap ); \end{terminalv} \end{small} c- f+ \begin{small} \begin{terminalv} INTEGER WCSINFO1, WCSINFO2 ... * If necessary, make a copy of the WCS calibration, since we are * about to alter it. WCSINFO2 = AST_COPY( WCSINFO1, STATUS ) * Re-map the base Frame so that it refers to the new data grid * instead of the old one. CALL AST_REMAPFRAME( WCSINFO2, AST__BASE, NEWMAP, STATUS ) \end{terminalv} \end{small} f- c+ This will produce a pointer, ``wcsinfo2'', to a new FrameSet in which all the coordinate systems associated with your original image are modified so that they are correctly registered with the new image instead. c- f+ This will produce a pointer, WCSINFO2, to a new FrameSet in which all the coordinate systems associated with the original image are modified so that they are correctly registered with your new image instead. f- For more information about re-mapping the Frames within a FrameSet, see \secref{ss:remapframe}. Also see \secref{ss:wcsprocessingexample} for a similar example to the above, applicable to the case of reducing the size of an image by binning. \subsection{\label{ss:howtowritewcs}\ldots Write a Modified WCS Calibration to a Dataset} If you have modified the WCS calibration associated with a dataset, such as in the example above (\secref{ss:howtomodifywcs}), then you will need to write the modified version out along with any new data. In the same way as when reading a WCS calibration (\secref{ss:howtoreadwcs}), how you do this will depend on your data system, but we will assume that you wish to generate a set of FITS header cards that can be stored with the data. You should usually make preparations for doing this when you first read the WCS calibration from your input dataset by modifying the example given in \secref{ss:howtoreadwcs} as follows: c+ \begin{small} \begin{terminalv} AstFitsChan *fitschan1; AstFrameSet *wcsinfo1; const char *encode; ... /* Create an input FitsChan and fill it with FITS header cards. Note, if you have all the header cards in a single string, use astPutCards in place of astPutFits. */ fitschan1 = astFitsChan( NULL, NULL, "" ); for ( icard = 0; icard < ncard; icard++ ) astPutFits( fitschan1, cards[ icard ], 0 ); /* Note which encoding has been used for the WCS information. */ encode = astGetC( fitschan1, "Encoding" ); /* Rewind the input FitsChan and read the WCS information from it. */ astClear( fitschan1, "Card" ); wcsinfo1 = astRead( fitschan1 ); \end{terminalv} \end{small} c- f+ \begin{small} \begin{terminalv} INTEGER FITSCHAN1, WCSINFO1 CHARACTER * ( 20 ) ENCODE ... * Create an input FitsChan and fill it with FITS header cards. Note, * if you have all the header cards in a single string, use AST_PUTCARDS in * place of AST_PUTFITS. FITSCHAN1 = AST_FITSCHAN( AST_NULL, AST_NULL, ' ', STATUS ) DO 1 ICARD = 1, NCARD CALL AST_PUTFITS( FITSCHAN1, CARDS( ICARD ), .FALSE., STATUS ) 1 CONTINUE * Note which encoding has been used for the WCS information. ENCODE = AST_GETC( FITSCHAN1, 'Encoding', STATUS ); * Rewind the input FitsChan and read the WCS information from it. CALL AST_CLEAR( FITSCHAN1, 'Card', STATUS ) WCSINFO1 = AST_READ( FITSCHAN1, STATUS ) \end{terminalv} \end{small} f- c+ Note how we have added an enquiry to determine how the WCS information is encoded in the input FITS cards, storing a pointer to the resulting string in the ``encode'' variable. This must be done \textbf{before} actually reading the WCS calibration. c- f+ Note how we have added an enquiry to determine how the WCS information is encoded in the input FITS cards, storing the resulting string in the ENCODE variable. This must be done \textbf{before} actually reading the WCS calibration. f- c+ \emph{(\textbf{N.B.}\ If you will be making extensive use of astGetC in your program, then you should allocate a buffer and make a copy of this string, because the pointer returned by astGetC will only remain valid for 50 invocations of the function, and you will need to use the Encoding value again later on.)} c- c+ Once you have produced a modified WCS calibration for the output dataset (\emph{e.g.}\ \secref{ss:howtomodifywcs}), in the form of a FrameSet identified by the pointer ``wcsinfo2'', you can produce a new FitsChan containing the output FITS header cards as follows: c- f+ Once you have produced a modified WCS calibration for the output dataset (\emph{e.g.}\ \secref{ss:howtomodifywcs}), in the form of a FrameSet identified by the pointer WCSINFO2, you can produce a new FitsChan containing the output FITS header cards as follows: f- c+ \small \begin{terminalv} AstFitsChan *fitschan2; AstFrameSet *wcsinfo2; ... /* Make a copy of the input FitsChan, AFTER the WCS information has been read from it. This will propagate all the input FITS header cards, apart from those describing the input WCS calibration. */ fitschan2 = astCopy( fitschan1 ); /* If necessary, make modifications to the cards in "fitschan2" (e.g. you might need to change NAXIS1, NAXIS2, etc., to account for a change in image size). You probably only need to do this if your data system does not provide these facilities itself. */
/* Alternatively, if your data system handles the propagation of FITS header cards to the output dataset for you, then simply create an empty FitsChan to contain the output WCS information alone. fitschan2 = astFitsChan( NULL, NULL, "" ); */ /* Rewind the new FitsChan (if necessary) and attempt to write the output WCS information to it using the same encoding method as the input dataset. */ astSet( fitschan2, "Card=1, Encoding=%s", encode ); if ( !astWrite( fitschan2, wcsinfo2 ) ) { /* If this didn't work (the WCS FrameSet has become too complex), then use the native AST encoding instead. */ astSet( fitschan2, "Encoding=NATIVE" ); (void) astWrite( fitschan2, wcsinfo2 ); } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER FITSCHAN2, JUNK, WCSINFO2 ... * Make a copy of the input FitsChan, AFTER the WCS information has * been read from it. This will propagate all the input FITS header * cards, apart from those describing the WCS calibration. FITSCHAN2 = AST_COPY( FITSCHAN1, STATUS ) * If necessary, make modifications to the cards in FITSCHAN2 * (e.g. you might need to change NAXIS1, NAXIS2, etc., to account for * a change in image size). You probably only need to do this if your * data system does not provide these facilities itself.
* Alternatively, if your data system handles the propagation of FITS * header cards to the output dataset for you, then simply create an * empty FitsChan to contain the output WCS information alone. * FITSCHAN2 = AST_FITSCHAN( AST_NULL, AST_NULL, ' ', STATUS ) * Rewind the new FitsChan (if necessary) and attempt to write the * output WCS information to it using the same encoding method as the * input dataset. CALL AST_SET( FITSCHAN2, 'Card=1, Encoding=' // ENCODE, STATUS ) IF ( AST_WRITE( FITSCHAN2, WCSINFO2, STATUS ) .EQ. 0 ) THEN * If this didn't work (the WCS FrameSet has become too complex), then * use the native AST encoding instead. CALL AST_SETC( FITSCHAN2, 'Encoding', 'NATIVE', STATUS ); JUNK = AST_WRITE( FITSCHAN2, WCSINFO2, STATUS ); END IF \end{terminalv} \normalsize f- For details of how to modify the contents of the output FitsChan in other ways, such as by adding, over-writing or deleting header cards, see \secref{ss:addressingfitscards}, \secref{ss:addingmulticards}, \secref{ss:addingfitscards} and \secref{ss:findingandchangingfits}. Once you have assembled the output FITS cards, you may retrieve them from the FitsChan that contains them as follows: c+ \small \begin{terminalv} #include char card[ 81 ]; ... astClear( fitschan2, "Card" ); while ( astFindFits( fitschan2, "%f", card, 1 ) ) (void) printf( "%s\n", card ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CHARACTER * ( 80 ) CARD ... CALL AST_CLEAR( FITSCHAN2, 'Card', STATUS ) 5 CONTINUE IF ( AST_FINDFITS( FITSCHAN2, '%f', CARD, .TRUE., STATUS ) ) THEN WRITE ( *, '(A)' ) CARD GO TO 5 END IF \end{terminalv} \normalsize f- c+ Here, we have simply written each card to the standard output stream, but you would obviously replace this with a function invocation to store the cards in your output dataset. c- f+ Here, we have simply written each card to the standard output unit, but you would obviously replace this with a subroutine call to store the cards in your output dataset. f- c+ For data systems that do not use FITS header cards, a different approach may be needed, possibly involving use of a Channel or XmlChan (\secref{ss:channels}) rather than a FitsChan. In the case of the Starlink NDF data format, for example, all of the above may be replaced by a single call to the function \xref{ndfPtwcs}{sun33}{ndfPtwcs}---see \xref{SUN/33}{sun33}{}. The whole process can probably be encapsulated in a similar way for most data systems, whether they use FITS header cards or not. c- f+ For data systems that do not use FITS header cards, a different approach may be needed, possibly involving use of a Channel or XmlChan (\secref{ss:channels}) rather than a FitsChan. In the case of the Starlink NDF data format, for example, all of the above may be replaced by a single call to the routine \xref{NDF\_PTWCS}{sun33}{NDF_PTWCS}---see \xref{SUN/33}{sun33}{}. The whole process can probably be encapsulated in a similar way for most other data systems, whether they use FITS header cards or not. f- For an overview of how to propagate WCS information through data processing steps, see \secref{ss:propagatingwcsinformation}. For more information about writing WCS information to FitsChans, see \secref{ss:writingnativefits} and \secref{ss:writingforeignfits}. For information about the options for encoding WCS information in FITS header cards, see \secref{ss:nativeencoding}, \secref{ss:foreignencodings}, and the description of the Encoding attribute in \appref{ss:attributedescriptions}. For a complete understanding of FitsChans and their use with FITS header cards, you should read \secref{ss:nativefits} and \secref{ss:foreignfits}. \subsection{\label{ss:howtoplotgrid}\ldots Display a Graphical Coordinate Grid} A common requirement when displaying image data is to plot an associated coordinate grid (\emph{e.g.}\ Figure~\ref{fig:overgrid}) over the displayed image. \begin{figure} \begin{center} c+ \includegraphics[width=0.7\textwidth]{sun211_figures/overgrid_bw} c- f+ \includegraphics[width=0.7\textwidth]{sun210_figures/overgrid_bw} f- \caption[An example of a displayed image with a coordinate grid plotted over it.]{An example of a displayed image with a coordinate grid plotted over it.} \label{fig:overgrid} \end{center} \end{figure} c+ The use of AST in such circumstances is independent of the underlying graphics system, so starting up the graphics system, setting up a coordinate system, displaying the image, and closing down afterwards can all be done using the graphics functions you would normally use. c- f+ The use of AST in such circumstances is independent of the underlying graphics system, so starting up the graphics system, setting up a coordinate system, displaying the image, and closing down afterwards can all be done using the graphics routines you would normally use. f- c+ However, displaying an image at a precise location can be a little fiddly with some graphics systems, and obviously the grid drawn by AST will not be accurately registered with the image unless this is done correctly. In the following template, we therefore illustrate both steps, basing the image display on the C interface to the PGPLOT graphics package.\footnote{An interface is provided with AST that allows it to use PGPLOT (\xref{SUN/15}{sun15}{}) for its graphics, although interfaces to other graphics systems may also be written.} Plotting a coordinate grid with AST then becomes a relatively minor part of what is almost a complete graphics program. c- f+ However, displaying an image at a precise location can be a little fiddly with some graphics systems, and obviously the grid drawn by AST will not be accurately registered with the image unless this is done correctly. In the following template, we therefore illustrate both steps, basing the image display on the PGPLOT graphics package.\footnote{An interface is provided with AST that allows it to use PGPLOT (\xref{SUN/15}{sun15}{}) for its graphics, although interfaces to other graphics systems may also be written.} Plotting a coordinate grid with AST then becomes a relatively minor part of what is almost a complete graphics program. f- c+ Once again, we assume that a pointer, ``wcsinfo'', to a suitable FrameSet associated with the image has already been obtained (\secref{ss:howtoreadwcs}). c- f+ Once again, we assume that a pointer, WCSINFO, to a suitable FrameSet associated with the image has already been obtained (\secref{ss:howtoreadwcs}). f- c+ \small \begin{terminalv} #include "cpgplot.h" AstPlot *plot; const float *data; float hi, lo, scale, x1, x2, xleft, xright, xscale; float y1, y2, ybottom, yscale, ytop; int nx, ny; ... /* Access the image data, which we assume has dimension sizes "nx" and "ny", and will be accessed via the "data" pointer. Also derive limits for scaling it, which we assign to the variables "hi" and "lo". */ /* Open PGPLOT using the device given by environment variable PGPLOT_DEV and check for success. */ if( cpgbeg( 0, " ", 1, 1 ) == 1 ) { /* Clear the screen and ensure equal scales on both axes. */ cpgpage(); cpgwnad( 0.0f, 1.0f, 0.0f, 1.0f ); /* Obtain the extent of the plotting area (not strictly necessary for PGPLOT, but possibly for other graphics systems). From this, derive the display scale in graphics units per pixel so that the image will fit within the display area. */ cpgqwin( &x1, &x2, &y1, &y2 ); xscale = ( x2 - x1 ) / nx; yscale = ( y2 - y1 ) / ny; scale = ( xscale < yscale ) ? xscale : yscale; /* Calculate the extent of the area in graphics units that the image will occupy, so as to centre it within the display area. */ xleft = 0.5f * ( x1 + x2 - nx * scale ); xright = 0.5f * ( x1 + x2 + nx * scale ); ybottom = 0.5f * ( y1 + y2 - ny * scale ); ytop = 0.5f * ( y1 + y2 + ny * scale ); /* Set up a PGPLOT coordinate transformation matrix and display the image data as a grey scale map (these details are specific to PGPLOT). */ { float tr[] = { xleft - 0.5f * scale, scale, 0.0f, ybottom - 0.5f * scale, 0.0f, scale }; cpggray( data, nx, ny, 1, nx, 1, ny, hi, lo, tr ); } /* BEGINNING OF AST BIT */ /* ==================== */ /* Store the locations of the bottom left and top right corners of the region used to display the image, in graphics coordinates. */ { float gbox[] = { xleft, ybottom, xright, ytop }; /* Similarly, store the locations of the image's bottom left and top right corners, in pixel coordinates -- with the first pixel centred at (1,1). */ double pbox[] = { 0.5, 0.5, nx + 0.5, ny + 0.5 }; /* Create a Plot, based on the FrameSet associated with the image. This attaches the Plot to the graphics surface so that it matches the displayed image. Specify that a complete set of grid lines should be drawn (rather than just coordinate axes). */ plot = astPlot( wcsinfo, gbox, pbox, "Grid=1" ); } /* Optionally, we can now set other Plot attributes to control the appearance of the grid. The values assigned here use the colour/font indices defined by the underlying graphics system. */ astSet( plot, "Colour(grid)=2, Font(textlab)=3" ); /* Use the Plot to draw the coordinate grid. */ astGrid( plot ); /* Annul the Plot when finished (or use the astBegin/astEnd technique shown earlier). */ plot = astAnnul( plot ); /* END OF AST BIT */ /* ============== */ /* Close down the graphics system. */ cpgend(); } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} DOUBLE PRECISION BBOX( 4 ) INTEGER NX, NY, PGBEG, PLOT REAL DATA( NX, NY ), GBOX( 4 ), HI, LO, SCALE, TR( 6 ) REAL X1, X2, XLEFT, XRIGHT, Y1, Y2, YBOTTOM, YTOP ... * Access the image data, which we assume will be stored in the real * 2-dimensional array DATA with dimension sizes NX and NY. Also * derive limits for scaling it, which we assign to the variables HI * and LO. * Open PGPLOT using the device given by environment variable * PGPLOT_DEV and check for success. IF ( PGBEG( 0, ' ', 1, 1 ) .EQ. 1 ) THEN * Clear the screen and ensure equal scales on both axes. CALL PGPAGE CALL PGWNAD( 0.0, 1.0, 0.0, 1.0 ) * Obtain the extent of the plotting area (not strictly necessary for * PGPLOT, but possibly for other graphics systems). From this, derive * the display scale in graphics units per pixel so that the image * will fit within the display area. CALL PGQWIN( X1, X2, Y1, Y2 ) SCALE = MIN( ( X2 - X1 ) / NX, ( Y2 - Y1 ) / NY ) * Calculate the extent of the area in graphics units that the image * will occupy, so as to centre it within the display area. XLEFT = 0.5 * ( X1 + X2 - NX * SCALE ) XRIGHT = 0.5 * ( X1 + X2 + NX * SCALE ) YBOTTOM = 0.5 * ( Y1 + Y2 - NY * SCALE ) YTOP = 0.5 * ( Y1 + Y2 + NY * SCALE ) * Set up a PGPLOT coordinate transformation matrix and display the * image data as a grey scale map (these details are specific to * PGPLOT). TR( 1 ) = XLEFT - 0.5 * SCALE TR( 2 ) = SCALE TR( 3 ) = 0.0 TR( 4 ) = YBOTTOM - 0.5 * SCALE TR( 5 ) = 0.0 TR( 6 ) = SCALE CALL PGGRAY( DATA, NX, NY, 1, NX, 1, NY, HI, LO, TR ) * BEGINNING OF AST BIT * ==================== * Store the locations of the bottom left and top right corners of the * region used to display the image, in graphics coordinates. GBOX( 1 ) = XLEFT GBOX( 2 ) = YBOTTOM GBOX( 3 ) = XRIGHT GBOX( 4 ) = YTOP * Similarly, store the locations of the image's bottom left and top * right corners, in pixel coordinates -- with the first pixel centred * at (1,1). BBOX( 1 ) = 0.5D0 BBOX( 2 ) = 0.5D0 BBOX( 3 ) = NX + 0.5D0 BBOX( 4 ) = NY + 0.5D0 * Create a Plot, based on the FrameSet associated with the * image. This attaches the Plot to the graphics surface so that it * matches the displayed image. Specify that a complete set of grid * lines should be drawn (rather than just coordinate axes). PLOT = AST_PLOT( WCSINFO, GBOX, BBOX, 'Grid=1', STATUS ) * Optionally, we can now set other Plot attributes to control the * appearance of the grid. The values assigned here use the * colour/font indices defined by the underlying graphics system. CALL AST_SET( PLOT, 'Colour(grid)=2, Font(textlab)=3', STATUS ) * Use the Plot to draw the coordinate grid. CALL AST_GRID( PLOT, STATUS ) * Annul the Plot when finished (or use the AST_BEGIN/AST_END * technique shown earlier). CALL AST_ANNUL( PLOT, STATUS ) * END OF AST BIT * ============== * Close down the graphics system. CALL PGEND END IF \end{terminalv} \normalsize f- Note that once you have set up a Plot which is aligned with a displayed image, you may also use it to generate further graphical output of your own, specified in the image's world coordinate system (such as markers to represent astronomical objects, annotation, \emph{etc.}). There is also a range of Plot attributes which gives control over most aspects of the output's appearance. For details of the facilities available, see \secref{ss:plots} and the description of the Plot class in \appref{ss:classdescriptions}. For details of how to build a graphics program which uses PGPLOT, see \secref{ss:howtobuild} and the description of the ast\_link command in \appref{ss:commanddescriptions}. \subsection{\label{ss:howtoswitchgrid}\ldots Switch to Plot a Different Celestial Coordinate Grid} c+ Once you have set up a Plot to draw a coordinate grid (\secref{ss:howtoplotgrid}), it is a simple matter to change things so that the grid represents a different celestial coordinate system. For example, after creating the Plot with astPlot, you could use: c- f+ Once you have set up a Plot to draw a coordinate grid (\secref{ss:howtoplotgrid}), it is a simple matter to change things so that the grid represents a different celestial coordinate system. For example, after creating the Plot with AST\_PLOT, you could use: f- c+ \small \begin{terminalv} astSet( plot, "System=Galactic" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SET( PLOT, 'System=Galactic', STATUS ) \end{terminalv} \normalsize f- or: c+ \small \begin{terminalv} astSet( plot, "System=FK5, Equinox=J2010" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SET( PLOT, 'System=FK5, Equinox=J2010', STATUS ) \end{terminalv} \normalsize f- and any axes and/or grid drawn subsequently would represent the new celestial coordinate system you specified. Note, however, that this will only work if the original grid represented celestial coordinates of some kind (see \secref{ss:howtotestforcelestial} for how to determine if this is the case\footnote{Note that the methods applied to a FrameSet may be used equally well with a Plot.}). If it did not, you will get an error message. For more information about the celestial coordinate systems available, see the descriptions of the System, Equinox and Epoch attributes in \appref{ss:attributedescriptions}. \subsection{\ldots Give a User Control Over the Appearance of a Plot} The idea of using a Plot's attributes to control the appearance of the graphical output it produces (\secref{ss:howtoplotgrid} and \secref{ss:howtoswitchgrid}) can easily be extended to allow the user of a program complete control over such matters. For instance, if the file ``plot.config'' contains a series of plotting options in the form of Plot attribute assignments (see below for an example), then we could create a Plot and implement these assignments before producing the graphical output as follows: c+ \small \begin{terminalv} #include #define MAXCHARS 120 FILE *stream; char line[ MAXCHARS + 2 ]; int base; ... /* Create a Plot and define the default appearance of the graphical output it will produce. */ plot = astPlot( wcsinfo, gbox, pbox, "Grid=1, Colour(grid)=2, Font(textlab)=3" ); /* Obtain the value of any Plot attributes we want to preserve. */ base = astGetI( plot, "Base" ); /* Open the plot configuration file, if it exists. Read each line of text and use it to set new Plot attribute values. Close the file when done. */ if ( stream = fopen( "plot.config", "r" ) ) { while ( fgets( line, MAXCHARS + 2, stream ) ) astSet( plot, "%s", line ); close( stream ); } /* Restore any attribute values we are preserving. */ astSetI( plot, "Base", base ); /* Produce the graphical output (e.g.). */ astGrid( plot ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CHARACTER LINE( 120 ) INTEGER BASE ... * Create a Plot and define the default appearance of the graphical * output it will produce. PLOT = AST_PLOT( WCSINFO, GBOX, PBOX, : 'Grid=1, Colour(grid)=2, Font(textlab)=3', : STATUS ) * Obtain the value of any Plot attributes we want to preserve. BASE = AST_GETI( PLOT, 'Base', STATUS ) * Open the plot configuration file, if it exists. OPEN ( 1, FILE = 'plot.config', STATUS = 'OLD', ERR = 8 ) * Read each line of text and use it to set new Plot attribute * values. Close the file when done. 6 CONTINUE READ ( 1, '(A)', END = 7 ) LINE CALL AST_SET( PLOT, LINE, STATUS ) GO TO 6 7 CLOSE ( 1 ) 8 CONTINUE * Restore any attribute values we are preserving. CALL AST_SETI( PLOT, 'Base', BASE, STATUS ) * Produce the graphical output (e.g.). CALL AST_GRID( PLOT, STATUS ) \end{terminalv} \normalsize f- Notice that we take care that the Plot's Base attribute is preserved so that the user cannot change it. This is because graphical output will not be produced successfully if the base Frame does not describe the plotting surface to which we attached the Plot when we created it. The arrangement shown above allows the contents of the ``plot.config'' file to control most aspects of the graphical output produced (including the coordinate system used; the colour, line style, thickness and font used for each component; the positioning of axes and tick marks; the precision, format and positioning of labels; \emph{etc.}) \emph{via} assignments of the form: \small \begin{terminalv} System=Galactic, Equinox = 2001 Border = 1, Colour( border ) = 1 Colour( grid ) = 2 DrawAxes = 1 Colour( axes ) = 3 Digits = 8 Labelling = Interior \end{terminalv} \normalsize For a more sophisticated interface, you could obviously perform pre-processing on this input---for example, to translate words like ``red'', ``green'' and ``blue'' into colour indices, to permit comments and blank lines, \emph{etc.} For a full list of the attributes that may be used to control the appearance of graphical output, see the description of the Plot class in \appref{ss:classdescriptions}. For a complete description of each individual attribute (\emph{e.g.}\ those above), see the attribute's entry in \appref{ss:attributedescriptions}. \cleardoublepage \section{\label{ss:primer}An AST Object Primer} c+ The AST library deals throughout with entities called Objects and a basic understanding of how to handle these is needed before you can use the library effectively. If you are already familiar with an object-oriented language, such as C$++$, few of the concepts should seem new to you. Be aware, however, that AST is designed to be used \emph{via} fairly conventional C and Fortran interfaces, so some things have to be done a little differently. c- f+ The AST library deals throughout with entities called Objects and a basic understanding of how to handle these is needed before you can use the library effectively. If you are already familiar with an object-oriented language, such as C$++$, few of the concepts should seem new to you. Be aware, however, that AST is designed to be used \emph{via} fairly conventional Fortran and C interfaces, so some things have to be done a little differently. f- c+ If you are not already familiar with object-oriented programming, then don't worry---we will not emphasise this aspect more than is necessary and will not assume any background knowledge. Instead, this section concentrates on presenting all the fundamental information you will need, explaining how AST Objects behave and how to manipulate them from conventional C programs. c- f+ If you are not already familiar with object-oriented programming, then don't worry---we will not emphasise this aspect more than is necessary and will not assume any background knowledge. Instead, this section concentrates on presenting all the fundamental information you will need, explaining how AST Objects behave and how to manipulate them from conventional Fortran programs. f- If you like to read documents from cover to cover, then you can consider this section as an introduction to the programming techniques used in the rest of the document. Otherwise, you may prefer to skim through it on a first reading and return to it later as reference material. \subsection{AST Objects} An AST Object is an entity which is used to store information and Objects come in various kinds, called \emph{classes}, according to the sort of information they hold. Throughout this section, we will make use of a simple Object belonging to the ``ZoomMap'' class to illustrate many of the basic concepts. A ZoomMap is an Object that contains a recipe for converting coordinates between two hypothetical coordinate systems. It does this by multiplying all the coordinate values by a constant called the \emph{Zoom factor}. A ZoomMap is a very simple Object which exists mainly for use in examples. It allows us to illustrate the ways in which Objects are manipulated and to introduce the concept of a Mapping---a recipe for converting coordinates---which is fundamental to the way the AST library works. \subsection{\label{ss:objectcreation}Object Creation and Pointers} Let us first consider how to create a ZoomMap. This is done very simply as follows: c+ \small \begin{terminalv} #include "ast.h" AstZoomMap *zoommap; ... zoommap = astZoomMap( 2, 5.0, "" ) \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INCLUDE 'AST_PAR' INTEGER STATUS, ZOOMMAP STATUS = 0 ... ZOOMMAP = AST_ZOOMMAP( 2, 5.0D0, ' ', STATUS ) \end{terminalv} \normalsize f- c+ The first step is to include the header file ``ast.h'' which declares the interface to the AST library. We then declare a pointer of type AstZoomMap$*$ to receive the result and invoke the function astZoomMap to create the ZoomMap. The pattern is the same for all other classes of AST Object---you simply prefix ``ast'' to the class name to obtain the function that creates the Object and prefix ``Ast'' to obtain the type of the returned pointer. c- f+ The first step is to include the file AST\_PAR which defines the interface to the AST library and, amongst other things, declares AST\_ZOOMMAP to be an integer function. We then declare an integer variable ZOOMMAP to receive the result and an integer STATUS variable to hold the error status, which we initialise to zero. Next, we invoke AST\_ZOOMMAP to create the ZoomMap. The pattern is the same for all other classes of AST Object---you simply prefix ``AST\_'' to the class name to obtain the function that creates the Object. f- These functions are called \emph{constructor functions}, or simply \emph{constructors} (you can find an individual description of all AST functions in \appref{ss:functiondescriptions}) and the arguments passed to the constructor are used to initialise the new Object. In this case, we specify 2 as the number of coordinates (\emph{i.e.}\ we are going to work in a 2-dimensional c+ space) and 5.0 as the Zoom factor to be applied. Note that this is a C double value. We will return to the final argument, an empty string, shortly (\secref{ss:attributeinitialisation}). c- f+ space) and 5.0D0 as the Zoom factor to be applied. Note that this is a Fortran double precision value. We will return to the final two arguments, a blank string and the error status, shortly (\secref{ss:attributeinitialisation} and \secref{ss:errordetection}). f- c+ The value returned by the constructor is termed an \emph{Object pointer} or, in this case, a \emph{ZoomMap pointer} and is used to refer to the Object. You perform all subsequent operations on the Object by passing this pointer to other AST functions. c- f+ The integer value returned by the constructor is termed an \emph{Object pointer} or, in this case, a \emph{ZoomMap pointer}. This pointer is not an Object itself, but is a value used to refer to the Object. You should be careful not to modify any Object pointer yourself, as this may render it invalid. Instead, you perform all subsequent operations on the Object by passing this pointer to other AST routines. f- \subsection{\label{ss:objecthierarchy}The Object Hierarchy} Now that we have created our first ZoomMap, let us examine how it relates to other kinds of Object before investigating what we can do with it. We have so far indicated that a ZoomMap is a kind of Object and have also mentioned that it is a kind of Mapping as well. These statements can be represented very simply using the following hierarchy: \small \begin{terminalv} Object Mapping ZoomMap \end{terminalv} \normalsize which is a way of stating that a ZoomMap is a special class of Mapping, while a Mapping, in turn, is a special class of Object. This is exactly like saying that an Oak is a special form of Tree, while a Tree, in turn, is a special form of Plant. This may seem almost trivial, but before you turn to read something less dull, be assured that it is a very important idea to keep in mind in what follows. If we look at some of the other Objects used by the AST library, we can see how these are all related in a similar way (don't worry about what they do at this stage): \label{ss:mappinghierarchy} \small \begin{terminalv} Object Mapping Frame FrameSet Plot UnitMap ZoomMap Channel FitsChan XmlChan \end{terminalv} \normalsize Notice that there are several different types of Mapping available (\emph{i.e.}\ there are classes of Object indented beneath the ``Mapping'' heading) and, in addition, other types of Object which are not Mappings---Channels for instance (which are at the same hierarchical level as Mappings). The most specialised Object we have shown here is the Plot (which we will not discuss in detail until \secref{ss:plots}). As you can see, a Plot is a FrameSet\ldots\ and a Frame\ldots\ and a Mapping\ldots\ and, like everything else, ultimately an Object. What this means is that you can use a Plot not only for its own specialised behaviour, but also whenever any of these other less-specialised classes of Object is called for. The general rule is that an Object of a particular class may substitute for any of the classes appearing above it in this hierarchy. The Object is then said to \emph{inherit} the behaviour of these higher classes. We can therefore use our ZoomMap whenever a ZoomMap, a Mapping or an Object is called for. Sometimes, this can lead to some spectacular short-cuts by avoiding the need to break large Objects down in order to access their components. With some practice and a little lateral thinking you should soon be able to spot opportunities for this. You can find the full \emph{class hierarchy}, as this is called, for the AST library in \appref{ss:classhierarchy} and you may need to refer to it occasionally until you are familiar with the classes you need to use. \subsection{\label{ss:displayingobjects}Displaying Objects} Let us now return to the ZoomMap that we created earlier (\secref{ss:objectcreation}) and examine what it's made of. c+ There is a function for doing this, called astShow, which is provided mainly for looking at Objects while you are debugging programs. c- f+ There is a routine for doing this, called AST\_SHOW, which is provided mainly for looking at Objects while you are debugging programs. f- c+ If you consult the description of astShow in \appref{ss:functiondescriptions}, you will find that it takes a pointer to an Object (of type AstObject$*$) as its argument. Although we have only a ZoomMap pointer available, this is not a problem. If you refer to the brief class hierarchy described above (\secref{ss:mappinghierarchy}), you will see that a ZoomMap is an Object, albeit a specialised one, so it inherits the properties of all Objects and can be substituted wherever an Object is required. We can therefore pass our ZoomMap pointer directly to astShow, as follows: c- f+ If you consult the description of AST\_SHOW in \appref{ss:functiondescriptions}, you will find that it takes a pointer to an Object as its argument (in addition to the usual STATUS argument). Although we have only a ZoomMap pointer available, fortunately this is not a problem. If you refer to the brief class hierarchy described above (\secref{ss:mappinghierarchy}), you will see that a ZoomMap is an Object, albeit a specialised one, so it inherits the properties of all Objects and can be substituted wherever an Object is required. We can therefore pass our ZoomMap pointer directly to AST\_SHOW, as follows: f- c+ \small \begin{terminalv} astShow( zoommap ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SHOW( ZOOMMAP, STATUS ) \end{terminalv} \normalsize f- The output from this will appear on the standard output stream and should look like the following: \small \begin{terminalv} Begin ZoomMap Nin = 2 IsA Mapping Zoom = 5 End ZoomMap \end{terminalv} \normalsize Here, the ``Begin'' and ``End'' lines mark the beginning and end of the ZoomMap, while the values 2 and 5 are simply the values we supplied to initialise it (\secref{ss:objectcreation}). These have been given simple names to make them easy to refer to. The line in the middle which says ``IsA~Mapping'' is a dividing line between the two values. It indicates that the ``Nin'' value is a property shared by all Mappings, so the ZoomMap has inherited this from its \emph{parent class} (Mapping). The ``Zoom'' value, however, is specific to a ZoomMap and isn't shared by other kinds of Mappings. \subsection{\label{ss:gettingattributes}Getting Attribute Values} We saw above (\secref{ss:displayingobjects}) how to display the internal values of an Object, but what about accessing these values from a program? Not all internal Object values are accessible in this way, but many are. Those that are, are called \emph{attributes}. A description of all the attributes used by the AST library can be found in \appref{ss:attributedescriptions}. c+ Attributes come in several data types (character string, integer, boolean and floating point) and there is a standard way of obtaining their values. As an example, consider obtaining the value of the Nin attribute for the ZoomMap created earlier. This could be done as follows: c- f+ Attributes come in several data types (character string, integer, boolean and floating point) and there is a standard way of obtaining their values. As an example, consider obtaining the value of the Nin attribute for the ZoomMap created earlier. This could be done as follows: f- c+ \small \begin{terminalv} int nin; ... nin = astGetI( zoommap, "Nin" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER NIN ... NIN = AST_GETI( ZOOMMAP, 'Nin', STATUS ) \end{terminalv} \normalsize f- c+ Here, the function astGetI is used to extract the attribute value by giving it the ZoomMap pointer and the attribute name (attribute names are not case sensitive, but we have used consistent capitalisation in this document in order to identify them). Remember to use the ``ast.h'' header file to include the function prototype. c- f+ Here, the integer function AST\_GETI is used to extract the attribute value by giving it the ZoomMap pointer and the attribute name (attribute names are not case sensitive, but we have used consistent capitalisation in this document in order to identify them). Remember to use the AST\_PAR include file to save having to declare AST\_GETI as integer yourself. f- c+ If we had wanted the value of the Zoom attribute, we would probably have used astGetD instead, this being a double version of the same function, for example: c- f+ If we had wanted the value of the Zoom attribute, we would probably have used AST\_GETD instead, this being a double precision version of the same function, for example: f- c+ \small \begin{terminalv} double zoom; ... zoom = astGetD( zoommap, "Zoom" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} DOUBLE PRECISION ZOOM ... ZOOM = AST_GETD( ZOOMMAP, 'Zoom', STATUS ) \end{terminalv} \normalsize f- c+ However, we could equally well have read the Nin value as double, or the Zoom value as an integer, or whatever we wanted. c- f+ However, we could equally well have read the Nin value as double precision, or the Zoom value as an integer, or whatever we wanted. f- c+ The data type you want returned is specified simply by replacing the final character of the astGetX function name with C~(character string), D~(double), F~(float), I~(int) or L~(long). If possible, the value is converted to the type you want. If not, an error message will result. Note that all floating point values are stored internally as double, and all integer values as int. Boolean values are also stored as integers, but only take the values 1 and 0 (for true/false). c- f+ The data type you want returned is specified simply by replacing the final character of the AST\_GETx function name with C~(character), D~(double precision), I~(integer), L~(logical) or R~(real). If possible, the value is converted to the type you want. If not, an error message will result. In converting from integer to logical, zero is regarded as .FALSE.\ and non-zero as .TRUE.. Note that all floating point values are stored internally as double precision. Boolean values are stored as integers, but only take the values 1 and 0 (for true/false). f- \subsection{\label{ss:settingattributes}Setting Attribute Values} Some attribute values are read-only and cannot be altered after an Object has been created. The Nin attribute of a ZoomMap (describing the number of coordinates) is like this. It is defined when the ZoomMap is created, but cannot then be altered. Other attributes, however, can be modified whenever you want. A ZoomMap's Zoom attribute is like this. If we wanted to change it, this could be done simply as follows: c+ \small \begin{terminalv} astSetD( zoommap, "Zoom", 99.6 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SETD( ZOOMMAP, 'Zoom', 99.6D0, STATUS ) \end{terminalv} \normalsize f- c+ which sets the value to 99.6. As when getting an attribute value (\secref{ss:gettingattributes}), you have a choice of which data type you will use to supply the new value. For instance, you could use an integer value, as in: c- f+ which sets the value to 99.6 (double precision). As when getting an attribute value (\secref{ss:gettingattributes}), you have a choice of which data type you will use to supply the new value. For instance, you could use an integer value, as in: f- c+ \small \begin{terminalv} astSetI( zoommap, "Zoom", 99 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SETI( ZOOMMAP, 'Zoom', 99, STATUS ) \end{terminalv} \normalsize f- c+ and the necessary data conversion would occur. You specify the data type you want to supply simply by replacing the final character of the astSetX function name with C~(character string), D~(double), F~(float), I~(int) or L~(long). Setting a boolean attribute to any non-zero integer causes it to take the value 1. c- f+ and the necessary data conversion would occur. You specify the data type you want to supply simply by replacing the final character of the AST\_SETx routine name with C~(character), D~(double precision), I~(integer), L~(logical) or R~(real). Setting a boolean attribute to any non-zero integer causes it to take the value 1. f- c+ An alternative way of setting attribute values for Objects is to use the astSet function (\emph{i.e.}\ with no final character specifying a data type). In this case, you supply the attribute values in a character string. The big advantage of this method is that you can assign values to several attributes at once, separating them with commas. This also reads more naturally in programs. For example: c- f+ An alternative way of setting attribute values for Objects is to use the AST\_SET routine (\emph{i.e.}\ with no final character specifying a data type). In this case, you supply the attribute values in a character string. The big advantage of this method is that you can assign values to several attributes at once, separating them with commas. This also reads more naturally in programs. For example: f- c+ \small \begin{terminalv} astSet( zoommap, "Zoom=99.6, Report=1" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SET( ZOOMMAP, 'Zoom=99.6, Report=1', STATUS ) \end{terminalv} \normalsize f- c+ would set values for both the Zoom attribute and the Report attribute (about which more shortly---\secref{ss:transforming}). You don't really have to worry about data types with this method, as any character representation will do. Note, when using astSet, a literal comma may be included in an attribute value by enclosed the value in quotation marks: \small \begin{terminalv} astSet( skyframe, 'SkyRef="12:13:32,-23:12:44"' ); \end{terminalv} \normalsize c- f+ would set values for both the Zoom attribute and the Report attribute (about which more shortly---\secref{ss:transforming}). You don't really have to worry about data types with this method, as any character representation will do (although you must use 0/1 instead of .TRUE./.FALSE., which are not supported). Note, when using AST\_SET, a literal comma may be included in an attribute value by enclosed the value in quotation marks: \small \begin{terminalv} CALL AST_SET( SKYFRAME, 'SkyRef="12:13:32,-23:12:44"', STATUS ) \end{terminalv} \normalsize f- c+ Another attractive feature of astSet is that you can build the character string which contains the attribute settings in the same way as when using the C run time library ``printf'' function. This is most useful when the values you want to set are held in other variables. For example: \small \begin{terminalv} double zoom = 99.6; int report = 1; ... astSet( zoommap, "Zoom=%g, Report=%d", zoom, report ); \end{terminalv} \normalsize would replace the ``\%'' conversion specifications by the values supplied as additional arguments. Any number of additional arguments may be supplied and the formatting rules are exactly the same as for the C ``printf'' family of functions. This is a very flexible technique, but does contain one pitfall: \begin{quote} \textbf{Pitfall.} The default precision used by ``printf'' (and astSet) for floating point values is only 6 decimal digits, corresponding approximately to float on most machines, whereas the AST library stores such values internally as doubles. You should be careful to specify a larger precision (such as DBL\_DIG, as defined in $<$float.h$>$) when necessary. For example: \small \begin{terminalv} #include ... astSet( zoommap, "Zoom=%.*g", DBL_DIG, double_value ); \end{terminalv} \normalsize \end{quote} Substituted strings may contain commas and this is a useful way of assigning such strings as attribute values without the comma being interpreted as an assignment separator, for example: \small \begin{terminalv} astSet( object, "Attribute=%s", "A string, containing a comma" ); \end{terminalv} \normalsize This is equivalent to using astSetC and one of these two methods should always be used when assigning string attribute values which might potentially contain a comma (\emph{e.g.}\ strings obtained from an external source). However, you should not attempt to use astSet to substitute strings that contain newline characters, since these are used internally as separators between adjacent attribute assignments. c- \label{ss:attributeinitialisation} c+ Finally, a very convenient way of setting attribute values is to do so at the same time as you create an Object. Every Object constructor function has a final character string argument which allows you to do this. Although you can simply supply an empty string, it is an ideal opportunity to initialise the Object to have just the attributes you want. For example, we might have created our original ZoomMap with: c- f+ Finally, a very convenient way of setting attribute values is to do so at the same time as you create an Object. Every Object constructor function has a penultimate character argument which allows you to do this. Although you can simply leave this blank, it is an ideal opportunity to initialise the Object to have just the attributes you want. For example, we might have created our original ZoomMap with: f- c+ \small \begin{terminalv} zoommap = astZoomMap( 2, 5.0, "Report=1" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} ZOOMMAP = AST_ZOOMMAP( 2, 5.0D0, 'Report=1', STATUS ) \end{terminalv} \normalsize f- and it would then start life with its Report attribute set to 1. c+ The ``printf''-style substitution described above may also be used here. c- \subsection{\label{ss:defaultingattributes}Testing, Clearing and Defaulting Attributes} c+ You can use the astGetX family of functions (\secref{ss:gettingattributes}) to get a value for any Object attribute at any time, regardless of whether a value has previously been set for it. If no value has been set, the AST library will generate a suitable default value. c- f+ You can use the AST\_GETx family of routines (\secref{ss:gettingattributes}) to get a value for any Object attribute at any time, regardless of whether a value has previously been set for it. If no value has been set, the AST library will generate a suitable default value. f- Often, the default value of an attribute will not simply be trivial (zero or blank) but may involve considerable processing to calculate. Wherever possible, defaults are designed to be real-life, sensible values that convey information about the state of the Object. In particular, they may often be based on the values of other attributes, so their values may change in response to changes in these other attributes. The ZoomMap class that we have studied so far is a little too simple to show this behaviour, but we will meet it later on. An attribute that returns a default value in this way is said to be \emph{un-set}. Conversely, once an explicit value has been assigned to an attribute, it becomes \emph{set} and will always return precisely that value, never a default. c+ The distinction between set and un-set attributes is important and affects the behaviour of several key routines in the AST library. You can test if an attribute is set using the function astTest, which returns a boolean (integer) result, as in: c- f+ The distinction between set and un-set attributes is important and affects the behaviour of several key routines in the AST library. You can test if an attribute is set using the logical function AST\_TEST, as in: f- c+ \small \begin{terminalv} if ( astTest( zoommap, "Report" ) ) { } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} IF ( AST_TEST( ZOOMMAP, 'Report', STATUS ) ) THEN END IF \end{terminalv} \normalsize f- f+ (as usual, remember to include the AST\_PAR file to declare the function as LOGICAL, or make this declaration yourself). f- c+ Once an attribute is set, you can return it to its un-set state using astClear. The effect is as if it had never been set in the first place. For example: c- f+ Once an attribute is set, you can return it to its un-set state using AST\_CLEAR. The effect is as if it had never been set in the first place. For example: f- c+ \small \begin{terminalv} astClear( zoommap, "Report" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_CLEAR( ZOOMMAP, 'Report', STATUS ) \end{terminalv} \normalsize f- would ensure that the default value of the Report attribute is used subsequently. %\subsection{TBW--Handling Character Attributes} \subsection{\label{ss:transforming}Transforming Coordinates} We now have the necessary apparatus to start using our ZoomMap to show what it is really for. Here, we will also encounter a routine that is a little more fussy about the type of pointer it will accept. The purpose of a ZoomMap is to multiply coordinates by a constant zoom factor. To witness this in action, we will first set the Report attribute for our ZoomMap to a non-zero value: c+ \small \begin{terminalv} astSet( zoommap, "Report=1" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SET( ZOOMMAP, 'Report=1', STATUS ) \end{terminalv} \normalsize f- This boolean (integer) attribute, which is present in all Mappings (and a ZoomMap is a Mapping), causes the automatic display of all coordinate values that the Mapping converts. It is not a good idea to leave this feature turned on in a finished program, but it can save a lot of work during debugging. c+ Our next step is to set up some coordinates for the ZoomMap to work on, using two arrays ``xin'' and ``yin'', and two arrays to receive the transformed coordinates, ``xout'' and ``yout''. Note that these are arrays of double, as are all coordinate data processed by the AST library: c- f+ Our next step is to set up some coordinates for the ZoomMap to work on, using two arrays XIN and YIN, and two arrays to receive the transformed coordinates, XOUT and YOUT. Note that these arrays are double precision, as are all coordinate data processed by the AST library: f- c+ \small \begin{terminalv} double xin[ 10 ] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0 }; double yin[ 10 ] = { 0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0 }; double xout[ 10 ]; double yout[ 10 ]; \end{terminalv} \normalsize c- f+ \small \begin{terminalv} DOUBLE PRECISION XIN( 10 ), YIN( 10 ), XOUT( 10 ), YOUT( 10 ) DATA XIN / 0D0, 1D0, 2D0, 3D0, 4D0, 5D0, 6D0, 7D0, 8D0, 9D0 / DATA YIN / 0D0, 2D0, 4D0, 6D0, 8D0, 10D0, 12D0, 14D0, 16D0, 18D0 / \end{terminalv} \normalsize f- c+ We will now use the function astTran2 to transform the input coordinates. This is the most commonly-used (2-dimensional) coordinate transformation function. If you look at its description in \appref{ss:functiondescriptions}, you will see that it requires a pointer to a Mapping, so we cannot supply just any old Object pointer, as we could with the functions discussed previously. If we passed it a pointer to an inappropriate Object, an error message would result. c- f+ We will now use the routine AST\_TRAN2 to transform the input coordinates. This is the most commonly-used (2-dimensional) coordinate transformation routine. If you look at its description in \appref{ss:functiondescriptions}, you will see that it requires a pointer to a Mapping, so we cannot supply just any old Object pointer, as we could with the routines discussed previously. If we passed it a pointer to an inappropriate Object, an error message would result. f- c+ Fortunately, a ZoomMap is a Mapping (\appref{ss:classhierarchy}), so we can use it with astTran2 to transform our coordinates, as follows: c- f+ Fortunately, a ZoomMap is a Mapping (\appref{ss:classhierarchy}), so we can use it with AST\_TRAN2 to transform our coordinates, as follows: f- c+ \small \begin{terminalv} astTran2( zoommap, 10, xin, yin, 1, xout, yout ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_TRAN2( ZOOMMAP, 10, XIN, YIN, .TRUE., XOUT, YOUT, STATUS ) \end{terminalv} \normalsize f- c+ Here, 10 is the number of points we want to transform and the fifth argument value of 1 indicates that we want to transform in the \emph{forward} direction (from input to output). c- f+ Here, 10 is the number of points we want to transform and the fifth argument value of .TRUE.\ indicates that we want to transform in the \emph{forward} direction (from input to output). f- Because our ZoomMap's Report attribute is set to 1, this will cause the effects of the ZoomMap on the coordinates to be displayed on the standard output stream: \small \begin{terminalv} (0, 0) --> (0, 0) (1, 2) --> (5, 10) (2, 4) --> (10, 20) (3, 6) --> (15, 30) (4, 8) --> (20, 40) (5, 10) --> (25, 50) (6, 12) --> (30, 60) (7, 14) --> (35, 70) (8, 16) --> (40, 80) (9, 18) --> (45, 90) \end{terminalv} \normalsize c+ This shows the coordinate values of each point both before and after the ZoomMap is applied. You can see that each coordinate value has been multiplied by the factor 5 determined by the Zoom attribute value. The transformed coordinates are now stored in the ``xout'' and ``yout'' arrays. c- f+ This shows the coordinate values of each point both before and after the ZoomMap is applied. You can see that each coordinate value has been multiplied by the factor 5 determined by the Zoom attribute value. The transformed coordinates are now stored in the XOUT and YOUT arrays. f- c+ If we wanted to transform in the opposite direction, we need simply change the fifth argument of astTran2 from 1 to 0. We can also feed the output coordinates from the above back into the function: c- f+ If we wanted to transform in the opposite direction, we need simply change the fifth argument of AST\_TRAN2 from .TRUE. to .FALSE.. We can also feed the output coordinates from the above back into the routine: f- c+ \small \begin{terminalv} astTran2( zoommap, 10, xout, yout, 0, xin, yin ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_TRAN2( ZOOMMAP, 10, XOUT, YOUT, .FALSE., XIN, YIN, STATUS ) \end{terminalv} \normalsize f- The output would then look like: \small \begin{terminalv} (0, 0) --> (0, 0) (5, 10) --> (1, 2) (10, 20) --> (2, 4) (15, 30) --> (3, 6) (20, 40) --> (4, 8) (25, 50) --> (5, 10) (30, 60) --> (6, 12) (35, 70) --> (7, 14) (40, 80) --> (8, 16) (45, 90) --> (9, 18) \end{terminalv} \normalsize This is termed the \emph{inverse} transformation (we have converted from output to input) and you can see that the original coordinates have been recovered by dividing by the Zoom factor. \subsection{\label{ss:annullingpointers}Managing Object Pointers} So far, we have looked at creating Objects and using them in various simple ways but have not yet considered how to get rid of them again. c+ Every Object consumes various computer resources (principally memory) and should be disposed of when it is no longer required, so as to free up these resources. One way of doing this (not necessarily the best---\secref{ss:contexts}) is to \emph{annul} each Object pointer once you have finished with it, using astAnnul. For example: c- f+ Every Object consumes various computer resources (principally memory) and should be disposed of when it is no longer required, so as to free up these resources. One way of doing this (not necessarily the best---\secref{ss:contexts}) is to \emph{annul} each Object pointer once you have finished with it, using AST\_ANNUL. For example: f- c+ \small \begin{terminalv} zoommap = astAnnul( zoommap ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_ANNUL( ZOOMMAP, STATUS ) \end{terminalv} \normalsize f- c+ This indicates that you have finished with the pointer. Since astAnnul always returns the null value AST\_\_NULL (as defined in ``ast.h''), the recommended way of using it, as here, is to assign the returned value to the pointer being annulled. This ensures that any attempt to use the pointer again will generate an error message. c- f+ This indicates that you have finished with the pointer and sets it to the null value AST\_\_NULL (as defined in the AST\_PAR include file), so that any attempt to use it again will generate an error message. f- c+ In general, this process may not delete the Object, because there may still be other pointers associated with it. However, each Object maintains a count of the number of pointers associated with it and will be deleted if you annul the final pointer. Using astAnnul consistently will therefore ensure that all Objects are disposed of at the correct time. You can determine how many pointers are associated with an Object by examining its (read-only) RefCount attribute. c- f+ In general, this process may not delete the Object, because there may still be other pointers associated with it. However, each Object maintains a count of the number of pointers associated with it and will be deleted if you annul the final pointer. Using AST\_ANNUL consistently will therefore ensure that all Objects are disposed of at the correct time. You can determine how many pointers are associated with an Object by examining its (read-only) RefCount attribute. f- c+ \subsection{\label{ss:contexts}AST Pointer Contexts---Begin and End} c- f+ \subsection{\label{ss:contexts}AST Pointer Contexts---Begin and End} f- c+ The use of astAnnul (\secref{ss:annullingpointers}) is not completely foolproof, however. Consider the following: c- f+ The use of AST\_ANNUL (\secref{ss:annullingpointers}) is not completely foolproof, however. Consider the following: f- c+ \small \begin{terminalv} astShow( astZoomMap( 2, 5.0, "" ) ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SHOW( AST_ZOOMMAP( 2, 5.ODO, ' ', STATUS ), STATUS ) \end{terminalv} \normalsize f- c+ This creates a ZoomMap and displays it on standard output (\secref{ss:displayingobjects}). Using function invocations as arguments to other functions in this way is very convenient because it avoids the need for intermediate pointer variables. However, the pointer generated by astZoomMap is still active, and since we have not stored its value, we cannot use astAnnul to annul it. The ZoomMap will therefore stay around until the end of the program. c- f+ This creates a ZoomMap and displays it on standard output (\secref{ss:displayingobjects}). Using function invocations as arguments to other routines in this way is very convenient because it avoids the need for intermediate pointer variables. However, the pointer generated by AST\_ZOOMMAP is still active, and since we have not stored its value, we cannot use AST\_ANNUL to annul it. The ZoomMap will therefore stay around until the end of the program. f- c+ A simple way to avoid this problem is to enclose all use of AST functions between invocations of astBegin and astEnd, for example: c- f+ A simple way to avoid this problem is to enclose all use of AST routines between calls to AST\_BEGIN and AST\_END, for example: f- c+ \small \begin{terminalv} astBegin; astShow( astZoomMap( 2, 5.0, "" ) ); astEnd; \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_BEGIN( STATUS ) CALL AST_SHOW( AST_ZOOMMAP( 2, 5.ODO, ' ', STATUS ), STATUS ) CALL AST_END( STATUS ) \end{terminalv} \normalsize f- c+ When the expansion of astEnd (which is a macro) executes, every Object pointer created since the previous use of astBegin (also a macro) is automatically annulled and any Objects left without pointers are deleted. This provides a simple solution to managing Objects and their pointers, and allows you to create Objects very freely without needing to keep detailed track of each one. Because this is so convenient, we implicitly assume that astBegin and astEnd are used in most of the examples given in this document. Pointer management is not generally shown explicitly unless it is particularly relevant to the point being illustrated. c- f+ When the AST\_END call executes, every Object pointer created since the previous AST\_BEGIN call is automatically annulled and any Objects left without pointers are deleted. This provides a simple solution to managing Objects and their pointers, and allows you to create Objects very freely without needing to keep detailed track of each one. Because this is so convenient, we implicitly assume that AST\_BEGIN and AST\_END are used in most of the examples given in this document. Pointer management is not generally shown explicitly unless it is particularly relevant to the point being illustrated. f- c+ If necessary, astBegin and astEnd may be nested, like blocks delimited by ``\{\ldots\}'' in C, to define a series of AST pointer contexts. Each use of astEnd will then annul only those Object pointers created since the matching use of astBegin. c- f+ If necessary, calls to AST\_BEGIN and AST\_END may be nested, like IF\ldots ENDIF blocks in Fortran, to define a series of AST pointer contexts. Each call to AST\_END will then annul only those Object pointers created since the matching call to AST\_BEGIN. f- \subsection{Exporting, Importing and Exempting AST Pointers} c+ The astExport function allows you to export particular pointers from one AST context (\secref{ss:contexts}) to the next outer one, as follows: c- f+ The AST\_EXPORT routine allows you to export particular pointers from one AST context (\secref{ss:contexts}) to the next outer one, as follows: f- c+ \small \begin{terminalv} astExport( zoommap ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_EXPORT( ZOOMMAP, STATUS ) \end{terminalv} \normalsize f- c+ This would identify the pointer stored in ``zoommap'' as being required after the end of the current AST context. It causes any pointers nominated in this way to survive the next use of astEnd (but only one such use) unscathed, so that they are available to the next outer context. This facility is not needed often, but is invaluable when the purpose of your astBegin\ldots astEnd block is basically to generate an Object pointer. Without this, there is no way of getting that pointer out. c- f+ This would identify the pointer stored in ZOOMMAP as being required after the end of the current AST context. It causes any pointers nominated in this way to survive the next call to AST\_END (but only one such call) unscathed, so that they are available to the next outer context. This facility is not needed often, but is invaluable when the purpose of your AST\_BEGIN\ldots AST\_END block is basically to generate an Object pointer. Without this, there is no way of getting that pointer out. f- f+ The AST\_IMPORT routine can be used in a similar manner to import a pointer into the current context, so that it is deleted when the current context is closed using AST\_END. f- c+ The astImport routine can be used in a similar manner to import a pointer into the current context, so that it is deleted when the current context is closed using astEnd. c- c+ Sometimes, you may also want to exempt a pointer from all the effects of AST contexts. You should not need to do this often, but it will prove essential if you ever need to write a library of functions that stores AST pointers as part of its own internal data. Without some form of exemption, the caller of your routines could cause the pointers you have stored to be annulled---thus corrupting your internal data---simply by using astEnd. To avoid this, you should use astExempt on each pointer that you store, for example: c- f+ Sometimes, you may also want to exempt a pointer from all the effects of AST contexts. You should not need to do this often, but it will prove essential if you ever need to write a library of routines that stores AST pointers as part of its own internal data. Without some form of exemption, the caller of your routines could cause the pointers you have stored to be annulled---thus corrupting your internal data---simply by using AST\_END. To avoid this, you should use AST\_EXEMPT on each pointer that you store, for example: f- c+ \small \begin{terminalv} astExempt( zoommap ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_EXEMPT( ZOOMMAP, STATUS ) \end{terminalv} \normalsize f- c+ This will prevent the pointer being affected by any subsequent use of astEnd. Of course, it then becomes your responsibility to annul this pointer (using astAnnul) when it is no longer required. c- f+ This will prevent the pointer being affected by any subsequent use of AST\_END. Of course, it then becomes your responsibility to annul this pointer (using AST\_ANNUL) when it is no longer required. f- c+ \subsection{AST Objects within Multi-threaded Applications} When the AST library is built from source, the build process checks to see if the POSIX threads library (``\texttt{pthreads}'') is available. If so, appropriate \texttt{pthreads} calls are inserted into the AST source code to ensure that AST is thread-safe, and the AST\_\_THREADSAFE macro (defined in the ``ast.h'' header file) is set to ``\texttt{1}''. If the \texttt{pthreads} library cannot be found when AST is built, a working version of the AST library will still be created, but it will not be thread-safe. In this case the AST\_\_THREADSAFE macro will be set to ``\texttt{0}'' in ast.h. The rest of this section assumes that the thread-safe version of AST is being used. Note, some AST functions call externally specified functions (\emph{e.g.} the source and sink functions used by the Channel class or the graphics primitives functions used by the Plot class). AST does not know whether such functions are thread-safe or not. For this reason, invocations of these functions within a multi-threaded environment are serialised using a mutex in order to avoid two or more threads executing an external function simultaneously. If an application uses more than one thread, the possibility arises that an Object created by one thread may be accessed by another thread, potentially simultaneously. If any of the threads modifies any aspect of the Object, this could lead to serious problems within the other threads. For this reason, some restrictions are placed on how Objects can be used in a multi-threaded application. \subsubsection{Locking AST Objects for Exclusive Use} The basic restriction is that a thread can only access Objects that it has previously locked for its own exclusive use. If a thread attempts to access any Object that it has not locked, an error is reported. The astAnnul function is the one exception to this restriction. Pointers for Objects not currently locked by the calling thread can be annulled succesfully using astAnnul. This means that a thread that has finished with an Object pointer can unlock the Object by passing the pointer to astUnlock (so that other threads can use the Object via their own cloned pointers), and can then annul the pointer using astAnnul. Note, however, that an error will be reported by astAnnul if the supplied pointer has been locked by another thread using astLock. When an Object is created, it is initially locked by the calling thread. Therefore a thread does not need to lock an Object explicitly if it was created in the same thread. If the Object pointer is then passed to another thread, the first thread must unlock the Object using astUnlock and the second thread must then lock it using astLock. If a thread attempts to lock an Object that is already locked by another thread, it can choose to report an error immediately or to wait until the Object is available. The astThread function can be used to determine whether an Object is locked by the running thread, locked by another thread, or unlocked. If two or more threads need simultaneous access to an Object, a deep copy of the Object should be taken for each thread, using astCopy, and then the copies should be unlocked and passed to the othe threads, which should then lock them. Note, if a thread modifies the Object, the modification will have no effect on the other threads, because the Object copies are independent of each other. \subsubsection{AST Pointer Contexts} Each thread maintains its own set of nested AST contexts, so when astEnd is called, only Objects that are locked by the current thread will be annulled. If an Object is unlocked by a thread using astUnlock, it is exempted from context handling so that subsequent invocations of astEnd will not cause it to be annulled (this is similar to using astExempt on the Object). When the Object is subsequently locked by another thread using astLock, it will be imported into the context that was active when astLock was called. c- \subsection{\label{ss:copyingobjects}Copying Objects} The AST library makes extensive use of pointers, not only for accessing Objects directly, but also as a means of storing Objects inside other Objects (a number of classes of Object are designed to hold collections of other Objects). Rather than copy an Object in its entirety, a pointer to the interior Object is simply stored in the enclosing Object. This means that Objects may frequently not be completely independent of each other because, for instance, they both contain pointers to the same sub-Object. In this situation, changing one Object (say assigning an attribute value) may affect the other one \emph{via} the common Object. c+ It is difficult to describe all cases where this may happen, so you should always be alert to the possibility. Fortunately, there is a simple solution. If you require two Objects to be independent, then simply use astCopy to make a copy of one, \emph{e.g.}: c- f+ It is difficult to describe all cases where this may happen, so you should always be alert to the possibility. Fortunately, there is a simple solution. If you require two Objects to be independent, then simply use AST\_COPY to make a copy of one, \emph{e.g.}: f- c+ \small \begin{terminalv} AstZoomMap *zoommap1, *zoommap2; ... zoommap2 = astCopy( zoommap1 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER ZOOMMAP1, ZOOMMAP2 ... ZOOMMAP2 = AST_COPY( ZOOMMAP1, STATUS ) \end{terminalv} \normalsize f- This process will create a true copy of any Object and return a pointer to the copy. This copy will not contain any pointers to any component of the original Object (everything is duplicated), so you can then modify it safely, without fear of affecting either the original or any other Object. %\subsection{TBW - Inheritance} c+ \subsection{C Pointer Types} At this point it is necessary to confess to a small amount of deception. So far, we have been passing Object pointers to AST functions in order to perform operations on those Objects. In fact, however, what we were using were not true C functions at all, but merely macros which invoke a related set of hidden functions with essentially the same arguments. In practical terms, this makes very little difference to how you use the functions, as we will continue to call them.\footnote{About the only difference is that you cannot store a pointer to an AST ``function'' in a variable and use the variable's value to invoke that function again later.} The reason for this deception has to do with the rules for data typing in C. Recall that most AST functions can be used to process Objects from a range of different classes (\secref{ss:objecthierarchy}). In C, this means passing different pointer types to the same function and most C compilers will not permit this (at least, not without grumbling) because it usually indicates a programming error. In AST, however, it is perfectly safe if done properly. Some way is therefore needed of circumventing the normal compiler checking. The normal way of doing this in C is with a cast. This approach quickly becomes cumbersome, however, so we have adopted the strategy of wrapping each function in a macro which applies the appropriate cast for you. This means that you can pass pointers of any type to any AST function. For example, in passing a ZoomMap pointer to astShow: \small \begin{terminalv} AstZoomMap *zoommap; ... zoommap = astZoomMap( 2, 5.0, "" ); astShow( zoommap ); \end{terminalv} \normalsize we are exploiting this mechanism to avoid a compiler warning, because the notional type of astShow's parameter is AstObject$*$ (not AstZoomMap$*$). We must still guard against programming errors, however, so every pointer's type is checked by the enclosing macro immediately before any AST function executes. This allows pointer mis-matches (in the more liberal AST sense---\emph{i.e.}\ taking account of the class hierarchy, rather than the stricter C sense) to be detected at run-time and a suitable error message will be reported. This message should also identify the line where the error occurs. A similar strategy is used when pointers are returned by AST functions (\emph{i.e.}\ as the function result). In this case the pointer is cast to void$*$, although we retain the notional pointer type in the function's documentation (\emph{e.g.}\ \appref{ss:functiondescriptions}). This allows you to assign function results to pointer variables without using an explicit cast. For example, the astRead function returns an Object pointer, but might be used to read (say) a ZoomMap as follows: \small \begin{terminalv} AstChannel *channel; AstZoomMap *zoommap; ... zoommap = astRead( channel ); \end{terminalv} \normalsize Strictly, there is a C pointer mis-match here, but it is ignored because the operation makes perfect sense to AST. \textbf{There is an important exception to this, however, in that constructor functions always return strongly-typed pointers.} What we mean by this is that the returned pointer is never implicitly cast to void$*$. You must therefore match pointer types when you initially create an Object using its constructor, such as in the following: \small \begin{terminalv} AstZoomMap *zoommap; ... zoommap = astZoomMap( 2, 5.0, "" ); \end{terminalv} \normalsize If the variable receiving the pointer is of a different type, an appropriate cast should be used, as in: \small \begin{terminalv} AstMapping *mapping; ... mapping = (AstMapping *) astZoomMap( 2, 5.0, "" ); \end{terminalv} \normalsize This is an encouragement for you to declare your pointer types consistently, since this is of great benefit to anyone trying to understand your software. Finally, we should also make one more small confession---AST pointers are not really pointers at all. Although they behave like pointers, the actual ``values'' stored are not the addresses of C data structures. This means that you cannot de-reference an AST pointer to examine the data within (although you can use astShow instead---\secref{ss:displayingobjects}). This is necessary so that AST pointers can be made unique even although several of them might reference the same Object. c- \subsection{\label{ss:errordetection}Error Detection} c+ If an error occurs in an AST function (for example, if you supply an invalid argument, such as a pointer to the wrong class of Object), an error message will be written to the standard error stream and the function will immediately return. c- f+ If an error occurs in an AST routine (for example, if you supply an invalid argument, such as a pointer to the wrong class of Object), an error message will be written to the standard error stream and the function will immediately return. f- c+ To indicate than an error has occurred, an AST \emph{error status} value is used. This integer value is stored internally by AST and is initially clear (\emph{i.e.}\ set to zero\footnote{We will assume throughout that the ``OK'' value is zero, as it currently is. However, a different value could, in principle, be used if the environment in which AST is running requires it. This is why a simple interface is provided to isolate you from the actual value of the error status.} to indicate no error). If an error occurs, it becomes set to a different \emph{error value}, which allows you to detect the error, as follows: c- f+ To indicate that an error has occurred, each AST routine that can potentially fail has a final integer \emph{error status} argument called STATUS. This is both an input and an output argument. Normally, you should declare a single error status variable and pass it as the STATUS argument to every AST routine you invoke. This variable must initially be cleared (\emph{i.e.}\ set to zero\footnote{We will assume throughout that the ``OK'' value is zero, as it currently is. However, a different value could, in principle, be used if the environment in which AST is running requires it. To allow for this possibility, you might prefer to use a parameter constant to represent the value zero when testing for errors.} to indicate no error). If an error occurs, the STATUS argument is returned set to a different \emph{error value}, which allows you to detect the error, as follows: f- c+ \small \begin{terminalv} zoommap = astZoomMap( 2, 5.0, "Title=My ZoomMap" ); if ( !astOK ) { } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} STATUS = 0 ... ZOOMMAP = AST_ZOOMMAP( 2, 5.0D0, 'Title=My ZoomMap', STATUS ) IF ( STATUS .NE. 0 ) THEN END IF \end{terminalv} \normalsize f- c+ The macro astOK is used to test whether the AST error status is still OK. In this example it would not be, because we have attempted to set a value for the Title attribute of a ZoomMap and a ZoomMap does not have such an attribute. The actual value of the AST error status can be obtained using the astStatus macro, as follows: \small \begin{terminalv} int status; ... status = astStatus; \end{terminalv} \normalsize c- f+ In this example, an error would be detected because we have attempted to set a value for the Title attribute of a ZoomMap and a ZoomMap does not have such an attribute. f- c+ A consequence of the AST error status being set is that almost all AST functions will subsequently cease to function and will instead simply return without action. This means that you do not need to use astOK to check for errors very frequently. Instead, you can usually simply invoke a succession of AST functions. If an error occurs in any of them, the following ones will do nothing and you can check for the error at the end, for example: c- f+ A consequence of the error status variable STATUS being set to an error value is that almost all AST routines will subsequently cease to function and will instead simply return without action. This means that you do not need to check for errors very frequently. Instead, you can usually simply invoke a succession of AST routines. If an error occurs in any of them, the following ones will do nothing and you can check for the error at the end, for example: f- c+ \small \begin{terminalv} astFunctionA( ... ); astFunctionB( ... ); astFunctionC( ... ); if ( !astOK ) { } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} STATUS = 0 ... CALL AST_ROUTINEA( ... , STATUS ) CALL AST_ROUTINEB( ... , STATUS ) CALL AST_ROUTINEC( ... , STATUS ) IF ( STATUS .NE. 0 ) THEN END IF \end{terminalv} \normalsize f- c+ There are, however, a few functions which do not adhere to this general rule and which will attempt to execute if the AST error status is set. These functions, such as astAnnul, are concerned with cleaning up and recovering resources. For example, in the following: c- f+ There are, however, a few routines which do not adhere to this general rule and which will attempt to execute if their STATUS argument is initially set. These routines, such as AST\_ANNUL, are concerned with cleaning up and recovering resources. For example, in the following: f- c+ \small \begin{terminalv} zoommap = astZoomMap( 2, 5.0, "" ); astFunctionX( ... ); astFunctionY( ... ); astFunctionZ( ... ); zoommap = astAnnul( zoommap ); if ( !astOK ) { } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} STATUS = 0 ... ZOOMMAP = AST_ZOOMMAP( 2, 5.0D0, ' ', STATUS ) CALL AST_ROUTINEX( ... , STATUS ) CALL AST_ROUTINEY( ... , STATUS ) CALL AST_ROUTINEZ( ... , STATUS ) CALL AST_ANNUL( ZOOMMAP, STATUS ) IF ( STATUS .NE. 0 ) THEN END IF \end{terminalv} \normalsize f- c+ astAnnul will execute normally in order to recover the resources associated with the ZoomMap that was created earlier, regardless of whether an error has occurred in any of the intermediate functions. Functions which behave in this way are noted in the relevant descriptions in \appref{ss:functiondescriptions}. c- f+ AST\_ANNUL will execute normally in order to recover the resources associated with the ZoomMap that was created earlier, regardless of whether an error has occurred in any of the intermediate routines. Routines which behave in this way are noted in the relevant descriptions in \appref{ss:functiondescriptions}. f- c+ If a serious error occurs, you will probably want to abort your program, but sometimes you may want to recover and carry on. Because very few AST functions will execute once the AST error status has been set, you must first clear this status by using the astClearStatus macro, as follows: c- f+ If a serious error occurs, you will probably want to abort your program, but sometimes you may want to recover and carry on. This is simply done by resetting your error status variable to zero, whereupon the AST routines you pass it to will execute normally again. f- c+ \small \begin{terminalv} astClearStatus; \end{terminalv} \normalsize c- c+ This will restore the AST error status to its OK value, so that AST functions execute normally again. c- c+ Occasionally, you may also need to set the AST error status to an explicit error value (see \secref{ss:channelsink} for an example). This is done using astSetStatus and can be used to communicate to AST that an error has occurred in some other item of software, for example: c- c+ \small \begin{terminalv} int new_status; ... astSetStatus( new_status ); \end{terminalv} \normalsize c- c+ The effect is that most AST routines will subsequently return without action, just as if an error had occurred within the AST library itself. c- c+ \subsection{Sharing the Error Status} In some software, it is usual to maintain a single integer error status variable which is accessed by each function as it executes. If an error occurs, this status variable is set and other functions can detect this and take appropriate action. If you use AST in such a situation, it can be awkward to have a separate internal error status used by AST functions alone. To remedy this, AST is capable of sharing the error status variable used by any other software, so long as they use the same conventions (\emph{i.e.}\ a C int with the same ``OK'' value). To enable this facility, you should pass the address of your status variable to astWatch, as follows: \small \begin{terminalv} int my_status; int *old_address; ... old_address = astWatch( &my_status ); \end{terminalv} \normalsize Henceforth, instead of using its own internal error status variable, AST will use the one you supply, so that it can detect errors flagged by other parts of your software. The address of the original error status variable is returned by astWatch, so you can restore the original behaviour later if necessary. Note that this facility is not available \emph{via} the Fortran interface to the AST library. c- \cleardoublepage \section{\label{ss:mappings}Inter-Relating Coordinate Systems (Mappings)} In \secref{ss:primer} we used the ZoomMap as an example of a Mapping. We saw how it could be used to transform coordinates from its input to its output and back again (\secref{ss:transforming}). We also saw how its behaviour could be controlled by setting various attributes, such as the Zoom factor and the Report attribute that made it display coordinate values as it transformed them. In this section, we will look at Mappings a bit more thoroughly and explore the behaviour which is common to all the Mappings provided by AST. This is good background for what follows, because many of the Objects we discuss later will also turn out to be Mappings in various disguises. \subsection{\label{ss:mappingclass}The Mapping Class} Before we start, it is worth taking a quick look at the Mapping class as a whole and some of the sub-classes it contains: \begin{terminalv} Mapping CmpMap DssMap GrismMap IntraMap LutMap MathMap MatrixMap PermMap PolyMap ChebyMap SlaMap SpecMap TimeMap UnitMap WcsMap ZoomMap Frame \end{terminalv} The Frame sub-class has been separated out here because it is covered in detail in \secref{ss:frames}. We start by looking at the parent class, Mapping. c+ AST does not provide a function to create a basic Mapping (\emph{i.e.}\ the astMapping constructor does not exist). This is because the Mapping class itself is ``virtual'' and basic Mappings are of no use in themselves. The Mapping class serves simply to contain the various specialised Mappings that exist. c- f+ AST does not provide a function to create a basic Mapping (\emph{i.e.}\ the AST\_MAPPING constructor does not exist). This is because the Mapping class itself is ``virtual'' and basic Mappings are of no use in themselves. The Mapping class serves simply to contain the various specialised Mappings that exist. f- However, it provides more than just a convenient heading for them because it bestows all classes of Mapping with common properties (\emph{e.g.}\ attributes) and behaviour. By examining the Mapping class, we are therefore examining the things that all other Mappings have in common. \subsection{The Mapping Model} The concept of a Mapping was illustrated in Figure~\ref{fig:mapping}. It is a black box which you can supply with a set of coordinate values in return for a set of transformed coordinates. The two sets are termed \emph{input} and \emph{output} coordinates. You can also go back the other way and transform output coordinates back into input coordinates, as we saw in \secref{ss:transforming}. \subsection{Changing Attributes of a Mapping} Many classes of Mapping have attributes that provide values for parameter used within the transformation. For instance, the ZoomMap class has an attribute called ``Zoom'' that gives the scalar value by which each coordinate is to be multiplied. These attribute values should be set when the Mapping is created and should not be changed afterwards. Indeed, the AST library will report an error if an attempt is made to change the value of a Mapping attribute. This is because, once created, Mappings are often later included within other objects such as FrameSets and CmpMaps. This means that in general there could be many active references to a single Mapping object within a program. Changing an attribute of the Mapping via one particular reference (i.e pointer) would cause all the other references to change too, with often undesirable or unpredictable consequences. To avoid this, Mappings are considered \emph{immutable} in most situations. The one exception is if the Mapping has not yet been cloned or included in another Object (\emph{i.e.} it has a reference couint of one) - changing the attributes of such a Mapping is allowed, and will not generate an error. Note, the Invert attribute of a Mapping is not subject to this rule and can be changed at any time. \subsection{Input and Output Coordinate Numbers} In general, the number of coordinates you feed into a Mapping to represent a single point need not be the same as the number that comes out. Often these numbers will be the same, and often they will both equal 2 (because 2-dimensional coordinate systems are common), but this needn't necessarily be the case. The number of coordinates required to specify an input point is represented by the integer attribute Nin and the number required to specify an output point is represented by Nout. These are read-only attributes common to all Mappings. Generally, their values are fixed when a Mapping is created. c+ In \secref{ss:objectcreation}, we saw how the Nin attribute for a ZoomMap was initialised by the call to the constructor function astZoomMap which created it. In this case, the Nout attribute was not needed and it implicitly took the same value as Nin, but we could have enquired about its value had we wanted, as follows: c- f+ In \secref{ss:objectcreation}, we saw how the Nin attribute for a ZoomMap was initialised by the call to the constructor function AST\_ZOOMMAP which created it. In this case, the Nout attribute was not needed and it implicitly took the same value as Nout, but we could have enquired about its value had we wanted, as follows: f- c+ \small \begin{terminalv} #include "ast.h" AstZoomMap *zoommap; int nout; ... nout = astGetI( zoommap, "Nout" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INCLUDE 'AST_PAR' INTEGER NOUT, STATUS, ZOOMMAP STATUS = 0 ... NOUT = AST_GETI( ZOOMMAP, 'Nout', STATUS ) \end{terminalv} \normalsize f- \subsection{Forward and Inverse Transformations} We stated earlier that a Mapping may be used to transform coordinates either from input to output, or \emph{vice versa}. These are termed its \emph{forward} and \emph{inverse} transformations. This statement was not quite accurate, however, because in general Mappings are only \textbf{potentially} capable of working in both directions. In practice, coordinate transformation may only be feasible in one direction or the other because some functions are not easily inverted (they may be multi-valued, for instance). Allowance must be made for this, so each Mapping has two read-only boolean (integer) attributes, TranForward and TranInverse, which indicate whether each transformation is available. A transformation is available if the corresponding attribute is non-zero, otherwise it is not.\footnote{Most of the Mappings provided by the AST library work in both directions, although the LutMap can behave otherwise.} If you enquire about the value of these attributes, a value of 0 or 1 is returned. Attempting to use a Mapping to apply a transformation which is not available will result in an error. \subsection{\label{ss:invertingmappings}Inverting Mappings} An important attribute, common to all Mappings, is the Invert flag. This is a boolean (integer) attribute that can be assigned a new value at any time. If it is non-zero, it has the effect of interchanging the Mapping's input and output coordinates and the Mapping is then said to be \emph{inverted}. By default, the Invert attribute is zero. There is no magic in this. There is no fancy arithmetic involved in inverting mathematical functions, for instance. The Invert flag is simply a switch that interchanges a Mapping's input and output ports. If it is non-zero, the Mapping's Nin and Nout attributes are swapped, its TranForward and TranInverse attributes are swapped, and when you ask for what was once the forward transformation you get the inverse transformation instead (and \emph{vice versa}). When you return the Invert attribute to zero, or clear it, the Mapping returns to its original behaviour. c+ Often, the actual value of the Invert attribute is unimportant and you simply wish to invert its boolean sense, so that what was the Mapping's input becomes its output and \emph{vice versa}. This is most easily accomplished using astInvert, as follows: c- f+ Often, the actual value of the Invert attribute is unimportant and you simply wish to invert its boolean sense, so that what was the Mapping's input becomes its output and \emph{vice versa}. This is most easily accomplished using AST\_INVERT, as follows: f- c+ \small \begin{terminalv} AstMapping *mapping; ... astInvert( mapping ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER MAPPING ... CALL AST_INVERT( MAPPING, STATUS ) \end{terminalv} \normalsize f- c+ If the Mapping you have happens to be the wrong way around, astInvert allows you to correct the problem. c- f+ If the Mapping you have happens to be the wrong way around, AST\_INVERT allows you to correct the problem. f- \subsection{Finding the Rate of Change of a Mapping Output} The c+ astRate c- f+ AST\_RATE f- function can be used to find the rate of change of any Mapping output with respect to any Mapping input, at a given input position. The method used produces good accuracy (typically a relative error of 10E-10 or less) but may require the Mapping to be evaluated 100 or more times. An estimate of the second derivative is also produced by this function. \subsection{Reporting Coordinate Transformations} We have already seen (\secref{ss:transforming}) how the boolean (integer) Report attribute of a Mapping works. If it is non-zero, the operation of transforming a set of coordinates will result in a report being written to standard output. This will display the coordinate values before and after transformation. It can save considerable time during program development by eliminating the need to add loops and output statements to your program. c+ In a finished program, however, you should be careful that the Report attribute is not set to a non-zero value unless you want to see the output (there may often be rather a lot of this!). To help prevent unwanted output being produced by accident, the Report attribute is unusual in that its value is not preserved when a Mapping is copied using astCopy (\secref{ss:copyingobjects}). Instead, it reverts to its default of zero (\emph{i.e.}\ un-set) in the copy. It also reverts to zero when a Mapping is written out, \emph{e.g.}\ to a file using a Channel (\secref{ss:channels}). c- f+ In a finished program, however, you should be careful that the Report attribute is not set to a non-zero value unless you want to see the output (there may often be rather a lot of this!). To help prevent unwanted output being produced by accident, the Report attribute is unusual in that its value is not preserved when a Mapping is copied using AST\_COPY (\secref{ss:copyingobjects}). Instead, it reverts to its default of zero (\emph{i.e.}\ un-set) in the copy. It also reverts to zero when a Mapping is written out, \emph{e.g.}\ to a file using a Channel (\secref{ss:channels}). f- %\subsection{TBW---More on Transforming Coordinates} \subsection{\label{ss:badcoordinates}Handling Missing (Bad) Coordinate Values} c+ Even when coordinates can, in principle, be transformed in either direction by a Mapping, there may still be instances where specific coordinate values cannot be handled. For example, the Mapping may be mathematically intractable (\emph{e.g.}\ singular) in certain places, or it may map a subset of one space on to another, so that some points in one space are not represented in the other. Sky projections often show this behaviour, since it is quite common to project only half of the celestial sphere on to two dimensions, omitting points on the opposite side of the sky. There are many other examples. c- f+ Even when coordinates can, in principle, be transformed in either direction by a Mapping, there may still be instances where specific coordinate values cannot be handled. For example, the Mapping may be mathematically intractable (\emph{e.g.}\ singular) in certain places, or it may map a subset of one space on to another, so that some points in one space are not represented in the other. Sky projections often show this behaviour, since it is quite common to project only half of the celestial sphere on to two dimensions, omitting points on the opposite side of the sky. There are many other examples. f- c+ To indicate when coordinates cannot be transformed, for whatever reason, AST substitutes a special output coordinate value given by the macro AST\_\_BAD (as defined in the ``ast.h'' header file). Before making use of coordinates generated by any of the AST transformation functions, therefore, you may need to check for the presence of this value. c- f+ To indicate when coordinates cannot be transformed, for whatever reason, AST substitutes a special output coordinate value given by the parameter constant AST\_\_BAD (as defined in the AST\_PAR include file). Before making use of coordinates generated by any of the AST transformation routines, therefore, you may need to check for the presence of this value. f- c+ Because coordinates with the value AST\_\_BAD can be generated in this way, all other AST functions are also capable of recognising this value and handling it appropriately. The coordinate transformation functions do this by propagating any missing input coordinate information through to their output. This means that if you supply coordinates with the value AST\_\_BAD, the returned coordinates are also likely to contain this value. Here, for example, is what happens if you use a ZoomMap (with Zoom factor 5) to transform such a set of coordinates: c- f+ Because coordinates with the value AST\_\_BAD can be generated in this way, all other AST routines are also capable of recognising this value and handling it appropriately. The coordinate transformation routines do this by propagating any missing input coordinate information through to their output. This means that if you supply coordinates with the value AST\_\_BAD, the returned coordinates are also likely to contain this value. Here, for example, is what happens if you use a ZoomMap (with Zoom factor 5) to transform such a set of coordinates: f- \small \begin{terminalv} (0, 0) --> (0, 0) (, 2) --> (, 10) (2, 4) --> (10, 20) (3, 6) --> (15, 30) (4, ) --> (20, ) (5, 10) --> (25, 50) (, ) --> (, ) (7, 14) --> (35, 70) (8, 16) --> (40, 80) (9, 18) --> (45, 90) \end{terminalv} \normalsize The AST\_\_BAD value is represented by the string ``$<$bad$>$''. This is a case of ``garbage in, garbage out'' but at least it's consistent garbage that you can recognise! Note how the presence of the AST\_\_BAD value in one input dimension does not necessarily result in the loss of information for all output dimensions. Sometimes, such loss will be unavoidable, but in general an attempt is made to preserve information as far as possible. The exact behaviour will depend on the Mapping involved. \subsection{\label{ss:unitmapexample}Example---the UnitMap} The UnitMap is the simplest of Mappings. It is a null Mapping. Its purpose is simply to copy coordinate values, unaltered, from its input to its output and \emph{vice versa}. A UnitMap has no additional attributes beyond those of a basic Mapping. Its Nin and Nout attributes are always equal and are specified by the first argument supplied to its constructor. For example: c+ \small \begin{terminalv} AstUnitMap *unitmap; ... unitmap = astUnitMap( 2, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER UNITMAP ... UNITMAP = AST_UNITMAP( 2, ' ', STATUS ) \end{terminalv} \normalsize f- will create a UnitMap that copies 2-dimensional coordinates. Inverting a UnitMap has no effect beyond changing the value of its Invert attribute. c+ The main use of a UnitMap is to allow a Mapping to be supplied when one is required (as an argument to a function, for example) but you wish it to leave coordinate values unchanged. c- f+ The main use of a UnitMap is to allow a Mapping to be supplied when one is required (as an argument to a routine, for example) but you wish it to leave coordinate values unchanged. f- \subsection{\label{ss:permmapexample}Example---the PermMap} The PermMap is a rather more complicated Mapping than we have met previously. Its purpose is to change the order, or number, of coordinates. It is also able to substitute fixed values for coordinates. To illustrate its action, suppose our input coordinates are denoted by ($x_1,x_2,x_3,x_4$) in a 4-dimensional space and suppose our output coordinates are to be ($x_4,x_1,x_2,x_3$). Our PermMap, therefore, should rotate the coordinate values by one position. c+ To create such a PermMap, we first set up two integer arrays. One of these, ``outperm'', controls the selection of input coordinates for use in the output and the other, ``inperm'', controls selection of output coordinates for use in the input: c- f+ To create such a PermMap, we first set up two integer arrays. One of these, OUTPERM, controls the selection of input coordinates for use in the output and the other, INPERM, controls selection of output coordinates for use in the input: f- c+ \small \begin{terminalv} int outperm[ 4 ] = { 4, 1, 2, 3 }; int inperm[ 4 ] = { 2, 3, 4, 1 }; \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER OUTPERM( 4 ), INPERM( 4 ) DATA OUTPERM / 4, 1, 2, 3 / DATA INPERM / 2, 3, 4, 1 / \end{terminalv} \normalsize f- Note that the numbers we store in these arrays are the indices of the coordinates that we want to select. We have chosen these so that the forward and inverse transformations will perform complementary permutations on the coordinates. The PermMap is then created by passing these arrays to its constructor, as follows: c+ \small \begin{terminalv} AstPermMap *permmap; ... permmap = astPermMap( 4, inperm, 4, outperm, NULL, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER PERMMAP DOUBLE PRECISION DUMMY( 1 ) ... PERMMAP = AST_PERMMAP( 4, INPERM, 4, OUTPERM, DUMMY, ' ', STATUS ) \end{terminalv} \normalsize f- f+ (the fifth argument is not being used, so a dummy array has been supplied). f- Note that we specify the number of input and output coordinates separately, but set both to 4 in this example. The resulting PermMap would have the following effect when used to transform coordinates: \begin{terminalv} Forward: (1, 2, 3, 4) --> (4, 1, 2, 3) (2, 4, 6, 8) --> (8, 2, 4, 6) (3, 6, 9, 12) --> (12, 3, 6, 9) (4, 8, 12, 16) --> (16, 4, 8, 12) (5, 10, 15, 20) --> (20, 5, 10, 15) Inverse: (4, 1, 2, 3) --> (1, 2, 3, 4) (8, 2, 4, 6) --> (2, 4, 6, 8) (12, 3, 6, 9) --> (3, 6, 9, 12) (16, 4, 8, 12) --> (4, 8, 12, 16) (20, 5, 10, 15) --> (5, 10, 15, 20) \end{terminalv} c+ If the number of input and output coordinates are unequal so, also, will be the size of the ``outperm'' and ``inperm'' arrays. This means, however, that we cannot fill them with coordinate indices so that they perform complementary permutations, because one transformation will lose information (discard a coordinate) that the other cannot recover. To give an example, consider the following: c- f+ If the number of input and output coordinates are unequal so, also, will be the size of the OUTPERM and INPERM arrays. This means, however, that we cannot fill them with coordinate indices so that they perform complementary permutations, because one transformation will lose information (discard a coordinate) that the other cannot recover. To give an example, consider the following: f- c+ \small \begin{terminalv} int outperm[ 3 ] = { 4, 3, 2 }; int inperm[ 4 ] = { -1, 3, 2, 1 }; double con[ 1 ] = { 99.004 }; \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER OUTPERM( 3 ), INPERM( 4 ) DOUBLE PRECISION CONST( 1 ) DATA OUTPERM / 4, 3, 2 / DATA INPERM / -1, 3, 2, 1 / DATA CONST / 99.004D0 / \end{terminalv} \normalsize f- c+ In this case, the forward transformation will change ($x_1,x_2,x_3,x_4$) into ($x_4,x_3,x_2$) and will discard $x_1$. The inverse transformation restores the original coordinate order, but has no value to assign to the first coordinate. In this case, the number entered in the ``inperm'' array is $-$1. c- f+ In this case, the forward transformation will change ($x_1,x_2,x_3,x_4$) into ($x_4,x_3,x_2$) and will discard $x_1$. The inverse transformation restores the original coordinate order, but has no value to assign to the first coordinate. In this case, the number entered in the INPERM array is $-$1. f- c+ This negative value indicates that the coordinate value should be obtained by addressing the first element of the ``con'' array (\emph{i.e.}\ element zero). This array, ignored in the previous example, may then be used to supply a value for the missing coordinate. c- f+ This negative value indicates that the coordinate value should be obtained by addressing the CONST array using an index of 1 (the absolute value). This array, ignored in the previous example, may then be used to supply a value for the missing coordinate. f- The constructor function: c+ \small \begin{terminalv} permmap = astPermMap( 4, inperm, 3, outperm, con, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} PERMMAP = AST_PERMMAP( 4, INPERM, 3, OUTPERM, CONST, ' ', STATUS ) \end{terminalv} \normalsize f- will then create a PermMap with the following effect when used to transform coordinates: \begin{terminalv} Forward: (1, 2, 3, 4) --> (4, 3, 2) (2, 4, 6, 8) --> (8, 6, 4) (3, 6, 9, 12) --> (12, 9, 6) (4, 8, 12, 16) --> (16, 12, 8) (5, 10, 15, 20) --> (20, 15, 10) Inverse: (4, 3, 2) --> (99.004, 2, 3, 4) (8, 6, 4) --> (99.004, 4, 6, 8) (12, 9, 6) --> (99.004, 6, 9, 12) (16, 12, 8) --> (99.004, 8, 12, 16) (20, 15, 10) --> (99.004, 10, 15, 20) \end{terminalv} c+ The ``con'' array may contain more than one value if necessary and may be addressed by both the ``inperm'' and ``outperm'' arrays using coordinate indices $-$1, $-$2, $-$3,~\emph{etc.}\ to refer to the first, second, third,~\emph{etc.}\ elements. c- f+ The CONST array may contain more than one value if necessary and may be addressed by both the INPERM and OUTPERM arrays using coordinate indices $-$1, $-$2, $-$3,~\emph{etc.}\ to refer to the first, second, third,~\emph{etc.}\ elements. f- c+ If there is no suitable replacement value that can be supplied \emph{via} the ``con'' array, a value of zero may be entered into the ``outperm'' and/or ``inperm'' arrays. This causes the value AST\_\_BAD to be used for the affected coordinate (as defined in the ``ast.h'' header file), thus indicating a missing coordinate value (\secref{ss:badcoordinates}). c- f+ If there is no suitable replacement value that can be supplied \emph{via} the CONST array, a value of zero may be entered into the OUTPERM and/or INPERM arrays. This causes the value AST\_\_BAD to be used for the affected coordinate (as defined in the AST\_PAR include file), thus indicating a missing coordinate value (\secref{ss:badcoordinates}). f- The principle use for a PermMap lies in matching a coordinate system to a data array where there is a choice of storage order for the data. PermMaps are also useful for discarding unwanted coordinates so as to reduce the number of dimensions, such as when selecting a ``slice'' from a multi-dimensional array. \cleardoublepage \section{\label{ss:cmpmaps}Compound Mappings (CmpMaps)} We now turn to a rather special form of Mapping, the CmpMap. The Mappings we have considered so far have been atomic, in the sense that they perform pre-defined elementary transformations. A CmpMap, however, is a compound Mapping. In essence, it is a framework for containing other Mappings and its purpose is to allow those Mappings to work together in various combinations while appearing as a single Object. A CmpMap's behaviour is therefore not pre-defined, but is determined by the other Mappings it contains. \subsection{\label{ss:seriescmpmap}Combining Mappings in Series} Consider a simple example based on two 2-dimensional coordinate systems. Suppose that to convert from one to the other we must swap the coordinate order and multiply both coordinates by 5, so that the coordinates ($x_1,x_2$) transform into ($5x_2,5x_1$). This can be done in two stages: \begin{enumerate} \item Apply a PermMap (\secref{ss:permmapexample}) to swap the coordinate order. \item Apply a ZoomMap (\secref{ss:transforming}) to multiply both coordinate values by the constant 5. \end{enumerate} The PermMap and ZoomMap are then said to operate \emph{in series}, because they are applied sequentially (\emph{c.f.}\ Figure~\ref{fig:seriescmpmap}). We can create a CmpMap that applies these Mappings in series as follows: c+ \small \begin{terminalv} #include "ast.h" AstCmpMap *cmpmap; AstPermMap *permmap; AstZoomMap *zoommap; ... /* Create the individual Mappings. */ { int inperm[ 2 ] = { 2, 1 }; int outperm[ 2 ] = { 2, 1 }; permmap = astPermMap( 2, inperm, 2, outperm, NULL, "" ); } zoommap = astZoomMap( 2, 5.0, "" ) /* Combine them in series. */ cmpmap = astCmpMap( permmap, zoommap, 1, "" ); /* Annul the individual Mapping pointers. */ permmap = astAnnul( permmap ); zoommap = astAnnul( zoommap ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INCLUDE 'AST_PAR' INTEGER CMPMAP, PERMMAP, STATUS, ZOOMMAP INTEGER INPERM( 2 ), OUTPERM( 2 ), CONST( 1 ) DATA INPERM / 1, 2 / DATA OUTPERM / 1, 2 / STATUS = 0 ... * Create the individual Mappings. PERMMAP = AST_PERMMAP( 2, INPERM, 2, OUTPERM, CONST, ' ', STATUS ) ZOOMMAP = AST_ZOOMMAP( 2, 5.0D0, ' ', STATUS ) * Combine them in series. CMPMAP = AST_CMPMAP( PERMMAP, ZOOMMAP, .TRUE., ' ', STATUS ) * Annul the individual Mapping pointers. CALL AST_ANNUL( PERMMAP, STATUS ) CALL AST_ANNUL( ZOOMMAP, STATUS ) \end{terminalv} \normalsize f- c+ Here, the third argument (1) of the constructor function astCmpMap indicates ``in series''. c- f+ Here, the third argument (.TRUE.) of the constructor function AST\_CMPMAP indicates ``in series''. f- When used to transform coordinates in the forward direction, the resulting CmpMap will apply the first component Mapping (the PermMap) and then the second one (the ZoomMap). When transforming in the inverse direction, it will apply the second one (in the inverse direction) and then the first one (also in the inverse direction). In general, although not in this particular example, the order in which the two component Mappings are supplied is significant. Clearly, also, the Nout attribute (number of output coordinates) for the first Mapping must equal the Nin attribute (number of input coordinates) for the second one. \subsection{Combining Mappings in Parallel} Connecting two Mappings in series (\secref{ss:seriescmpmap}) is not the only way of combining them. The alternative, \emph{in parallel}, involves applying the two Mappings at once but on different subsets of the coordinate values. Consider, for example, a set of 3-dimensional coordinates and suppose we wish to transform them by swapping the first two coordinate values and multiplying the final one by 5, so that ($x_1,x_2,x_3$) transforms into ($x_2,x_1,5x_3$). Again, we can perform each of these steps individually using Mappings similar to the PermMap and ZoomMap used earlier (\secref{ss:seriescmpmap}). In this case, however, the ZoomMap is 1-dimensional and the individual Mappings are applied in parallel (\emph{c.f.}\ Figure~\ref{fig:parallelcmpmap}). Creating a CmpMap for this purpose is also very simple: c+ \small \begin{terminalv} cmpmap = astCmpMap( permmap, zoommap, 0, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CMPMAP = AST_CMPMAP( PERMMAP, ZOOMMAP, .FALSE., ' ', STATUS ) \end{terminalv} \normalsize f- c+ The only difference is that the third argument of astCmpMap is now zero, meaning ``in parallel''. c- f+ The only difference is that the third argument of AST\_CMPMAP is now .FALSE., meaning ``in parallel''. f- As before, the order in which the two component Mappings are supplied is significant. The first one acts on the lower-numbered input coordinate values (however many it needs) and produces the lower-numbered output coordinates, while the second Mapping acts on the higher-numbered input coordinates (however many remain) and generates the remaining higher-numbered output coordinates. When the CmpMap transforms coordinates in the inverse direction, both component Mappings are applied to the same coordinates, but in the inverse direction. Note that the Nin and Nout attributes of the component Mappings (\emph{i.e.}\ the numbers of input and output coordinates) will sum to give the Nin and Nout attributes of the overall CmpMap. \subsection{\label{ss:cmpmapcomponents}The Component Mappings} c+ A CmpMap does not store copies of its component Mappings, but simply holds pointers to them. In the example above (\secref{ss:seriescmpmap}), we were free to annul the individual Mapping pointers after creating the CmpMap because the pointers held internally by the CmpMap increased the reference count (RefCount attribute) of each component Mapping by one. The individual components are therefore not deleted by astAnnul, but retained until the CmpMap itself is deleted and annuls the pointers it holds. Consistent use of astAnnul (\secref{ss:annullingpointers}) and/or pointer contexts (\secref{ss:contexts}) will therefore ensure that all Objects are deleted at the appropriate time. c- f+ A CmpMap does not store copies of its component Mappings, but simply holds pointers to them. In th example above (\secref{ss:seriescmpmap}), we were free to annul the individual Mapping pointers after creating the CmpMap because the pointers held internally by the CmpMap increased the reference count (RefCount attribute) of each component Mapping by one. The individual components are therefore not deleted by AST\_ANNUL, but retained until the CmpMap itself is deleted and annuls the pointers it holds. Consistent use of AST\_ANNUL (\secref{ss:annullingpointers}) and/or pointer contexts (\secref{ss:contexts}) will therefore ensure that all Objects are deleted at the appropriate time. f- Note that access to a CmpMap's component Mappings is not generally available unless pointers to them are retained when the CmpMap is created. If such pointers are retained, then subsequent modifications to the individual components can be used to indirectly modify the behaviour of the overall CmpMap. There is an important exception to this, however, because a CmpMap retains a copy of the initial Invert flag settings of each of its components and uses these in order to ignore any subsequent external changes. This means that you may invert either component Mapping before inserting it into a CmpMap and need not worry if you un-invert it again later. The CmpMap's behaviour will not be affected by the later action. \subsection{\label{ss:complexcmpmap}Creating More Complex Mappings} c+ Because a CmpMap is itself a Mapping, any existing CmpMap can substitute (\secref{ss:objecthierarchy}) as a component Mapping when constructing a new CmpMap using astCmpMap. This has the effect of nesting one CmpMap inside another and opens up many new possibilities. For example, combining three Mappings in series can be accomplished as follows: c- f+ Because a CmpMap is itself a Mapping, any existing CmpMap can substitute (\secref{ss:objecthierarchy}) as a component Mapping when constructing a new CmpMap using AST\_CMPMAP. This has the effect of nesting one CmpMap inside another and opens up many new possibilities. For example, combining three Mappings in series can be accomplished as follows: f- c+ \small \begin{terminalv} AstMapping *map1, *map2, *map3; ... cmpmap = astCmpMap( map1, astCmpMap( map2, map3, 1, "" ), 1, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER MAP1, MAP2, MAP3 ... CMPMAP = AST_CMPMAP( MAP1, AST_CMPMAP( MAP2, MAP3, .TRUE., ' ', STATUS ), : .TRUE., ' ', STATUS ) \end{terminalv} \normalsize f- The way in which the individual component Mappings are grouped within the nested CmpMaps is not usually important. A similar technique can be used to combine multiple Mappings in parallel and, of course, mixed series and parallel combinations are also possible (Figure~\ref{fig:complexcmpmap}). There is no built-in limit to how many CmpMaps may be nested in this way, so this mechanism provides an indefinitely extensible method of building complex Mappings out of the elemental building blocks provided by AST. In practice, you might not need to construct such complex CmpMaps yourself very frequently, but they will often be returned by AST routines. Nested CmpMaps underlie the library's entire ability to represent a wide range of different coordinate transformations. \subsection{\label{ss:cmpmapexample}Example---Transforming Between Two Calibrated Images} Consider, as a practical example of CmpMaps, two images of the sky. Suppose that for each image we have a Mapping which converts from pixel coordinates to a standard celestial coordinate system, say FK5~(J2000.0). If we wish to inter-compare these images, we can do so by using this celestial coordinate system to align them. That is, we first convert from pixel coordinates in the first image into FK5 coordinates and we then convert from FK5 coordinates into pixel coordinates in the second image. c+ If ``mapa'' and ``mapb'' are pointers to our two original Mappings, we could form a CmpMap which transforms directly between the pixel coordinates of the first and second images by combining these Mappings, as follows: c- f+ If MAPA and MAPB are pointers to our two original Mappings, we could form a CmpMap which transforms directly between the pixel coordinates of the first and second images by combining these Mappings, as follows: f- c+ \small \begin{terminalv} AstCmpMap *alignmap; AstMapping *mapa, *mapb; ... astInvert( mapb ); alignmap = astCmpMap( mapa, mapb, 1, "" ); astInvert( mapb ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER ALIGNMAP, MAPA, MAPB ... CALL AST_INVERT( MAPB, STATUS ) ALIGNMAP = AST_CMPMAP( MAPA, MAPB, .TRUE., ' ', STATUS ) CALL AST_INVERT( MAPB, STATUS ) \end{terminalv} \normalsize f- c+ Here, we have used astInvert (\secref{ss:invertingmappings}) to invert ``mapb'' before inserting it into the CmpMap because, as supplied, it converted in the wrong direction. Afterwards, we invert it again to return it to its original state. The CmpMap, however, will ignore this subsequent change (\secref{ss:cmpmapcomponents}). c- f+ Here, we have used AST\_INVERT (\secref{ss:invertingmappings}) to invert MAPB before inserting it into the CmpMap because, as supplied, it converted in the wrong direction. Afterwards, we invert it again to return it to its original state. The CmpMap, however, will ignore this subsequent change (\secref{ss:cmpmapcomponents}). f- The forward transformation of the resulting CmpMap will now transform from pixel coordinates in the first image to pixel coordinates in the second image, while its inverse transformation will convert in the opposite direction. \subsection{\label{ss:overcomplexcmpmaps}Over-Complex Compound Mappings} While a CmpMap provides a very flexible way of constructing arbitrarily complex Mappings (\secref{ss:complexcmpmap}), it unfortunately also provides an opportunity for representing simple Mappings in complex ways. Sometimes, unnecessary complexity can be difficult to avoid but can obscure important simplifications. Consider the example above (\secref{ss:cmpmapexample}), in which we inter-related two images of the sky \emph{via} a CmpMap. If the two images turned out to be simply offset from each other by a shift along each pixel axis, then this approach would align them correctly, but it would be inefficient. This is because it would introduce unnecessary and expensive transformations to and from an intermediate celestial coordinate system, whereas a simple shift of pixel origin would suffice. Recognising that a simpler and more efficient solution exists obviously requires a little more than simply joining two Mappings end-to-end. We must also determine whether the resulting CmpMap is more complex than it needs to be, \emph{i.e.}\ contains redundant information. If it is, we then need a way to simplify it. The problem is not always just one of efficiency, however. Sometimes we may also need to know something about the actual form a Mapping takes---\emph{i.e.}\ the nature of the operations it performs. Unnecessary complexity can obscure this, but such complexity can easily accumulate during normal data processing. For example, a Mapping that transforms pixel coordinates into positions on the sky might be repeatedly modified as changes are made to the shape and size of the image. Typically, on each occasion, another Mapping will be concatenated to reflect what has happened to the image. This could soon make it difficult to discern the overall nature of the transformation from the complex CmpMap that accumulates. If only shifts of origin were involved on each occasion, however, they could be combined into a single shift which could be represented much more simply. Suppose we now wanted to represent our image's celestial coordinate calibration using FITS conventions (\secref{ss:foreignfits}). This requires AST to determine whether the Mapping which relates pixel coordinate to sky positions conforms to the FITS model (for example, whether it is equivalent to applying a single set of shifts and scale factors followed by a map projection). Clearly, there is an important use here for some means of simplifying the internal structure of a CmpMap. \subsection{\label{ss:simplifyingcmpmaps}Simplifying Compound Mappings} c+ The ability to simplify compound Mappings is provided by the astSimplify function. This function encapsulates a number of heuristics for converting Mappings, or combinations of Mappings within a CmpMap, into simpler, equivalent ones. When applied to a CmpMap, astSimplify tries to reduce the number of individual Mappings within it by merging neighbouring component Mappings together. It will do this with both series and parallel combinations of Mappings, or both, and will handle CmpMaps nested to any depth (\secref{ss:complexcmpmap}). c- f+ The ability to simplify compound Mappings is provided by the AST\_SIMPLIFY function. This function encapsulates a number of heuristics for converting Mappings, or combinations of Mappings within a CmpMap, into simpler, equivalent ones. When applied to a CmpMap, AST\_SIMPLIFY tries to reduce the number of individual Mappings within it by merging neighbouring component Mappings together. It will do this with both series and parallel combinations of Mappings, or both, and will handle CmpMaps nested to any depth (\secref{ss:complexcmpmap}). f- c+ To illustrate how astSimplify works, consider the combination of Mappings shown in Figure~\ref{fig:simplifyexample}. c- f+ To illustrate how AST\_SIMPLIFY works, consider the combination of Mappings shown in Figure~\ref{fig:simplifyexample}. f- \begin{figure} \begin{center} c+ \includegraphics[width=0.7\textwidth]{sun211_figures/simpexamp} c- f+ \includegraphics[width=0.7\textwidth]{sun210_figures/simpexamp} f- \caption[An over-complex compound Mapping.]{An over-complex compound Mapping, consisting of PermMaps, ZoomMaps and a UnitMap, which can be simplified to become a single UnitMap. The enclosing nested CmpMaps have been omitted for clarity.} \label{fig:simplifyexample} \end{center} \end{figure} If this were contained in a CmpMap, it could be simplified as follows: c+ \small \begin{terminalv} AstMapping *simpler; ... simpler = astSimplify( cmpmap ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER SIMPLER ... SIMPLER = AST_SIMPLIFY( CMPMAP, STATUS ); \end{terminalv} \normalsize f- c+ In this case, the result would be a simple 3-dimensional UnitMap (the identity Mapping). To reach this conclusion, astSimplify will have made a number of deductions, roughly as follows: c- f+ In this case, the result would be a simple 3-dimensional UnitMap (the identity Mapping). To reach this conclusion, AST\_SIMPLIFY will have made a number of deductions, roughly as follows: f- \begin{enumerate} \item The two 2-dimensional ZoomMaps in series are equivalent to a single ZoomMap with a combined Zoom factor of unity. This, in turn, is equivalent to a 2-dimensional UnitMap. \item This UnitMap in parallel with the other 1-dimensional UnitMap is equivalent to a single 3-dimensional UnitMap. This UnitMap, sandwiched between any other pair of Mappings, can then be eliminated. \item The remaining two PermMaps in series are equivalent to a single 3-dimensional PermMap. When these are combined, the resulting PermMap is found to be equivalent to a 3-dimensional UnitMap. \end{enumerate} c+ This example is a little contrived, but illustrates how astSimplify can deal with even quite complicated compound Mappings through a series of incremental simplifications. Where possible, this will result in either a simpler compound Mapping or, if feasible, an atomic (non-compound) Mapping, as here. If no simplification is possible, astSimplify will just return a pointer to the original Mapping. c- f+ This example is a little contrived, but illustrates how AST\_SIMPLIFY can deal with even quite complicated compound Mappings through a series of incremental simplifications. Where possible, this will result in either a simpler compound Mapping or, if feasible, an atomic (non-compound) Mapping, as here. If no simplification is possible, AST\_SIMPLIFY will just return a pointer to the original Mapping. f- c+ Although astSimplify cannot identify every simplification that is theoretically possible, sufficient rules are included to deal with the most common and important cases. c- f+ Although AST\_SIMPLIFY cannot identify every simplification that is theoretically possible, sufficient rules are included to deal with the most common and important cases. f- \cleardoublepage \section{\label{ss:frames}Representing Coordinate Systems (Frames)} An AST Frame is an Object that is used to represent a coordinate system. Contrast this with a Mapping (\secref{ss:mappings}), which is used to describe how to convert between coordinate systems. The two concepts are complementary and we will see how they work together in \secref{ss:framesets}. In this section we will discuss only basic Frames, which represent Cartesian coordinate systems. More specialised types of Frame (\emph{e.g.}\ the SkyFrame, which represents celestial coordinate systems, and the SpecFrame, which represents spectral coordinate systems) are covered later (\secref{ss:skyframes} and \secref{ss:specframes}) and, naturally, inherit the properties and behaviour of the simple Frames discussed here. \subsection{The Frame Model} The best way to think about a Frame is like the frame that you would plot around a graph. In two dimensions, you would have an ``$x$'' and a ``$y$'' axis, a title on the graph and labels on the axes, together with an indication of the physical units being plotted. The values marked along each axis would be formatted in a human-readable way. The frame around a graph therefore defines a coordinate space within which you can locate points, draw lines, calculate distances, \emph{etc.} An AST Frame works in much the same way, embodying all of these concepts and a few more. It also allows any number of axes, which means that a Frame can represent coordinate systems with any number of dimensions. You specify how many when you create it. Remember that the basic Frame we are considering here is completely general. It knows nothing of celestial coordinates, for example, and all its axes are equivalent. It can be adapted to describe any general purpose Cartesian coordinate system by setting its attributes, such as its Title and axis Labels, \emph{etc.}\ to appropriate values. \subsection{\label{ss:creatingframes}Creating a Frame} Creating a Frame is straightforward and follows the usual pattern: c+ \small \begin{terminalv} #include "ast.h" astFrame *frame; ... frame = astFrame( 2, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INCLUDE 'AST_PAR' INTEGER FRAME, STATUS STATUS = 0 ... FRAME = AST_FRAME( 2, ' ', STATUS ) \end{terminalv} \normalsize f- c+ The first argument of the astFrame constructor function specifies the number of axes which the Frame should have. c- f+ The first argument of the AST\_FRAME constructor function specifies the number of axes which the Frame should have. f- \subsection{\label{ss:frameasmapping}Using a Frame as a Mapping} We should briefly point out that the Frame we created above (\secref{ss:creatingframes}) is also a Mapping (\secref{ss:mappingclass}) and therefore inherits the properties and behaviour common to other Mappings. c+ One way to see this is to set the Frame's Report attribute (inherited from the Mapping class) to a non-zero value and pass the Frame pointer to a coordinate transformation function, such as astTran2. c- f+ One way to see this is to set the Frame's Report attribute (inherited from the Mapping class) to a non-zero value and pass the Frame pointer to a coordinate transformation routine, such as AST\_TRAN2. f- c+ \small \begin{terminalv} double xin[ 5 ] = { 0.0, 1.0, 2.0, 3.0, 4.0, 5.0 }; double yin[ 5 ] = { 0.0, 2.0, 4.0, 6.0, 8.0, 10.0 }; double xout[ 5 ]; double yout[ 5 ]; ... astSet( frame, "Report=1" ); astTran2( frame, 5, xin, yin, 1, xout, yout ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} DOUBLE PRECISION XIN( 5 ), YIN( 5 ), XOUT( 5 ), YOUT( 5 ) DATA XIN / 0D0, 1D0, 2D0, 3D0, 4D0, 5D0 / DATA YIN / 0D0, 2D0, 4D0, 6D0, 8D0, 10D0 / CALL AST_SET( FRAME, 'Report=1', STATUS ) CALL AST_TRAN2( FRAME, 5, XIN, YIN, .TRUE., XOUT, YOUT, STATUS ) \end{terminalv} \normalsize f- The resulting output might then look like this: \begin{terminalv} (1, 2) --> (1, 2) (2, 4) --> (2, 4) (3, 6) --> (3, 6) (4, 8) --> (4, 8) (5, 10) --> (5, 10) \end{terminalv} This is not very exciting because a Frame implements an identity transformation just like a UnitMap (\secref{ss:unitmapexample}). However, it illustrates that a Frame can be used as a Mapping and that its Nin and Nout attributes are both equal to the number of Frame axes. When we consider more specialised Frames (\emph{e.g.}~\secref{ss:framesets}), we will see that using them as Mappings can be very useful indeed. \subsection{\label{ss:frameaxisattributes}Frame Axis Attributes} Frames have a number of attributes which can take multiple values, one for each axis. These separate values are identified by appending the axis number in parentheses to the attribute name. For example, the Label(1) attribute is a character string containing the label which appears on the first axis. Axis attributes are accessed in the same way as all other attributes (\secref{ss:gettingattributes}, \secref{ss:settingattributes} and \secref{ss:defaultingattributes}). For example, the Label on the second axis might be obtained as follows: c+ \small \begin{terminalv} const char *label; ... label = astGetC( frame, "Label(2)" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CHARACTER * ( 70 ) LABEL ... LABEL = AST_GETC( FRAME, 'Label(2)', STATUS ) \end{terminalv} \normalsize f- c+ Other attribute access functions (astSetX, astTest and astClear) may also be applied to axis attributes in the same way. c- f+ Other attribute access routines (AST\_SETx, AST\_TEST and AST\_CLEAR) may also be applied to axis attributes in the same way. f- If the axis number is stored in a program variable, then its value must be formatted to generate a suitable attribute name before using this to access the attribute itself. For example, the following will print out the Label value for each axis of a Frame: c+ \small \begin{terminalv} #include char name[ 18 ]; int iaxis, naxes; ... naxes = astGetI( frame, "Naxes" ); for ( iaxis = 1; iaxis <= naxes; iaxis++ ) { (void) sprintf( name, "Label(%d)", iaxis ); label = astGetC( frame, name ); (void) printf( "Label %2d: %s\n", iaxis, label ); } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CHARACTER * ( 10 ) AXIS INTEGER IAXIS ... DO 1 IAXIS = 1, AST_GETI( FRAME, 'Naxes', STATUS ) WRITE ( AXIS, '( I10 )' ) IAXIS LABEL = AST_GETC( FRAME, 'Label(' // AXIS // ')', STATUS ) WRITE ( *, 199 ) IAXIS, LABEL 199 FORMAT ( 'Label ', I2, ': ', A ) 1 CONTINUE \end{terminalv} \normalsize f- Note the use of the Naxes attribute to determine the number of Frame axes. The output from this might look like the following: \begin{terminalv} Label 1: Axis 1 Label 2: Axis 2 \end{terminalv} In this case, the Frame's default axis Labels have been revealed as rather un-exciting. Normally, you would set much more useful values, typically when you create the Frame---perhaps something like: c+ \small \begin{terminalv} frame = astFrame( 2, "Label(1)=Offset from centre of field," "Unit(1) =mm," "Label(2)=Transmission coefficient," "Unit(2) =%" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} FRAME = AST_FRAME( 2, 'Label(1)=Offset from centre of field,' // 'Unit(1) =mm,' // 'Label(2)=Transmission coefficient,' // 'Unit(2) =%', STATUS ) \end{terminalv} \normalsize f- Here, we have also set the (character string) Unit attribute for each axis to describe the physical units represented on that axis. All the attribute assignments have been combined into a single string, separated by commas. \subsection{\label{ss:frameattributes}Frame Attributes} We will now briefly outline the various attributes associated with a Frame (this is, of course, in addition to those inherited from the Mapping class). We will not delve too deeply into the details of each attribute, for which you should consult the appropriate description in \appref{ss:attributedescriptions}. Instead, we aim simply to sketch the range of facilities available: \begin{quote} \begin{description} \item[Naxes]\mbox{}\\ A read-only integer giving the number of Frame axes. \item[Title]\mbox{}\\ A string describing the coordinate system which the Frame represents. \item[Label(axis)]\mbox{}\\ A label string for each axis. \item[Unit(axis)]\mbox{}\\ A string describing the physical units on each axis. You can choose whether to make this attribute ``active'' or ``passive'' (using c+ astSetActiveUnit c- f+ AST\_SETACTIVEUNIT f- ). If active, its value will be taken into account when finding the Mapping between two Frames (\emph{e.g.} a scaling of 0.001 would be used to connect two axis with units of ``km'' and ``m''). If passive, its value is ignored. Its use is described in more detail in \secref{ss:frameunits}. \item[Symbol(axis)]\mbox{}\\ A string containing a ``short form'' symbol (\emph{e.g.}\ like ``X'' or ``Y'') used to represent the quantity plotted on each axis. \item[Digits/Digits(axis)]\mbox{}\\ The preferred number of digits of precision to be used when formatting values for display on each axis. \item[Format(axis)]\mbox{}\\ A string containing a \emph{format specifier} which determines exactly how values should be formatted for display on each axis (\secref{ss:formattingaxisvalues}). If this attribute is un-set, the formatting is based on the Digits value, otherwise the Format string over-rides the Digits value. \item[Direction(axis)]\mbox{}\\ A boolean (integer) value which indicates in which direction each axis should be plotted. If it is non-zero (the default), the axis should be plotted in the conventional direction---\emph{i.e.}\ increasing to the right for the abscissa and increasing upwards for the ordinate. If it is zero, the axis should be plotted in reverse. This attribute is provided as a hint only and programs are free to ignore it if they wish. \item[Domain]\mbox{}\\ A character string which identifies the \emph{physical domain} to which the Frame's coordinate system applies. The primary purpose of this attribute is to prevent unwanted conversions from occurring between coordinate systems which are not related. Its use is described in more detail in \secref{ss:framedomains}. \item[System]\mbox{}\\ A character string which identifies the specific coordinate system used to describe positions within the physical domain represented by the Frame. For a simple Frame, this attribute currently has a fixed value of ``Cartesian'', but could in principle be extended to include options such as ``Polar'', ``Cylindrical'', \emph{etc}. More specialised Frames such as the SkyFrame, TimeFrame and SpecFrame, re-define the allowed values to be appropriate to the domain which they describe. For instance, the SkyFrame allows values such as ``FK4'' and ``Galactic'', and the SpecFrame allows values such as ``frequency'' and ``wavelength''. \item[Epoch]\mbox{}\\ This value is used to qualify a coordinate system by giving the moment in time when the coordinates are correct. Usually, this will be the date of observation. The Epoch value is important in cases where coordinates systems move with respect to each other over time. An example of two such coordinate systems are the FK4 and FK5 celestial coordinate systems. \item[ObsLon]\mbox{}\\ Specifies the longitude of the observer (assumed to be on the surface of the earth). The basic Frame class does not use this value, but specialised sub-classes may. For instance, the SpecFrame class uses it to calculate the relative velocity of the observer and the centre of the earth for use in converting between standards of rest. \item[ObsLat]\mbox{}\\ Specifies the latitude of the observer. Use in conjunction with ObsLon. \end{description} \end{quote} There are also some further Frame attributes, not described above, which are important when Frames are used as templates to search for other Frames. Their use goes beyond the present discussion. %TBW---Add reference here. \subsection{\label{ss:formattingaxisvalues}Formatting Axis Values} c+ The coordinate values associated with each axis of a Frame are stored (\emph{e.g.}\ within your program) as double values. The Frame class therefore provides a function, astFormat, to convert these values into formatted strings for display: c- f+ The coordinate values associated with each axis of a Frame are stored (\emph{e.g.}\ within your program) as double precision values. The Frame class therefore provides a function, AST\_FORMAT, to convert these values into formatted strings for display: f- c+ \small \begin{terminalv} const char *string double value; ... string = astFormat( frame, iaxis, value ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CHARACTER * ( 50 ) STRING DOUBLE PRECISION VALUE ... STRING = AST_FORMAT( FRAME, IAXIS, VALUE, STATUS ) \end{terminalv} \normalsize f- c+ Here, the astFormat function is passed a Frame pointer, the number of an axis (``iaxis'') and a double precision value to format (``value''). It returns a pointer to character string containing the formatted value. c- f+ Here, the AST\_FORMAT character function is passed a Frame pointer, the number of an axis (IAXIS) and a double precision value to format (VALUE). It returns a character string containing the formatted value. f- \label{ss:formattingwithdigits} c+ By default, the formatting applied will be determined by the Frame's Digits attribute and will normally display results with seven digits of precision (corresponding approximately to the C ``float'' data type on many machines). Setting a different Digits value, however, allows you to adjust the precision as necessary to suit the accuracy of the coordinate data you are processing. If finer control is needed, it is also possible to set a Digits value for each individual axis by appending an axis number to the attribute name (\emph{e.g.}\ ``Digits(2)''). If this is done, it over-rides the effect of the Frame's main Digits value for that axis. c- f+ By default, the formatting applied will be determined by the Frame's Digits attribute and will normally display results with seven digits of precision (corresponding approximately to the Fortran REAL data type on many machines). Setting a different Digits value, however, allows you to adjust the precision as necessary to suit the accuracy of the coordinate data you are processing. If finer control is needed, it is also possible to set a Digits value for each individual axis by appending an axis number to the attribute name (\emph{e.g.}\ ``Digits(2)''). If this is done, it over-rides the effect of the Frame's main Digits value for that axis. f- c+ Even finer control is possible by setting the (character string) Format attribute for a Frame axis. The string given should contain a C \emph{format specifier} which explicitly determines how the values on that axis should be formatted. This will over-ride the effects of any Digits value\footnote{The exception to this rule is that if the Format value includes a precision of ``$.*$'', then Digits will be used to determine the actual precision used.}. Any valid ``printf'' format specifier may be used so long as it consumes exactly one double value. c- f+ Even finer control is possible by setting the (character string) Format attribute for a Frame axis. The string given should contain a \emph{format specifier} which explicitly determines how the values on that axis should be formatted. This will over-ride the effects of any Digits value\footnote{The exception to this rule is that if the Format value includes a precision of ``$.*$'', then Digits will be used to determine the actual precision used.}. Unfortunately for Fortran programmers, this must be a C language format specifier,\footnote{This is a consequence of implementing the AST library in C.} so you might find the Digits approach preferable. f- c+ When setting Format values, remember that the ``\%'' which appears in the format specifier may need to be doubled to ``\%\%'' if you are using a function (such as astSet) which interprets ``printf'' format specifiers itself. c- f+ The simplest type of format specifier takes the form ``\%m.nG'', where ``m'' and ``n'' are integers giving the minimum field width in characters and the number of significant digits to display (\emph{e.g.}\ ``\%10.5G''). The ''n'' value may be replaced by an asterisk, in which case the value of the Digits attribute is used to determine the number of significant digits to display. Other formatting options are also possible and if you need to use them you may wish to consult a book on C (see the ``printf'' function), remembering that you want to format a double precision (C double) value. f- c+ It is recommended that you use astFormat whenever you display formatted coordinate values, even although you could format them yourself using ``sprintf''. This is because it puts the Frame in control of formatting. When you start to handle more elaborate Frames (representing, say, celestial coordinates), you will need different formatting methods. This approach delivers them without any change to your software. c- f+ It is recommended that you use AST\_FORMAT whenever you display formatted coordinate values, even although you could format them yourself using a WRITE statement. This is because it puts the Frame in control of formatting. When you start to handle more elaborate Frames (representing, say, celestial coordinates), you will need different formatting methods. This approach delivers them without any change to your software. f- c+ You should also consider regularly using the astNorm function, described below (\secref{ss:normalising}), for any values that will be made visible to the user of your software. c- f+ You should also consider regularly using the AST\_NORM routine, described below (\secref{ss:normalising}), for any values that will be made visible to the user of your software. f- \subsection{\label{ss:normalising}Normalising Frame Coordinates} c+ The function astNorm is provided to cope with the fact that some coordinate systems do not extend indefinitely in all directions. Some may have boundaries, outside which coordinates are meaningless, while others wrap around on themselves, so that after a certain distance you return to the beginning again (coordinate systems based on circles and spheres, for instance). A basic Frame has no such complications, but other more specialised Frames (such as SkyFrames, representing the celestial sphere---\secref{ss:skyframes}) do. c- f+ The routine AST\_NORM is provided to cope with the fact that some coordinate systems do not extend indefinitely in all directions. Some may have boundaries, outside which coordinates are meaningless, while others wrap around on themselves, so that after a certain distance you return to the beginning again (coordinate systems based on circles and spheres, for instance). A basic Frame has no such complications, but other more specialised Frames (such as SkyFrames, representing the celestial sphere---\secref{ss:skyframes}) do. f- c+ The role played by astNorm is to \emph{normalise} any arbitrary set of coordinates by converting them into a set which is ``within bounds'', interpreted according to the particular Frame in question. For example, on the celestial sphere, a right ascension value of 24~hours or more can have a suitable multiple of 24~hours subtracted without affecting its meaning and astNorm would perform this task. Similarly, negative values of right ascension would have a multiple of 24~hours added, so that the result lies in the range zero to 24~hours. The coordinates in question are modified in place by astNorm, as follows: c- f+ The role played by AST\_NORM is to \emph{normalise} any arbitrary set of coordinates by converting them into a set which is ``within bounds'', interpreted according to the particular Frame in question. For example, on the celestial sphere, a right ascension value of 24~hours or more can have a suitable multiple of 24~hours subtracted without affecting its meaning and AST\_NORM would perform this task. Similarly, negative values of right ascension would have a multiple of 24~hours added, so that the result lies in the range zero to 24~hours. The coordinates in question are modified in place by AST\_NORM, as follows: f- c+ \small \begin{terminalv} double point[ 2 ]; ... astNorm( frame, point ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} DOUBLE PRECISION POINT( 2 ) ... CALL AST_NORM( FRAME, POINT, STATUS ) \end{terminalv} \normalsize f- If the coordinates supplied are initially OK, as they would always be with a basic Frame, then they are returned unchanged. c+ Because the main purpose of astNorm is to convert coordinates into the preferred range for human consumption, its use is almost always appropriate immediately before formatting coordinate values for display using astFormat (\secref{ss:formattingaxisvalues}). Even if the Frame in question does not restrict the range of coordinates, so that astNorm does nothing, using it will allow you to process other more specialised Frames, where normalisation is important, without changing your software. c- f+ Because the main purpose of AST\_NORM is to convert coordinates into the preferred range for human consumption, its use is almost always appropriate immediately before formatting coordinate values for display using AST\_FORMAT (\secref{ss:formattingaxisvalues}). Even if the Frame in question does not restrict the range of coordinates, so that AST\_NORM does nothing, using it will allow you to process other more specialised Frames, where normalisation is important, without changing your software. f- \subsection{\label{ss:unformattingaxisvalues}Reading Formatted Axis Values} c+ The process of converting a formatted coordinate value for a Frame axis, such as might be produced by astFormat (\secref{ss:formattingaxisvalues}), back into a numerical (double) value ready for processing is performed by astUnformat. However, although this process is essentially the inverse of that performed by astFormat, there are a number of additional difficulties that must be addressed in practice. c- f+ The process of converting a formatted coordinate value for a Frame axis, such as might be produced by AST\_FORMAT (\secref{ss:formattingaxisvalues}), back into a numerical (double precision) value ready for processing is performed by AST\_UNFORMAT. However, although this process is essentially the inverse of that performed by AST\_FORMAT, there are a number of additional difficulties that must be addressed in practice. f- c+ The main use for astUnformat is in reading formatted coordinate values which have been entered by the user of a program, or read from a file. As such, we can rarely assume that the values are neatly formatted in the way that astFormat would produce. Instead, it is usually desirable to allow considerable flexibility in the form of input that can be accommodated, so as to permit ``free-format'' data input by the user. In addition, we may need to extract individual coordinate values embedded in other textual data. c- f+ The main use for AST\_UNFORMAT is in reading formatted coordinate values which have been entered by the user of a program, or read from a file. As such, we can rarely assume that the values are neatly formatted in the way that AST\_FORMAT would produce. Instead, it is usually desirable to allow considerable flexibility in the form of input that can be accommodated, so as to permit ``free-format'' data input by the user. In addition, we may need to extract individual coordinate values embedded in other textual data. f- c+ Underlying these requirements is the root difficulty that the textual format used to represent a coordinate value will depend on the class of Frame we are considering. For example, for a basic Frame, astUnformat may have to read a value like ``1.25e-6'', whereas for a more specialised Frame representing celestial coordinates it may have to handle a value like ``-07d~49m~13s''. Of course, the format might also depend on which axis is being considered. c- f+ Underlying these requirements is the root difficulty that the textual format used to represent a coordinate value will depend on the class of Frame we are considering. For example, for a basic Frame, AST\_UNFORMAT may have to read a value like ``1.25E-6'', whereas a more specialised Frame representing celestial coordinates may have to handle a value like ``-07d~49m~13s''. Of course, the format might also depend on which axis is being considered. f- Ideally, we would like to write software that can handle any kind of Frame. However, this makes it a little more difficult to analyse textual input data to extract individual coordinate values, since we cannot make assumptions about how the values are formatted. It would not be safe, for example, simply to assume that the values being read are separated by white space. This is not just because they might be separated by some other character, but also because celestial coordinate values might themselves contain spaces. In fact, to be completely safe, we cannot make any assumptions about how a formatted coordinate value is separated from the surrounding text, except that it should be separated in some way which is not ambiguous. c+ This is the very basic assumption upon which astUnformat works. It is invoked as follows: c- f+ This is the very basic assumption upon which AST\_UNFORMAT works. It is invoked as follows: f- c+ \small \begin{terminalv} int n; ... n = astUnformat( frame, iaxis, string, &value ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER N ... N = AST_UNFORMAT( FRAME, IAXIS, STRING, VALUE, STATUS ) \end{terminalv} \normalsize f- c+ It is supplied with a Frame pointer (``frame''), the number of an axis (``iaxis'') and a character string to be read (``string''). If it succeeds in reading a value, astUnformat returns the resulting coordinate to the address supplied \emph{via} the final argument (``\&value''). The returned function value indicates how many characters were read from the string in order to obtain this result. c- f+ It is supplied with a Frame pointer (FRAME), the number of an axis (IAXIS) and a character string to be read (STRING). If it succeeds in reading a value, AST\_UNFORMAT returns the resulting coordinate \emph{via} its penultimate argument (VALUE). The returned function value indicates how many characters were read from the string in order to obtain this result. f- The string is read as follows: \begin{enumerate} \item Any white space at the start is skipped over. \item Further characters are considered, one at a time, until the next character no longer matches any of the acceptable forms of input (given the characters that precede it). The longest sequence of characters which matches is then considered ``read''. \item If a suitable sequence of characters was read successfully, it is converted into a coordinate value which is returned. Any white space following this sequence is then skipped over and the total number of characters consumed is returned as the function value. c+ \item If the sequence of characters read is empty, or insufficient to define a coordinate value, then the string does not contain a value to read. In this case, the read is aborted and astUnformat returns a function value of zero and no coordinate value. However, it returns without error. c- f+ \item If the sequence of characters read is empty, or insufficient to define a coordinate value, then the string does not contain a value to read. In this case, the read is aborted and AST\_UNFORMAT returns a function value of zero and no coordinate value. However, it returns without error. f- \end{enumerate} c+ Note that failing to read a coordinate value does not constitute an error, at least so far as astUnformat is concerned. However, an error can occur if the sequence of characters read appears to have the correct form but cannot be converted into a valid coordinate value. Typically, this will be because it violates some constraint, such as a limit on the value of one of its fields. The resulting error message will give details. c- f+ Note that failing to read a coordinate value does not constitute an error, at least so far as AST\_UNFORMAT is concerned. However, an error can occur if the sequence of characters read appears to have the correct form but cannot be converted into a valid coordinate value. Typically, this will be because it violates some constraint, such as a limit on the value of one of its fields. The resulting error message will give details. f- c+ For any given Frame axis, astUnformat does not necessarily always use the same algorithm for converting the sequence of characters it reads into a coordinate value. This is because some forms of input (particularly free-format input) can be ambiguous and might be interpreted in several ways depending on the context. For example, the celestial longitude ``12:34:56.7'' could represent an angle in degrees or a right ascension in hours. To decide which to use, astUnformat may examine the Frame's attributes and, in particular, the appropriate Format(axis) string which is used by astFormat when formatting coordinate values (\secref{ss:formattingaxisvalues}). This is done in order that astFormat and astUnformat should complement each other---so that formatting a value and then un-formatting it will yield the original value, subject to any rounding error. c- f+ For any given Frame axis, AST\_UNFORMAT does not necessarily always use the same algorithm for converting the sequence of characters it reads into a coordinate value. This is because some forms of input (particularly free-format input) can be ambiguous and might be interpreted in several ways depending on the context. For example, the celestial longitude ``12:34:56.7'' could represent an angle in degrees or a right ascension in hours. To decide which to use, AST\_UNFORMAT may examine the Frame's attributes and, in particular, the appropriate Format(axis) string which is used by AST\_FORMAT when formatting coordinate values (\secref{ss:formattingaxisvalues}). This is done in order that AST\_FORMAT and AST\_UNFORMAT should complement each other---so that formatting a value and then un-formatting it will yield the original value, subject to any rounding error. f- To give a simple (but crucially incomplete!) example, consider reading a value for the axis of a basic Frame, as follows: c+ \small \begin{terminalv} n = astUnformat( frame, iaxis, " 1.5e6 -99.0", &value ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} N = AST_UNFORMAT( FRAME, IAXIS, ' 1.5E6 -99.0', VALUE, STATUS ) \end{terminalv} \normalsize f- c+ astUnformat will skip over the initial space in the string supplied and then examine each successive character. It will accept the sequence ``1.5e6'' as input, but reject the space which follows because it does not form part of the format of a floating point number. It will then convert the characters ``1.5e6'' into a coordinate value and skip over the three spaces which follow them. The returned function value will therefore be 9, equal to the total number of characters consumed. This result may be used to address the string during a subsequent read, so as to commence reading at the start of ``-99.0''. c- f+ AST\_UNFORMAT will skip over the initial space in the string supplied and then examine each successive character. It will accept the sequence ``1.5E6'' as input, but reject the space which follows because it does not form part of the format of a floating point number. It will then convert the characters ``1.5E6'' into a coordinate value and skip over the three spaces which follow them. The returned function value will therefore be 9, equal to the total number of characters consumed. This result may be used to address the string during a subsequent read, so as to commence reading at the start of ``-99.0''. f- c+ Most importantly, however, note that if the user of a program mistakenly enters the string ``~1.5r6\ldots'' instead of ``~1.5e6\ldots'', a coordinate value of 1.5 and a function result of 4 will be returned, because the ``r'' would prematurely terminate the attempt to read the value. Because this sort of mistake does not automatically result in an error but can produce incorrect results, it is \textbf{vital} to check the returned function value to ensure that the expected number of characters have been read.\footnote{Anyone who seriously uses the C run time library ``scanf'' function will know about the need for this check!} For example, if the string is expected to contain exactly one value, and nothing else, then the following would suffice: c- f+ Most importantly, however, note that if the user of a program mistakenly enters the string ``~1.5R6\ldots'' instead of ``~1.5E6\ldots'', a coordinate value of 1.5 and a function result of 4 will be returned, because the ``R'' would prematurely terminate the attempt to read the value. Because this sort of mistake does not automatically result in an error but can produce incorrect results, it is \textbf{vital} to check the returned function value to ensure that the expected number of characters have been read. For example, if the string is expected to contain exactly one value, and nothing else, then the following would suffice: f- c+ \small \begin{terminalv} n = astUnformat( frame, iaxis, string, &value ); if ( astOK ) { if ( string[ n ] || !n ) { } else { } } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} N = AST_UNFORMAT( FRAME, IAXIS, STRING, VALUE, STATUS ) IF ( STATUS .EQ. 0 ) THEN IF ( N .LT. LEN( STRING ) ) THEN ELSE END IF END IF \end{terminalv} \normalsize f- c+ If astUnformat does not detect an error itself, we check that it has read to the end-of-string and consumed at least one character (which traps the case of a zero-length input string). If this reveals an error, the value of ``n'' indicates where it occurred. c- f+ If AST\_UNFORMAT does not detect an error itself, we check that it has read to the end of the string. If this reveals an error, the value of N indicates where it occurred. f- Another common requirement is to obtain a position by reading a list of coordinates from a string which contains one value for each axis of a Frame. We assume that the values are separated in some unambiguous manner, perhaps using white space and/or some unspecified single-character separator. The choice of separator is up to the data supplier, who must choose it so as not to conflict with the format of the coordinate values, but our software does not need to know what it is. The following is a template algorithm for reading data in this form: c+ \small \begin{terminalv} const char *s; double values[ 10 ]; ... /* Initialise a string pointer. */ s = string; /* Obtain the number of Frame axes and loop through them. */ naxes = astGetI( frame, "Naxes" ); for ( iaxis = 1; iaxis <= naxes; iaxis++ ) { /* Attempt to read a value for this axis. */ n = astUnformat( frame, iaxis, s, &values[ iaxis - 1 ] ); /* If nothing was read and this is not the first axis or the end-of-string, try stepping over a separator and reading again. */ if ( !n && ( iaxis > 1 ) && *s ) n = astUnformat( frame, iaxis, ++s, &values[ iaxis - 1 ] ); /* Quit if nothing was read, otherwise move on to the next value. */ if ( !n ) break; s += n; } /* Check for possible errors. */ if ( astOK ) { if ( *s || !n ) { } else { } } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER I DOUBLE PRECISION VALUES( 10 ) ... * Initialise the string index. I = 1 * Obtain the number of Frame axes and loop through them. DO 1 IAXIS = 1, AST_GETI( FRAME, 'Naxes', STATUS ) * Attempt to read a value for this axis. N = AST_UNFORMAT( FRAME, IAXIS, STRING( I : ), : VALUES( IAXIS ), STATUS ) * If nothing was read and this is not the first axis and the end of * the string has not been reached, try stepping over a separator and * reading again. IF ( ( N .EQ. 0 ) .AND. ( IAXIS .GT. 1 ) .AND. : ( I .LT. LEN( STRING ) ) ) THEN I = I + 1 N = AST_UNFORMAT( FRAME, IAXIS, STRING( I : ), : VALUES( IAXIS ), STATUS ) END IF * Quit if nothing was read, otherwise move on to the next value. IF ( N .EQ. 0 ) GO TO 2 I = I + N 1 CONTINUE 2 CONTINUE * Check for possible errors. IF ( STATUS .EQ. 0 ) THEN IF ( ( I .LT. LEN( STRING ) ) .OR. ( N .EQ. 0 ) ) THEN ELSE END IF END IF \end{terminalv} \normalsize f- c+ In this case, ``s'' will point to the location of any input error. c- f+ In this case, the value of I will indicate the location of any input error. f- Note that this algorithm is insensitive to the precise format of the data and will therefore work with any class of Frame and any reasonably unambiguous input data. For example, here is a range of suitable input data for a 3-dimensional basic Frame: \small \begin{terminalv} 1 2.5 3 3.1,3.2,3.3 1.5, 2.6, -9.9e2 -1.1+0.4-1.8 .1/.2/.3 44.0 ; 55.1 -14 \end{terminalv} \normalsize \subsection{\label{ss:permutingaxes}Permuting Frame Axes} Once a Frame has been created, it is not possible to change the number of axes it contains, but it is possible to change the order in which these axes occur. To do so, an integer \emph{permutation array} is filled with the numbers of the axes so as to specify the new order, \emph{e.g.:} c+ \small \begin{terminalv} int perm[ 2 ] = { 2, 1 }; \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER PERM( 2 ) DATA PERM / 2, 1 / \end{terminalv} \normalsize f- c+ In this case, the axes of a 2-dimensional Frame could be interchanged by passing this permutation array to the astPermAxes function. That is, an ($x_1,x_2$) coordinate system would be changed into an ($x_2,x_1$) coordinate system by: c- f+ In this case, the axes of a 2-dimensional Frame could be interchanged by passing this permutation array to the AST\_PERMAXES function. That is, an ($x_1,x_2$) coordinate system would be changed into an ($x_2,x_1$) coordinate system by: f- c+ \small \begin{terminalv} astPermAxes( frame, perm ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_PERMAXES( FRAME, PERM, STATUS ) \end{terminalv} \normalsize f- If the axes are permuted more than once, the effects are cumulative. You are, of course, not restricted to Frames with only two axes. \subsection{Selecting Frame Axes} c+ An alternative to changing the number of Frame axes, which is not allowed, is to create a new Frame by selecting axes from an existing one. The method of doing this is very similar to the way astPermAxes is used (\secref{ss:permutingaxes}), in that we supply an integer array filled with the numbers of the axes we want, in their new order. In this case, however, the number of array elements need not equal the number of Frame axes. c- f+ An alternative to changing the number of Frame axes, which is not allowed, is to create a new Frame by selecting axes from an existing one. The method of doing this is very similar to the way AST\_PERMAXES is used (\secref{ss:permutingaxes}), in that we supply an integer array filled with the numbers of the axes we want, in their new order. In this case, however, the number of array elements need not equal the number of Frame axes. f- For example, we could select axes 3 and 2 (in that order) from a 3-dimensional Frame as follows: c+ \small \begin{terminalv} astFrame *frame1, *frame2; astMapping *mapping; int pick[ 2 ] = { 3, 2 }; ... frame2 = astPickAxes( frame1, 2, pick, &mapping ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER FRAME1, FRAME2, MAPPING, PICK( 2 ) DATA PICK / 3, 2 / ... FRAME2 = AST_PICKAXES( FRAME1, 2, PICK, MAPPING, STATUS ) \end{terminalv} \normalsize f- c+ This would return a pointer to a 2-dimensional Frame (``frame2'') which contains the information associated with axes 3 and 2, in that order, from the original Frame (``frame1''). The original Frame is not altered by this process. Beware, however, that the axis information may still be shared by both Frames, so if you wish to alter either of them independently you may first need to use astCopy (\secref{ss:copyingobjects}) to make an independent copy. c- f+ This would return a pointer to a 2-dimensional Frame (FRAME2) which contains the information associated with axes 3 and 2, in that order, from the original Frame (FRAME1). The original Frame is not altered by this process. Beware, however, that the axis information may still be shared by both Frames, so if you wish to alter either of them independently you may first need to use AST\_COPY (\secref{ss:copyingobjects}) to make an independent copy. f- c+ In addition to the new Frame pointer, astPickAxes will also return a pointer to a new Mapping \emph{via} its fourth argument (you may supply a NULL pointer as an argument if you do not want this Mapping). This Mapping will inter-relate the two Frames. By this we mean that its forward transformation will convert coordinates originally in the coordinate system represented by ``frame1'' into that represented by ``frame2'', while its inverse transformation will convert in the opposite direction. In this particular case, the Mapping would be a PermMap (\secref{ss:permmapexample}) and would implement the following transformations: c- f+ In addition to the new Frame pointer, AST\_PICKAXES will also return a pointer to a new Mapping \emph{via} its fourth argument. This Mapping will inter-relate the two Frames. By this we mean that its forward transformation will convert coordinates originally in the coordinate system represented by FRAME1 into that represented by FRAME2, while its inverse transformation will convert in the opposite direction. In this particular case, the Mapping would be a PermMap (\secref{ss:permmapexample}) and would implement the following transformations: f- \begin{terminalv} Forward: (1, 2, 3) --> (3, 2) (2, 4, 6) --> (6, 4) (3, 6, 9) --> (9, 6) (4, 8, 12) --> (12, 8) (5, 10, 15) --> (15, 10) Inverse: (3, 2) --> (, 2, 3) (6, 4) --> (, 4, 6) (9, 6) --> (, 6, 9) (12, 8) --> (, 8, 12) (15, 10) --> (, 10, 15) \end{terminalv} This is our first introduction to the idea of inter-relating pairs of Frames \emph{via} a Mapping, but this will assume a central role later on. c+ Note that when using astPickAxes, it is also possible to request more axes than there were in the original Frame. This will involve selecting axes from the original Frame that do not exist. To do this, the corresponding axis number (in the ``pick'' array) should be set to zero and the effect is to introduce an additional new axis which is not derived from the original Frame. This axis will have default values for all its attributes. You will need to do this because astPickAxes does not allow you to select any of the original axes more than once.\footnote{It will probably not be obvious why this restriction is necessary, but consider creating a Frame with one longitude axis and two latitude axes. Which latitude axis should be associated with the longitude axis?} c- f+ Note that when using AST\_PICKAXES, it is also possible to request more axes than there were in the original Frame. This will involve selecting axes from the original Frame that do not exist. To do this, the corresponding axis number (in the PICK array) should be set to zero and the effect is to introduce an additional new axis which is not derived from the original Frame. This axis will have default values for all its attributes. You will need to do this because AST\_PICKAXES does not allow you to select any of the original axes more than once.\footnote{It will probably not be obvious why this restriction is necessary, but consider creating a Frame with one longitude axis and two latitude axes. Which latitude axis should be associated with the longitude axis?} f- \subsection{\label{ss:distanceandoffset}Calculating Distances, Angles and Offsets} Some complementary c+ functions c- f+ routines f- are provided for use with Frames to allow you to perform geometric operations without needing to know the nature of the coordinate system represented by the Frame. c+ Functions c- f+ Routines f- can be used to find the distance between two points, and to offset a specified distance along a line joining two points, \emph{etc.} In essence, these define the metric of the coordinate space which the Frame represents. In the case of a basic Frame, this is a Cartesian metric. c+ The first of these functions, astDistance, returns a double distance value when supplied with the Frame coordinates of two points. For example: c- f+ The first of these routines, AST\_DISTANCE, returns a double precision distance value when supplied with the Frame coordinates of two points. For example: f- c+ \small \begin{terminalv} double dist; double point1[ 2 ] = { 0.0, 0.0 }; double point2[ 2 ] = { 1.0, 1.0 }; ... dist = astDistance( frame, point1, point2 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} DOUBLE PRECISION DIST, POINT1( 2 ), POINT2( 2 ) DATA POINT1 / 0D0, 0D0 / DATA POINT2 / 1D0, 1D0 / ... DIST = AST_DISTANCE( FRAME, POINT1, POINT2, STATUS ) \end{terminalv} \normalsize f- c+ This calculates the distance between the origin (0,0) and a point at position (1,1). In this case, the result, as you would expect, is $\surd{2}$. However, this is only true for the Cartesian coordinate system which a basic Frame represents. In general, astDistance will calculate the geodesic distance between the two points, so that with a more specialised Frame (such as a SkyFrame, representing the celestial sphere) a great-circle distance might be returned. c- f+ This calculates the distance between the origin (0,0) and a point at position (1,1). In this case, the result, as you would expect, is $\surd{2}$. However, this is only true for the Cartesian coordinate system which a basic Frame represents. In general, AST\_DISTANCE will calculate the geodesic distance between the two points, so that with a more specialised Frame (such as a SkyFrame, representing the celestial sphere) a great-circle distance might be returned. f- c+ The astOffset function is really the inverse of astDistance. Given two points in a Frame, it calculates the coordinates of a third point which is offset a specified distance away from the first point along the geodesic joining it to the second one. For example: c- f+ The AST\_OFFSET routine is really the inverse of AST\_DISTANCE. Given two points in a Frame, it calculates the coordinates of a third point which is offset a specified distance away from the first point along the geodesic joining it to the second one. For example: f- c+ \small \begin{terminalv} double point1[ 2 ] = { 0.0, 0.0 }; double point2[ 2 ] = { 1.0, 1.0 }; double point3[ 2 ]; ... astOffset( frame, point1. point2, 0.5, point3 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} DOUBLE PRECISION POINT1( 2 ), POINT2( 2 ), POINT3( 2 ) DATA POINT1 / 0D0, 0D0 / DATA POINT2 / 1D0, 1D0 / ... CALL AST_OFFSET( FRAME, POINT1, POINT2, 0.5D0, POINT3, STATUS ) \end{terminalv} \normalsize f- c+ This would fill the ``point3'' array with the coordinates of a point which is offset 0.5 units away from the origin (0,0) in the direction of the position (1,1). Again, this is a simple result in a Cartesian Frame, as varying the offset will trace out a straight line. On the celestial sphere, however (\emph{e.g.}\ using a SkyFrame), it would trace out a great circle. c- f+ This would fill the POINT3 array with the coordinates of a point which is offset 0.5 units away from the origin (0,0) in the direction of the position (1,1). Again, this is a simple result in a Cartesian Frame, as varying the offset will trace out a straight line. On the celestial sphere, however (\emph{e.g.}\ using a SkyFrame), it would trace out a great circle. f- c+ The functions astAxDistance and astAxOffset are similar to astDistance and astOffset, except that the curves which they use as ``straight lines'' are not geodesics, but curves parallel to a specified axis\footnote {For instance, a line of constant Declination is not a geodesic}. One reason for using these functions is to deal with the cyclic ambiguity of longitude and latitude axes. c- f+ The routines AST\_AXDISTANCE and AST\_AXOFFSET are similar to AST\_DISTANCE and AST\_OFFSET, except that the curves which they use as ``straight lines'' are not geodesics, but curves parallel to a specified axis\footnote {For instance, a line of constant Declination is not a geodesic}. One reason for using these routines is to deal with the cyclic ambiguity of longitude and latitude axes. f- c+ The astOffset2 function is similar to astOffset, but instead of using the c- f+ The AST\_OFFSET2 routine is similar to AST\_OFFSET, but instead of using the f- geodesic which passes through two positions, it uses the geodesic which passes at a given position angle through the starting position. Position angles are always measured from the positive direction of the second Frame axis to the required line, with positive angles being in the same sense as rotation from the positive direction of the second axis to the positive direction of the first Frame axis. This definition applies to all classes of Frame, including SkyFrame. The default ordering of axes in a SkyFrame makes the second axis equivalent to north, and so the definition of position angle given above corresponds to the normal astronomical usage, ``from north, through east''. However, it should be remembered that it is possible to permute the axes of a SkyFrame (or indeed any Frame), so that north becomes axis 1. In this case, an AST ``position angle'' would be the angle ``from east, through north''. Always take the axis ordering into account when deriving an astronomical position angle from an AST position angle. Within a Cartesian coordinate system, the position angle of a geodesic (\emph{i.e.}\ a straight line) is constant along its entire length, but this is not necessarily true of other coordinate systems. Within a spherical coordinate system, for instance, the position angle of a geodesic will vary along its length (except for the special cases of a meridian and the equator). In addition to returning the required offset position, the c+ astOffset2 function c- f+ AST\_OFFSET2 routine f- returns the position angle of the geodesic at the offset position. This is useful if you want to trace out a path which involves turning through specified angles. For instance, tracing out a rectangle in which each side is a geodesic involves turning through 90 c+ degrees at the corners. To do this, use astOffset2 to calculate the position of each corner, and then add (or subtract) 90 degrees from the position angle returned by astOffset2. c- f+ degrees at the corners. To do this, use AST\_OFFSET2 to calculate the position of each corner, and then add (or subtract) 90 degrees from the position angle returned by AST\_OFFSET2. f- c+ The astAngle function c- f+ The AST\_ANGLE routine f- calculates the angle subtended by two points, at a third point. If used with a 2-dimensional Frame the returned angle is signed to indicate the sense of rotation (clockwise or anti-clockwise) in taking the ``shortest route'' from the first point to the second. If the Frame has more than 2 axes, the result is un-signed and is always in the range zero to $\pi$. c+ The astAxAngle function is similar to astAngle, c- f+ The AST\_AXANGLE routine is similar to AST\_AXANGLE, f- but the ``reference direction'', from which angles are measured, is a specified axis. c+ The astResolve function c- f+ The AST\_RESOLVE routine f- resolves a given displacement within a Frame into two components, parallel and perpendicular to a given reference direction. The displacement is specified by two positions within the Frame; the starting and ending positions. The reference direction is defined by the geodesic curve passing through the starting position and a third specified position. The lengths of the two components are returned, together with the position on the reference geodesic which is closest to the third supplied point. \subsection{\label{ss:framedomains}The Domain Attribute} The Domain attribute is one of the most important properties of a Frame, although the concept it expresses can sometimes seem a little subtle. We will introduce it here, but its true value will probably not become apparent until later (\secref{ss:framesetconverting}). To understand the need for the Domain attribute, consider using different Frames to represent the following different coordinate systems associated with a CCD image: \begin{enumerate} \item A coordinate system based on pixel numbers. \item Positions on the CCD chip, measured in $\mu$m. \item Positions in the focal plane of the telescope, measured in mm. \item A celestial coordinate system, measured in radians. \end{enumerate} If we had two such CCD images, we might legitimately want to align them pixel-for-pixel (\emph{i.e.}\ using the coordinate system based on pixel numbers) in order to, say, divide by a flat-field exposure. We might similarly consider aligning them using any of the other coordinate systems so as to achieve different results. For example, we might consider merging separate images from a CCD mosaic by using focal plane positions. It would obviously not be legitimate, however, to directly compare positions in one image measured in pixels with positions in the other measured in mm, nor to equate chip positions in $\mu$m with sky coordinates in radians. If we wanted to inter-compare these coordinates, we would need to do it indirectly, using other information based on the experimental set-up. For instance, we might need to know the size of the pixels expressed in mm and the orientation of the CCD chip in the focal plane. Note that it is not simply the difference in physical units which prevents certain coordinates from being directly inter-compared (because the appropriate unit scaling factors could be included without any additional information). Neither is it the fact that different coordinate systems are in use (because we could legitimately inter-compare two different celestial coordinate systems without any extra information). Instead, it is the different nature of the coordinate spaces to which these coordinate systems have been applied. We normally express this by saying that the coordinate systems apply to different \emph{physical domains}. Although we may establish \emph{ad hoc} relationships between coordinates in different physical domains, they are not intrinsically related to each other and we need to supply extra information before we can convert coordinates between them. In AST, the role of the (character string) Domain attribute is to assign Frames to their respective physical domains. The way it operates is as follows: \begin{itemize} \item Coordinate systems which apply to the same physical domain (\emph{i.e.}\ whose Frames have the same Domain value) can be directly inter-compared. If the domain has several coordinate systems associated with it (\emph{e.g.}\ the celestial sphere), then a coordinate conversion may be involved. Otherwise, coordinate values may simply be equated. \item Coordinate systems which apply to different physical domains (\emph{i.e.}\ whose Frames have different Domain values) cannot be directly inter-compared. If any relationship does exist between such coordinate systems---and it need not---then additional information must be supplied in order to establish the relationship between them in any particular case. We will see later (\secref{ss:framesets}) how to establish such relationships between Frames in different domains. \end{itemize} With the basic Frames we are considering here, each physical domain only has a single (Cartesian) coordinate system associated with it, so that if two such Frames have the same Domain value, their coordinate systems will be identical and may simply be equated. With more specialised Frames, however, more than one coordinate system may apply to each domain. In such cases, a coordinate conversion may need to be performed. c+ When a basic Frame is created, its Domain attribute defaults to an empty string. This means that all such Frames belong to the same (null) domain by default and therefore describe the same unspecified physical coordinate space. In order to assign a Frame to a different domain, you simply need to set its Domain value. This is normally most conveniently done when it is created, as follows: c- f+ When a basic Frame is created, its Domain attribute defaults to a blank string. This means that all such Frames belong to the same (null) domain by default and therefore describe the same unspecified physical coordinate space. In order to assign a Frame to a different domain, you simply need to set its Domain value. This is normally most conveniently done when it is created, as follows: f- c+ \small \begin{terminalv} frame1 = astFrame( 2, "Domain=CCD_CHIP," "Unit(1)=micron," "Unit(2)=micron" ); frame2 = astFrame( 2, "Domain=FOCAL_PLANE," "Unit(1)=mm," "Unit(2)=mm" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} FRAME1 = AST_FRAME( 2, 'Domain=CCD_CHIP,' // 'Unit(1)=micron,' // 'Unit(2)=micron', STATUS ) FRAME2 = AST_FRAME( 2, 'Domain=FOCAL_PLANE,' // 'Unit(1)=mm,' // 'Unit(2)=mm', STATUS ) \end{terminalv} \normalsize f- Here, we have created two Frames in different physical domains. Although their coordinate values all have units of length, they cannot be directly inter-compared (because their axes may be rotated with respect to each other, for instance). All Domain values are automatically converted to upper case and white space is removed, but there are no other restrictions on the names you may use to label different physical domains. From a practical point of view, however, it is worth following a few conventions (\secref{ss:domainconventions}). \subsection{\label{ss:domainconventions}Conventions for Domain Names} When choosing a value for the Domain attribute of a Frame, it obviously makes sense to avoid generic names which might clash with those used for similar (but subtly different!) purposes by other programmers. If you are developing software for an instrument, for example, and want to identify an instrumental coordinate system, then it is sensible to add a distinguishing prefix. For instance, you might use $<$INST$>$\_FOCAL\_PLANE, where $<$INST$>$ (\emph{e.g.}\ an acronym) identifies your instrument. For some purposes, however, a standard choice of Domain name is desirable so that different items of software can communicate. For this purpose, the following Domain names are reserved by AST and the use recommended below should be carefully observed: \begin{quote} \begin{description} \item[GRAPHICS]\mbox{}\\ Identifies the coordinate space used by an underlying computer graphics system to specify plotting operations. Typically, when performing graphical operations, AST is used to define additional coordinate systems which are related to these ``native'' graphical coordinates. Plotting may be carried out in any of these coordinate systems, but the GRAPHICS domain identifies the native coordinates through which AST communicates with the underlying graphics system. \item[GRID]\mbox{}\\ Identifies the instantaneous \emph{data grid} used to store and handle data, together with an associated coordinate system. In this coordinate system, the first element stored in an array of data always has a coordinate value of unity at its centre and all elements have unit extent. This applies to all dimensions. If data are copied or transformed to a new data grid (by whatever means), or a subset of the original grid is extracted, then the same rules apply to the copy or subset. Its first element therefore has GRID coordinate values of unity at its centre. Note that this means that GRID coordinates remain attached to the first element of the data grid and not to its data content (\emph{e.g.}\ the features in an image). \item[PIXEL]\mbox{}\\ Identifies an array of pixels and an associated \emph{pixel-based} coordinate system which is related to the GRID coordinate system (above) simply by a shift of origin along each axis. This shift may be integral, fractional, positive, negative or zero. The data elements retain their unit extent along each axis. Because the amount of shift is unspecified, the PIXEL domain is distinct from the GRID domain. The relationship between them contains a degree of uncertainty, such as typically arises from the different conventions used by different software systems. For instance, in some software the first pixel is regarded as being centred at (1,1), while in other software it is at (0.5,0.5). In addition, some software packages implement a ``pixel origin'' which allows pixel coordinates to start at an arbitrary value. The GRID domain (which corresponds with the pixel-numbering convention used by FITS) is a special case of the PIXEL domain and avoids this uncertainty. In general, additional information is required in order to convert from one to the other. \item[SKY]\mbox{}\\ Identifies the domain which contains all equivalent celestial coordinate systems. Because these are represented in AST by SkyFrames (\secref{ss:skyframes}), it should be no surprise that the default Domain value for a SkyFrame is SKY. Since there is only one sky, you probably won't need to change this very often. \item[SPECTRUM]\mbox{}\\ Identifies the domain used to describe positions within an electro-magnetic spectrum. The AST SpecFrame (\secref{ss:specframes}) class describes positions within this domain, allowing a wide range of different coordinate systems to be used (frequency, wavelength, \emph{etc}). The default Domain value for a SpecFrame is SPECTRUM. \item[TIME]\mbox{}\\ Identifies the domain used to describe moments in time. The AST TimeFrame class describes positions within this domain, allowing a wide range of different coordinate systems and timescales to be used. The default Domain value for a TimeFrame is TIME. \end{description} \end{quote} Although we have drawn a necessary distinction here between the GRID and PIXEL domains, we will continue to refer in general terms to image ``pixels'' and ``pixel coordinates'' whenever this distinction is not important. This should not be taken to imply that the GRID convention for numbering pixels is excluded---in fact, it is usually to be preferred (at the level of data handling being discussed in this document) and we recommend it. \subsection{\label{ss:frameunits}The Unit Attribute} Each axis of a Frame has a Unit attribute which holds the physical units used to describe positions on the axis. The index of the axis to which the attribute refers should normally be placed in parentheses following the attribute name (``Unit(2)'' for instance). However, if the Frame has only a single axis, then the axis index can be omitted. In versions of AST prior to version 2.0, the Unit attribute was nothing more than a descriptive string intended purely for human readers---no part of the AST system used the Unit string for any purpose (other than inclusion in axis labels produced by the Plot class). In particular, no account was taken of the Unit attribute when finding the Mapping between two Frames. Thus if the conversion between a pair of 1-dimensional Frames representing velocity was found (using c+ astConvert c- f+ AST\_CONVERT f- ) the returned Mapping would always be a UnitMap, even if the Unit attributes of the two Frames were ``km/h'' and ``m/s''. This behaviour is referred to below as a \emph{passive} Unit attribute. As of AST version 2.0, a facility exists which allows the Unit attribute to be \emph{active}; that is, differences in the Unit attribute may be taken into account when finding the Mapping between two Frames. In order to minimise the risk of breaking older software, the \emph{default} behaviour of simple Frames and SkyFrames is unchanged from previous versions (\emph{i.e.} they have passive Unit attributes). However, the new c+ functions astSetActiveUnit and astGetActiveUnit c- f+ routines AST\_SETACTIVEUNIT and AST\_GETACTIVEUNIT f- allow this default behaviour to be changed. The SpecFrame and TimeFrame classes \emph{always} have an active Unit attribute (attempts to change this are ignored). For instance, consider the above example of two 1-dimensional Frames describing velocity. These Frames can be created as follows: c+ \small \begin{terminalv} AstFrame *frame1, *frame2; frame1 = astFrame( 1, "Domain=VELOCITY,Unit=km/h" ); frame2 = astFrame( 1, "Domain=VELOCITY,Unit=m/s" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER FRAME1, FRAME2 FRAME1 = AST_FRAME( 1, 'Domain=VELOCITY,Unit=km/h' ) FRAME2 = AST_FRAME( 1, 'Domain=VELOCITY,Unit=m/s' ) \end{terminalv} \normalsize f- By default, these Frames have passive Unit attributes, and so an attempt to find a Mapping between them would ignore the difference in their Unit attributes and return a unit Mapping. To avoid this, we indicate that we want these Frames to have \emph{active} Unit attributes, as follows: c+ \small \begin{terminalv} astSetActiveUnit( frame1, 1 ); astSetActiveUnit( frame2, 1 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SETACTIVEUNIT( FRAME1, .TRUE., STATUS ) CALL AST_SETACTIVEUNIT( FRAME2, .TRUE., STATUS ) \end{terminalv} \normalsize f- If we then find the Mapping between them as follows: c+ \small \begin{terminalv} AstFrameSet *cvt; ... cvt = astConvert( frame1, frame2, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER CVT ... CVT = AST_CONVERT( FRAME1, FRAME2, ' ', STATUS ) \end{terminalv} \normalsize f- the Mapping contained within the FrameSet returned by c+ astConvert c- f+ AST\_CONVERT f- will be a one-dimensional ZoomMap which simply scales its input (a velocity in $km/h$) by a factor of 0.278 to create its output (a velocity in $m/s$). c+ In fact we need not have set the Unit attribute active in ``frame1'' since the behaviour of astConvert is determined by its ``to'' Frame (the second Frame parameter). c- f+ In fact we need not have set the Unit attribute active in FRAME1 since the behaviour of AST\_CONVERT is determined by its TO Frame (the second Frame argument). f- \subsubsection{\label{ss:unitsyntax}The Syntax for Unit Strings} Conversion between units systems relies on the use of a specific syntax for the Unit attribute. If the value of the Unit attribute does not conform to this syntax, then an error will be reported if an attempt is made to use it to determine an inter-unit Mapping (this will never happen if the Unit attribute is \emph{passive}). The adopted syntax is that described in FITS-WCS paper I "Representation of World Coordinate in FITS" by Greisen \& Calabretta. We distinguish here between ``basic'' units and ``derived'' units: derived units are defined in terms of other units (either derived or basic), whereas basic units have no such definitions. Derived units may be represented by their own \emph{symbol} (\emph{e.g.} ``Jy''---the Jansky) or by a \emph{mathematical expression} which combines other symbols and constants to form a definition of the unit (\emph{e.g.} ``km/s''---kilometres per second). Unit symbols may be prefixed by a string representing a standard multiple or sub-multiple. In addition to the unit symbols listed in FITS-WCS Paper I, any other arbitrary unit symbol may be used, with the proviso that it will not be possible to convert between Frames using such units. The exception to this is if both Frames refer to the same unknown unit string. For instance, an axis with unknown unit symbol "flop" \emph{could} be converted to an axis with unit "Mflop" (Mega-flop). Unit symbols (optionally prefixed with a multiple or sub-multiple) can be combined together using a limited range of mathematical operators and functions, to produce new units. Such expressions may also contain parentheses and numerical constants (these may optionally use ``scientific'' notation including an ``E'' character to represent the power of 10). The following tables list the symbols for the basic and derived units which may be included in a units string, the standard prefixes for multiples and sub-multiples, and the strings which may be used to represent mathematical operators and functions. \begin{table}[htbp] \begin{center} \begin{tabular}{|l|l|l|} \hline \multicolumn{3}{|c|}{{\large Basic units}} \\ \hline \multicolumn{1}{|c|}{Quantity} & \multicolumn{1}{|c|}{Symbol} & \multicolumn{1}{c|}{Full Name} \\ \hline length & m & metre \\ mass & g & gram \\ time & s & second \\ plane angle & rad & radian \\ solid angle & sr & steradian \\ temperature & K & Kelvin \\ electric current & A & Ampere \\ amount of substance & mol & mole \\ luminous intensity & cd & candela \\ \hline \end{tabular} \end{center} \end{table} \begin{table}[htbp] \begin{center} \begin{small} \begin{tabular}{|l|l|l|l|} \hline \multicolumn{4}{|c|}{{\large Derived units}} \\ \hline \multicolumn{1}{|c|}{Quantity} & \multicolumn{1}{|c|}{Symbol} & \multicolumn{1}{c|}{Full Name} & \multicolumn{1}{c|}{Definition} \\ \hline area & barn & barn & 1.0E-28 m**2 \\ area & pix & pixel & \\ area & pixel & pixel & \\ electric capacitance & F & Farad & C/V \\ electric charge & C & Coulomb & A s \\ electric conductance & S & Siemens & A/V \\ electric potential & V & Volt & J/C \\ electric resistance & Ohm & Ohm & V/A \\ energy & J & Joule & N m \\ energy & Ry & Rydberg & 13.605692 eV \\ energy & eV & electron-Volt & 1.60217733E-19 J \\ energy & erg & erg & 1.0E-7 J \\ events & count & count & \\ events & ct & count & \\ events & ph & photon & \\ events & photon & photon & \\ flux density & Jy & Jansky & 1.0E-26 W /m**2 /Hz \\ flux density & R & Rayleigh & 1.0E10/(4*PI) photon.m**-2 /s/sr \\ flux density & mag & magnitude & \\ force & N & Newton & kg m/s**2 \\ frequency & Hz & Hertz & 1/s \\ illuminance & lx & lux & lm/m**2 \\ inductance & H & Henry & Wb/A \\ length & AU & astronomical unit & 1.49598E11 m \\ length & Angstrom & Angstrom & 1.0E-10 m \\ length & lyr & light year & 9.460730E15 m \\ length & pc & parsec & 3.0867E16 m \\ length & solRad & solar radius & 6.9599E8 m \\ luminosity & solLum & solar luminosity & 3.8268E26 W \\ luminous flux & lm & lumen & cd sr \\ magnetic field & G & Gauss & 1.0E-4 T \\ magnetic flux & Wb & Weber & V s \\ mass & solMass & solar mass & 1.9891E30 kg \\ mass & u & unified atomic mass unit & 1.6605387E-27 kg \\ magnetic flux density & T & Tesla & Wb/m**2 \\ plane angle & arcmin & arc-minute & 1/60 deg \\ plane angle & arcsec & arc-second & 1/3600 deg \\ plane angle & mas & milli-arcsecond & 1/3600000 deg \\ plane angle & deg & degree & pi/180 rad \\ power & W & Watt & J/s \\ pressure, stress & Pa & Pascal & N/m**2 \\ time & a & year & 31557600 s \\ time & d & day & 86400 s \\ time & h & hour & 3600 s \\ time & yr & year & 31557600 s \\ time & min & minute & 60 s \\ & D & Debye & 1.0E-29/3 C.m \\ \hline \end{tabular} \end{small} \end{center} \end{table} \begin{table}[htbp] \begin{center} \begin{tabular}{|lll|lll|} \hline \multicolumn{6}{|c|}{{\large Prefixes for multiples \& sub-multiples}} \\ \hline \multicolumn{1}{|c}{Sub-multiple} & \multicolumn{1}{c}{Name} & \multicolumn{1}{c|}{Prefix} & \multicolumn{1}{|c}{Sub-multiple} & \multicolumn{1}{c}{Name} & \multicolumn{1}{c|}{Prefix} \\ \hline $10^{-1}$ & deci & d & $10$ & deca & da \\ $10^{-2}$ & centi & c & $10^{2}$ & hecto & h \\ $10^{-3}$ & milli & m & $10^{3}$ & kilo & k \\ $10^{-6}$ & micro & u & $10^{6}$ & mega & M \\ $10^{-9}$ & nano & n & $10^{9}$ & giga & G \\ $10^{-12}$ & pico & p & $10^{12}$ & tera & T \\ $10^{-15}$ & femto & f & $10^{15}$ & peta & P \\ $10^{-18}$ & atto & a & $10^{18}$ & exa & E \\ $10^{-21}$ & zepto & z & $10^{21}$ & zetta & Z \\ $10^{-24}$ & yocto & y & $10^{24}$ & yotta & Y \\ \hline \end{tabular} \end{center} \end{table} \begin{table}[htbp] \begin{center} \begin{tabular}{|l|l|} \hline \multicolumn{2}{|c|}{{\large Mathematical operators \& functions}} \\ \hline \multicolumn{1}{|c|}{String} & \multicolumn{1}{|c|}{Meaning} \\ \hline sym1 sym2 & multiplication (a space) \\ sym1*sym2 & multiplication (an asterisk) \\ sym1.sym2 & multiplication (a dot) \\ sym1/sym2 & division \\ sym1**y & exponentiation ($y$ must be a numerical constant)\\ sym1\verb+^+y & exponentiation ($y$ must be a numerical constant)\\ log(sym1) & common logarithm \\ ln(sym1) & natural logarithm \\ exp(sym1) & exponential \\ sqrt(sym1) & square root \\ \hline \end{tabular} \end{center} \end{table} \subsubsection{Side-effects of Changing the Unit attribute} If an Axis has an active Unit attribute, changing its value (either by setting a new value or by clearing it so that the default value is re-instated) may cause the Label and Symbol attributes to be changed accordingly. For instance, if an Axis has Unit, Label and Symbol of ``Hz'', ``Frequency'' and ``nu'', then changing its Unit attribute to ``log(Hz)'' will cause AST to change its Label and Symbol to ``log(Frequency)'' and ``Log(nu)''. These changes are only made if the Unit attribute is active, and a Mapping can be found from the old units to the new units. On the other hand, changing the Unit from ``Hz'' to ``MHz'' would not cause any change to the Label or Symbol attributes. \cleardoublepage \section{\label{ss:skyframes}Celestial Coordinate Systems (SkyFrames)} A Frame which is specialised for representing coordinate systems on the celestial sphere is obviously of great importance in astronomy. The SkyFrame is such a Frame. In this section we examine the additional properties and behaviour of a SkyFrame that distinguish it from a basic Frame (\secref{ss:frames}). \subsection{The SkyFrame Model} A SkyFrame is, of course, a Frame (\secref{ss:frames}) and also a Mapping (\secref{ss:mappings}), so it inherits all the properties and behaviour of these two ancestral classes. When used as a Mapping, a SkyFrame implements a unit transformation, exactly like a basic Frame (\secref{ss:frameasmapping}) or a UnitMap, so this aspect of its behaviour is not of great importance. When used as a Frame, however, a SkyFrame represents a 2-dimensional \emph{spherical} coordinate system, in which the shortest distance between two points is a great circle. A SkyFrame therefore always has exactly two axes which represent the longitude and latitude of a coordinate system residing on the celestial sphere. Many such coordinate systems can be represented by a SkyFrame, as we will see shortly. A SkyFrame can represent any of the commonly used celestial coordinate systems. Optionally, the origin of the longitude/latitude system can be moved to any specified point in the standard celestial system, allowing a SkyFrame to represent offsets from a specified sky position. c+ When it is first created, a SkyFrame's axes are always in the order (longitude,~latitude) but this can be changed, if required, by using the astPermAxes function (\secref{ss:permutingaxes}). The order of the axes can be determined at any time using the LatAxis and LonAxis attributes. A SkyFrame's coordinate values are always stored as angles in (double precision) radians, regardless of the setting of the Unit attribute c- f+ When it is first created, a SkyFrame's axes are always in the order (longitude,~latitude) but this can be changed, if required, by using the AST\_PERMAXES routine (\secref{ss:permutingaxes}). The order of the axes can be determined at any time using the LatAxis and LonAxis attributes. A SkyFrame's coordinate values are always stored as angles in (double precision) radians, regardless of the setting of the Unit attribute f- \footnote{The units used for the internal floating-point representation of an axis value can be determined by examining the InternalUnit attribute of the Frame. For most Frames, the Unit and InternalUnit attributes will be equal, but InternalUnit is always set to ``\texttt{rad}'' for SkyFrames.}. \subsection{Creating a SkyFrame} The SkyFrame constructor function is particularly simple and a SkyFrame with default attributes is created as follows: c+ \small \begin{terminalv} #include "ast.h" AstSkyFrame *skyframe; ... skyframe = astSkyFrame( "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INCLUDE 'AST_PAR' INTEGER SKYFRAME, STATUS STATUS = 0 ... SKYFRAME = AST_SKYFRAME( ' ', STATUS ) \end{terminalv} \normalsize f- Such a SkyFrame would represent the default celestial coordinate system which, at present, is the ICRS system (the default was "FK5(J2000)" in versions of AST prior to 3.0). \subsection{Specifying a Particular Celestial Coordinate System} For many purposes, the ICRS coordinate system is perfectly adequate. In order to support conversion between a variety of celestial coordinate systems, however, you can create SkyFrames that represent any of these. Selection of a particular coordinate system is performed simply by setting a value for the SkyFrame's (character string) System attribute. This setting is most conveniently done when the SkyFrame is created. For example, a SkyFrame representing the old FK4~(B1950.0) coordinate system would be created by: c+ \small \begin{terminalv} skyframe = astSkyFrame( "System=FK4" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} SKYFRAME = AST_SKYFRAME( 'System=FK4', STATUS ) \end{terminalv} \normalsize f- Note that specifying ``System$=$FK4'' also changes the associated equinox (from J2000.0 to B1950.0). This is because the default value of the SkyFrame's Equinox attribute (\secref{ss:equinoxitem}) depends on the System attribute setting. You may change the System value at any time, although this is not usually needed. The values supported are set out in the attribute's description in \appref{ss:attributedescriptions} and include a variety of equatorial coordinate systems, together with ecliptic and galactic coordinates. General spherical coordinates are supported by specifying ``System$=$unknown''. You should note, though, that no Mapping can be created to convert between ``unknown'' coordinates and any of the other celestial coordinate systems (see \secref{ss:introducingconversion} ). \subsection{Attributes which Qualify Celestial Coordinate Systems} Many celestial coordinate systems have some additional free parameters which serve to identify a particular coordinate system from amongst a broader class of related coordinate systems. For example, the FK5~(J2010.0) system is distinguished from the FK5~(J2000.0) system by a different equinox---and the coordinates of a fixed astronomical source would have different values when expressed in these two systems. In AST, these free parameters are represented by additional SkyFrame attributes, each of which has a default appropriate to (\emph{i.e.}\ defined by) the setting of the main System attribute. Each of these \emph{qualifying attributes} may, however, be assigned an explicit value so as to select a particular coordinate system. Note, it is usually best to assign explicit values whenever possible rather than relying on defaults. Attribute should only be left at their default value if you ``don't care'' what value is used. In certain circumstances (particularly, when aligning two Frames), a default value for an attribute may be replaced by the value from another similar Frame. Such value replacement can be prevented by assigning an explicit value to the attribute, rather than simply relying on the default. The main SkyFrame attributes which qualify the System attribute are: \begin{quote} \begin{description} \item[\label{ss:epochitem}Epoch]\mbox{}\\ This attribute is inherited from the Frame class. It gives the moment in time when the coordinates are correct for the astronomical source under study (usually the date of observation). \item[\label{ss:equinoxitem}Equinox]\mbox{}\\ This value is used to qualify celestial coordinate systems that are notionally based on the Earth's equator and/or the ecliptic (the plane of the Earth's orbit around the Sun). The position of either of these planes is difficult to specify precisely, so in practice a model \emph{mean} equator and/or ecliptic are used instead. These, together with the point on the sky that defines the coordinate origin (termed the \emph{mean equinox}) move with time according to some model which smoothes out the more rapid fluctuations. The SkyFrame class supports both the old FK4 model and the newer FK5 one. Coordinates expressed in any of these systems vary with time due to movement (by definition) of the coordinate system itself, and must therefore be qualified by a moment in time (the \emph{epoch of the mean equinox}, or ``equinox'' for short) which specifies the position of the model coordinate system on the sky. This is the role of the Equinox attribute. Note that it is quite valid and common to relate the position of a source to an equinox other than the date of observation. Usually a standard equinox such as J2000.0 is used, meaning that the coordinates are referred to axes defined by where the model mean equator and ecliptic would lie on the sky at the Julian epoch J2000.0. \end{description} \end{quote} For further details of these attributes you should consult their descriptions in \appref{ss:attributedescriptions} and for details of the System settings for which they are relevant, see the description of the System attribute (also in \appref{ss:attributedescriptions}). For the interested reader, an excellent overview of celestial coordinate systems can also be found in the documentation for the SLALIB library (\xref{SUN/67}{sun67}{}). The value of these qualifying attributes is most conveniently set at the same time as the System value, \emph{e.g.}\ when a SkyFrame is created. For instance: c+ \small \begin{terminalv} skyframe = astSkyFrame( "System=Ecliptic, Equinox=J2005.5" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} SKYFRAME = AST_SKYFRAME( 'System=Ecliptic, Equinox=J2005.5', STATUS ) \end{terminalv} \normalsize f- would create a SkyFrame representing an ecliptic coordinate system referred to the mean equinox and ecliptic of Julian epoch J2005.5. Note that it does no harm to assign values to qualifying attributes which are not relevant to the main System value. Any such values are stored, but are not used unless the System value is later set so that they become relevant. \subsection{Using Default SkyFrame Attributes} c+ The default values supplied for many SkyFrame attributes will depend on the value of the SkyFrame's System attribute. In practice, this means that there is usually little need to specify many of these attributes explicitly unless you have some special requirement. This can be illustrated by using astShow to examine a SkyFrame, as follows: c- f+ The default values supplied for many SkyFrame attributes will depend on the value of the SkyFrame's System attribute. In practice, this means that there is usually little need to specify many of these attributes explicitly unless you have some special requirement. This can be illustrated by using AST\_SHOW to examine a SkyFrame, as follows: f- c+ \small \begin{terminalv} astShow( astSkyFrame( "System=FK4-NO-E, Epoch=1958" ) ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SHOW( AST_SKYFRAME( 'System=FK4-NO-E, Epoch=1958', STATUS ), STATUS ) \end{terminalv} \normalsize f- The output from this might look like the following: \begin{terminalv} Begin SkyFrame # Description of celestial coordinate system # Title = "FK4 equatorial coordinates; no E-terms; mean equinox B1950.0; epoch B1958.0" # Title of coordinate system Naxes = 2 # Number of coordinate axes # Domain = "SKY" # Coordinate system domain Epoch = 1958 # Besselian epoch of observation # Lbl1 = "Right ascension" # Label for axis 1 # Lbl2 = "Declination" # Label for axis 2 System = "FK4-NO-E" # Coordinate system type # Uni1 = "hh:mm:ss.s" # Units for axis 1 # Uni2 = "ddd:mm:ss" # Units for axis 2 # Dir1 = 0 # Plot axis 1 in reverse direction # Bot2 = -1.5707963267949 # Lowest legal axis value # Top2 = 1.5707963267949 # Highest legal axis value Ax1 = # Axis number 1 Begin SkyAxis # Celestial coordinate axis End SkyAxis Ax2 = # Axis number 2 Begin SkyAxis # Celestial coordinate axis End SkyAxis IsA Frame # Coordinate system description # Eqnox = 1950 # Besselian epoch of mean equinox End SkyFrame \end{terminalv} Note that the defaults (indicated by the ``\verb?#?'' comment character at the start of the line) for attributes such as the Title, axis Labels and Format specifiers are all set to values appropriate for the particular equatorial coordinate system that the SkyFrame represents. c+ This means, for example, that if we were to use this SkyFrame to format a right ascension value stored in radians using astFormat (\secref{ss:formattingaxisvalues}), it would automatically result in a string in sexagesimal notation (such as ``12:14:35.7'') suitable for display. If we changed the value of the SkyFrame's Digits attribute (which is inherited from the Frame class), the number of digits appearing would also change accordingly. c- f+ This means, for example, that if we were to use this SkyFrame to format a right ascension value stored in radians using AST\_FORMAT (\secref{ss:formattingaxisvalues}), it would automatically result in a string in sexagesimal notation (such as ``12:14:35.7'') suitable for display. If we changed the value of the SkyFrame's Digits attribute (which is inherited from the Frame class), the number of digits appearing would also change accordingly. f- These choices would be appropriate for a System value of ``FK4-NO-E'', but if a different System value were set, the defaults would be correspondingly different. For example, ecliptic longitude is traditionally expressed in degrees, so setting ``System=ecliptic'' would result in coordinate values being formatted as degrees by default. Of course, if you do not like any of these defaults, you may always over-ride them by setting explicit attribute values yourself. \subsection{\label{ss:formattingskyaxisvalues}Formatting Celestial Coordinates} c+ SkyFrames use astFormat for formatting coordinate values in the same way as other Frames (\secref{ss:formattingaxisvalues}). However, they offer a different set of formatting options more appropriate to celestial coordinates. c- f+ SkyFrames use AST\_FORMAT for formatting coordinate values in the same way as other Frames (\secref{ss:formattingaxisvalues}). However, they offer a different set of formatting options more appropriate to celestial coordinates. f- The Digits attribute of a SkyFrame behaves in essentially the same way as for a basic Frame (\secref{ss:formattingwithdigits}), so the precision with which celestial coordinates are displayed can also be adjusted in this way. However, the range of format specifiers that can be given for the Format(axis) attribute, and the default format resulting from any particular Digits value, is different. The syntax of SkyFrame format specifiers is detailed under the description of the Format(axis) attribute in \appref{ss:attributedescriptions}. Briefly, however, it allows celestial coordinates to be expressed either as angles or times and to include one or more of the fields: \begin{quote} \begin{itemize} \item degrees or hours \item arc-minutes or minutes \item arc-seconds or seconds \end{itemize} \end{quote} with a specified number of decimal places for the final field. A range of field separators is also available, as the following examples show: \begin{quote} \begin{center} \begin{tabular}{|l|l|} \hline \textbf{Format Specifier} & \textbf{Example Formatted Value}\\ \hline \hline {\tt{d}} & {\tt{219}}\\ {\tt{d.3}} & {\tt{219.123}}\\ {\tt{dm}} & {\tt{219:05}}\\ {\tt{dm.2}} & {\tt{219:05.44}}\\ {\tt{dms}} & {\tt{219:05:42}}\\ {\tt{hms.1}} & {\tt{15:44:13.8}}\\ {\tt{bdms.2}} & {\tt{219 05 42.81}}\\ {\tt{lhms.3}} & {\tt{15h44m13.88s}}\\ {\tt{+zlhms}} & {\tt{+06h10m44s}}\\ {\tt{ms.1}} & {\tt{13145:42.8}}\\ {\tt{lmst.3}} & {\tt{876m22.854s}}\\ {\tt{s.2}} & {\tt{788742.81}}\\ \hline \end{tabular} \end{center} \end{quote} Note the following key points: \begin{itemize} \item The required fields are specified using characters chosen from either ``dms'' or ``hms'' according to whether the value is to be formatted as an angle (in degrees) or a time (in hours). \item If no degrees or hours field is required, the distinction between angle and time may be made by including ``t'' to request time. \item The number of decimal places (for the final field) is indicated using ``\texttt{.}'' followed by an integer. An asterisk can be used in place of an integer, in which case the number of decimal places is chosen so that the total number of digits in the formatted value is equal to the value of the Digits attribute. \item ``b'' causes fields to be separated by blanks, while ``l'' causes them to be separated by the appropriate letters (the default being a colon). \item ``z'' causes padding with leading zeros. \item ``+'' cause a plus sign to be prefixed to positive values (negative values always have a minus sign). \end{itemize} The formatting performed by a SkyFrame is also influenced by the AsTime(axis) attribute, which has a boolean (integer) value for each SkyFrame axis. It determines whether the default format specifier for an axis will present values as angles (\emph{e.g.}\ in degrees) if it is zero, or as times (\emph{e.g.}\ in hours) if it is non-zero. The default AsTime value depends on the celestial coordinate system which the SkyFrame represents which, in turn, depends on its System attribute value. For example, equatorial longitude values (right ascension) are normally expressed in hours, whereas ecliptic longitudes are normally expressed in degrees, so their default AsTime values will reflect this difference. The value of the AsTime attribute may be set explicitly to over-ride these defaults if required, with the formatting precision being determined by the Digits/Digits(axis) value. Alternatively, the Format(axis) attribute may be set explicitly to specify both the format and precision required. Setting an explicit Format value always over-rides the effects of both the Digits and AsTime attributes (unless the Format value does not specify the required number of decimal places, in which case Digits is used to determine the default number of decimal places) \subsection{\label{ss:unformattingskyaxisvalues}Reading Formatted Celestial Coordinates} c+ The process of converting formatted celestial coordinates, such as might be produced by the astFormat function (\secref{ss:formattingskyaxisvalues}), into numerical (double) coordinate values is performed by using astUnformat (\secref{ss:unformattingaxisvalues}) and passing it a pointer to a SkyFrame. The use of a SkyFrame means that the range of input formats accepted is appropriate to positions on the sky expressed as angles and/or times, while the returned value is in radians. c- f+ The process of converting formatted celestial coordinates, such as might be produced by the AST\_FORMAT function (\secref{ss:formattingskyaxisvalues}), into numerical (double precision) coordinate values is performed by using AST\_UNFORMAT (\secref{ss:unformattingaxisvalues}) and passing it a pointer to a SkyFrame. The use of a SkyFrame means that the range of input formats accepted is appropriate to positions on the sky expressed as angles and/or times, while the returned value is in radians. f- The following describes the forms of celestial coordinate which are supported: \begin{itemize} \item You may supply an optional sign, followed by between one and three fields representing either degrees, arc-minutes, arc-seconds or hours, minutes, seconds (\emph{e.g.}\ ``$-$12~42~03''). \item Each field should consist of a sequence of one or more digits, which may include leading zeros. At most one field may contain a decimal point, in which case it is taken to be the final field (\emph{e.g.}\ decimal degrees might be given as ``124.707'', while degrees and decimal arc-minutes might be given as ``$-$13~33.8''). \item The first field given may take any value, allowing angles and times outside the conventional ranges to be represented. However, subsequent fields must have values of less than 60 (\emph{e.g.} ``720~45~31'' is valid, whereas ``11~45~61'' is not). \item Fields may be separated by white space or by ``:'' (colon), but the choice of separator must be used consistently throughout the value. Additional white space may be present around fields and separators (\emph{e.g.}\ ``$-$~2:~04~:~7.1''). \item The following field identification characters may be used as separators to replace those above (or may be appended to the final field), in order to identify the field to which they are appended: \begin{quote} \begin{tabular}{lll} d & -- & degrees \\ h & -- & hours \\ m & -- & minutes (of arc or time) \\ s & -- & seconds (of arc or time) \\ \texttt{'} & -- & arc-minutes \\ \texttt{"} & -- & arc-seconds \end{tabular} \end{quote} Either lower or upper case may be used. Fields must be given in order of decreasing significance (\emph{e.g.}\ ``$-$11D~3\texttt{'}~14.4\texttt{"}'' or ``22h14m11.2s''). \item The presence of certain field identification characters indicates whether the value is to be interpreted as an angle or a time (with 24 hours corresponding to 360 degrees), as follows: \begin{quote} \begin{tabular}{lll} d & -- & angle \\ \texttt{'} & -- & angle \\ \texttt{"} & -- & angle \\ h & -- & time \end{tabular} \end{quote} Incompatible angle/time identification characters may not be mixed (\emph{e.g.}\ ``10h14\texttt{'}3\texttt{"}'' is not valid). The remaining field identification characters and separators do not specify a preference for an angle or a time and may be used with either. c+ \item If no preference for an angle or a time is expressed anywhere within the value, then it is interpreted as an angle if the Format attribute string associated with the SkyFrame axis generates an angle and as a time otherwise. This ensures that values produced by astFormat (\secref{ss:formattingskyaxisvalues}) are correctly interpreted by astUnformat. c- f+ \item If no preference for an angle or a time is expressed anywhere within the value, then it is interpreted as an angle if the Format attribute string associated with the SkyFrame axis generates an angle and as a time otherwise. This ensures that values produced by AST\_FORMAT (\secref{ss:formattingskyaxisvalues}) are correctly interpreted by AST\_UNFORMAT. f- \item Fields may be omitted, in which case they default to zero. The remaining fields may be identified by using appropriate field identification characters (see above) and/or by adding extra colon separators (e.g. ``$-$05m13s'' is equivalent to ``$-$:05:13''). If a field is not identified explicitly, it is assumed that adjacent fields have been given, after taking account of any extra separator characters. For example: \begin{quote} \begin{tabular}{lll} 10d & -- & degrees \\ 10d12 & -- & degrees and arc-minutes \\ 11:14\texttt{"} & -- & arc-minutes and arc-seconds \\ 9h13s & -- & hours and seconds of time \\ :45:33 & -- & minutes and seconds (of arc or time) \\ :55: & -- & minutes (of arc or time) \\ ::13 & -- & seconds (of arc or time) \\ $-$6::2.5 & -- & degrees/hours and seconds (of arc or time) \\ 07m14 & -- & minutes and seconds (of arc or time) \\ $-$8:14\texttt{'} & -- & degrees and arc-minutes \\ $-$h3:14 & -- & minutes and seconds of time \\ h:2.1 & -- & seconds of time \end{tabular} \end{quote} c+ \item If fields are omitted in such a way that the remaining ones cannot be identified uniquely (e.g. ``01:02''), then the first field (either given explicitly or implied by an extra leading colon separator) is taken to be the most significant field that astFormat would produce when formatting a value (using the Format attribute associated with the SkyFrame axis). By default, this means that the first field will normally be interpreted as degrees or hours. However, if this does not result in consistent field identification, then the last field (either given explicitly or implied by an extra trailing colon separator) is taken to to be the least significant field that astFormat would produce. c- f+ \item If fields are omitted in such a way that the remaining ones cannot be identified uniquely (e.g. ``01:02''), then the first field (either given explicitly or implied by an extra leading colon separator) is taken to be the most significant field that AST\_FORMAT would produce when formatting a value (using the Format attribute associated with the SkyFrame axis). By default, this means that the first field will normally be interpreted as degrees or hours. However, if this does not result in consistent field identification, then the last field (either given explicitly or implied by an extra trailing colon separator) is taken to to be the least significant field that AST\_FORMAT would produce. f- \end{itemize} c+ This final convention is intended to ensure that values formatted by astFormat which contain less than three fields will be correctly interpreted if read back using astUnformat, even if they do not contain field identification characters. However, it also affects other forms of input. For example, if the Format(axis) string were set to ``mst.1'' (producing two fields representing minutes and seconds of time), then formatted input would be interpreted by astUnformat as follows: c- f+ This final convention is intended to ensure that values formatted by AST\_FORMAT which contain less than three fields will be correctly interpreted if read back using AST\_UNFORMAT, even if they do not contain field identification characters. However, it also affects other forms of input. For example, if the Format(axis) string were set to ``mst.1'' (producing two fields representing minutes and seconds of time), then formatted input would be interpreted by AST\_UNFORMAT as follows: f- \begin{quote} \begin{tabular}{lll} 12 13 & -- & minutes and seconds \\ 12 & -- & minutes \\ :13 & -- & seconds \\ $-$18: & -- & minutes \\ 12.8 & -- & minutes \\ 1 2 3 & -- & hours, minutes and seconds \\ & & \\ 4\texttt{'} & -- & arc-minutes \\ 60::\texttt{"} & -- & degrees \\ $-$23:\texttt{"} & -- & arc-minutes \\ $-$33h & -- & hours \end{tabular} \end{quote} (in the last four cases, explicit field identification has been given which overrides the implicit identification). c+ Alternatively, if the Format(axis) string were set to ``s.3'' (producing only an arc-seconds field), then formatted input would be interpreted by astUnformat as follows: c- f+ Alternatively, if the Format(axis) string were set to ``s.3'' (producing only an arc-seconds field), then formatted input would be interpreted by AST\_UNFORMAT as follows: f- \begin{quote} \begin{tabular}{lll} 12.8 & -- & arc-seconds \\ 12 13 & -- & arc-minutes and arc-seconds \\ :12 & -- & arc-seconds \\ 13: & -- & arc-minutes \\ 1 2 3 & -- & degrees, arc-minutes and arc-seconds \end{tabular} \end{quote} In general, if you are preparing formatted input data containing celestial coordinates and wish to omit certain fields, then you are advised to identify clearly those that you do provide by using the appropriate field identification characters and/or extra colon separators. This prevents you depending on the implicit field identification described above which, in turn, depends on an appropriate Format(axis) string having been set. When writing software, it is also a good idea to set the Format(axis) string so that data input will be as simple as possible for the user. Unless some special effect is desired, this normally means that it should contain ``d'' or ``h'' to ensure that the first field entered by the user will be interpreted as degrees or hours, unless otherwise identified. This is the normal behaviour unless an explicit Format(axis) value has been set to override the default. \subsection{Representing Offsets from a Specified Sky Position} A SkyFrame can be modified so that its longitude and latitude axes are referred to an origin at any specified sky position. Such a coordinate system is referred to as an ``offset'' coordinate system. First, the System attribute should be set to represent the celestial coordinate system in which the origin is to be specified. Then the SkyRef attribute should be set to hold the coordinates of the origin within the selected celestial coordinate system. By default, ``north'' in the new offset coordinate system is parallel to north in the original celestial coordinate system. However, the direction of north in the offset system can be controlled by assigning a value to the SkyRefP attribute. This attribute should be assigned the celestial coordinates of a point which is on the zero longitude meridian and which has non-zero latitude. By default, the position given by the SkyRef attribute is used as the origin of the new longitude/latitude system, but an option exists to use it as the north pole of the system instead. This option is controlled by the SkyRefIs attribute. The choice of value for SkyRefIs depends on what sort of offset coordinate system you want. Setting SkyRefIs to ``Origin'' (the default) produces an offset coordinate system which is approximately Cartesian close to the specified position. Setting SkyRefIs to ``Pole'' produces an offset coordinate system which is approximately Polar close to the specified position. \cleardoublepage \section{\xlabel{ss_specframes}\label{ss:specframes}Spectral Coordinate Systems (SpecFrames)} The SpecFrame is a Frame which is specialised for representing coordinate systems which describe a position within an electro-magnetic spectrum. In this section we examine the additional properties and behaviour of a SpecFrame that distinguish it from a basic Frame (\secref{ss:frames}). \subsection{The SpecFrame Model} As for a SkyFrame, a SpecFrame is a Frame (\secref{ss:frames}) and also a Mapping (\secref{ss:mappings}), so it inherits all the properties and behaviour of these two ancestral classes. When used as a Mapping, a SpecFrame implements a unit transformation, exactly like a basic Frame (\secref{ss:frameasmapping}) or a UnitMap, so this aspect of its behaviour is not of great importance. When used as a Frame, however, a SpecFrame represents a wide range of different 1-dimensional coordinate system which can be used to describe positions within a spectrum. The options available largely mirror those described in the FITS-WCS paper III \emph{Representations of spectral coordinates in FITS} (Greisen, Valdes, Calabretta \& Allen). \subsection{Creating a SpecFrame} The SpecFrame constructor function is particularly simple and a SpecFrame with default attributes is created as follows: c+ \small \begin{terminalv} #include "ast.h" AstSpecFrame *specframe; ... specframe = astSpecFrame( "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INCLUDE 'AST_PAR' INTEGER SPECFRAME, STATUS STATUS = 0 ... SPECFRAME = AST_SPECFRAME( ' ', STATUS ) \end{terminalv} \normalsize f- Such a SpecFrame would represent the default coordinate system which is heliocentric wavelength in metres (i.e. wavelength corrected to take into account the Doppler shift caused by the velocity of the observer around the sun). \subsection{Specifying a Particular Spectral Coordinate System} Selection of a particular coordinate system is performed simply by setting a value for the SpecFrame's (character string) System attribute. This setting is most conveniently done when the SpecFrame is created. For example, a SpecFrame representing Energy would be created by: c+ \small \begin{terminalv} specframe = astSpecFrame( "System=Energy" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} SPECFRAME = AST_SPECFRAME( 'System=Energy', STATUS ) \end{terminalv} \normalsize f- Note that specifying ``System$=$Energy'' also changes the associated Unit (from metres to Joules). This is because the default value of the SpecFrame's Unit attribute depends on the System attribute setting. You may change the System value at any time, although this is not usually needed. The values supported are set out in the attribute's description in \appref{ss:attributedescriptions} and include a variety of velocity systems, together with frequency, wavelength, energy, wave-number, \emph{etc}. \subsection{Attributes which Qualify Spectral Coordinate Systems} Many spectral coordinate systems have some additional free parameters which serve to identify a particular coordinate system from amongst a broader class of related coordinate systems. For example, the velocity systems are all parameterised by a rest frequency---the frequency which defines zero velocity, and all coordinate systems are qualified by a `standard of rest'' which indicates the rest frame to which the values refer. In AST, these free parameters are represented by additional SpecFrame attributes, each of which has a default appropriate to (\emph{i.e.}\ defined by) the setting of the main System attribute. Each of these \emph{qualifying attributes} may, however, be assigned an explicit value so as to select a particular coordinate system. Note, it is usually best to assign explicit values whenever possible rather than relying on defaults. Attribute should only be left at their default value if you ``don't care'' what value is used. In certain circumstances (particularly, when aligning two Frames), a default value for an attribute may be replaced by the value from another similar Frame. Such value replacement can be prevented by assigning an explicit value to the attribute, rather than simply relying on the default. The main SpecFrame attributes which qualify the System attribute are: \begin{quote} \begin{description} \item[Epoch]\mbox{}\\ This attribute is inherited from the Frame class. It gives the moment in time when the coordinates are correct for the astronomical source under study (usually the date of observation). It is needed in order to calculate the Doppler shift produced by the velocity of the observer relative to the centre of the earth, and of the earth relative to the sun. \item[StdOfRest]\mbox{}\\ This specifies the rest frame in which the coordinates are correct. Transforming between different standards of rest involves taking account of the Doppler shift introduced by the relative motion of the two standards of rest. \item[RestFreq]\mbox{}\\ Specifies the frequency which correspond to zero velocity. When setting a value for this attribute, the value may be supplied as a wavelength (including an indication of the units being used, ``nm'' ``Angstrom'', \emph{etc.}), which will be automatically be converted to a frequency. \item[RefRA]\mbox{}\\ Specifies the RA (FK5 J2000) of the source. This is used when converting between standards of rest. It specifies the direction along which the component of the relative velocity of the two standards of rest is taken. \item[RefDec]\mbox{}\\ Specifies the Dec (FK5 J2000) of the source. Used in conjunction with REFRA. \item[SourceVel]\mbox{}\\ This defines the ``source'' standard of rest. This is a rest frame which is moving towards the position given by RefRA and RefDec, at a velocity given by SourceVel. The velocity is stored internally as a heliocentric velocity, but can be given in any of the other supported standards of rest. \end{description} \end{quote} For further details of these attributes you should consult their descriptions in \appref{ss:attributedescriptions} and for details of the System settings for which they are relevant, see the description of the System attribute (also in \appref{ss:attributedescriptions}). Note that it does no harm to assign values to qualifying attributes which are not relevant to the main System value. Any such values are stored, but are not used unless the System value is later set so that they become relevant. \subsection{Using Default SpecFrame Attributes} c+ The default values supplied for many SpecFrame attributes will depend on the value of the SpecFrame's System attribute. In practice, this means that there is usually little need to specify many of these attributes explicitly unless you have some special requirement. This can be illustrated by using astShow to examine a SpecFrame, as follows: c- f+ The default values supplied for many SpecFrame attributes will depend on the value of the SpecFrame's System attribute. In practice, this means that there is usually little need to specify many of these attributes explicitly unless you have some special requirement. This can be illustrated by using AST\_SHOW to examine a SpecFrame, as follows: f- c+ \small \begin{terminalv} astShow( astSpecFrame( "System=Vopt, RestFreq=250 GHz" ) ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SHOW( AST_SPECFRAME( 'System=Vopt, RestFreq=250 GHz', STATUS ), : STATUS ) \end{terminalv} \normalsize f- The output from this might look like the following: \begin{terminalv} Begin SpecFrame # Description of spectral coordinate system # Title = "Optical velocity, rest frequency = 250 GHz" # Title of coordinate system Naxes = 1 # Number of coordinate axes # Domain = "SPECTRUM" # Coordinate system domain # Epoch = 2000 # Julian epoch of observation # Lbl1 = "Optical velocity" # Label for axis 1 System = "VOPT" # Coordinate system type # Uni1 = "km/s" # Units for axis 1 Ax1 = # Axis number 1 Begin Axis # Coordinate axis End Axis IsA Frame # Coordinate system description # SoR = "Heliocentric" # Standard of rest RstFrq = 250000000000 # Rest frequency (Hz) End SpecFrame \end{terminalv} Note that the defaults (indicated by the ``\verb?#?'' comment character at the start of the line) for attributes such as the Title, axis Labels and Unit specifiers are all set to values appropriate for the particular velocity system that the SpecFrame represents. These choices would be appropriate for a System value of ``Vopt'', but if a different System value were set, the defaults would be correspondingly different. For example, by default frequency is measured in units of GHz, not $km/s$, so setting ``System=freq'' would change the appropriate line above from: \begin{terminalv} # Uni1 = "km/s" # Units for axis 1 \end{terminalv} to \begin{terminalv} # Uni1 = "GHz" # Units for axis 1 \end{terminalv} Of course, if you do not like any of these defaults, you may always over-ride them by setting explicit attribute values yourself. For instance, you may choose to have your frequency axis expressed in ``kHz'' rather than ``GHz''. To do this simply set the attribute value as follows: c+ \small \begin{terminalv} astSetC( specframe, "Unit", "kHz" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SETC( SPECFRAME, 'Unit', 'kHz', STATUS ) \end{terminalv} \normalsize f- No error will be reported if you accidentally set an inappropriate Unit value (say "J" - Joules)---after all, AST cannot tell what you are about to do, and you \emph{may} be about to change the System value to ``Energy''. However, an error \emph{will} be reported if you attempt to find a conversion between two SpecFrames (for instance using c+ astConvert c- f+ AST\_CONVERT f- ) if either SpecFrame has a Unit value which is inappropriate for its System value. SpecFrame attributes, like all other attributes, all have default value. However, be aware that for some attributes these default values can never be more than ``a legal numerical value'' and have no astronomical significance. For instance, the RefRA and RefDec attributes (which give the source position) both have a default value of zero. So unless your source happens to be at that point (highly unlikely!) you will need to set new values. Likewise, the RestFreq (rest frequency) attribute has an arbitrary default value of 1.0E5 GHz. Some operations are not affected by inappropriate values for these attributes (for instance, converting from frequency to wavelength, changing axis units, \emph{etc}), but some are. For instance, converting from frequency to velocity requires a correct rest frequency, moving between different standards of rest requires a correct source position. The moral is, always set explicit values for as many attributes as possible. \subsection{\label{ss:creatingspectralcubes}Creating Spectral Cubes} You can use a SpecFrame to describe the spectral axis in a data cube containing two spatial axes and a spectral axis. To do this you would create an appropriate SpecFrame, together with a 2-dimensional Frame (often a SkyFrame) to describe the spatial axes. You would then combine these two Frames together into a single CmpFrame. c+ \small \begin{terminalv} AstSkyFrame *skyframe; AstSpecFrame *specframe; AstCmpFrame *cmpframe; ... skyframe = astSkyFrame( "Epoch=J2002" ); specframe = astSpecFrame( "System=Freq,StdOfRest=LSRK" ); cmpframe = astCmpFrame( skyframe, specframe, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER SKYFRAME INTEGER SPECFRAME INTEGER CMPFRAME ... SKYFRAME = AST_SKYFRAME( 'Epoch=J2002', STATUS ) SPECFRAME = AST_SPECFRAME( 'System=Freq,StdOfRest=LSRK', : STATUS ) CMPFRAME = AST_CMPFRAME( SKYFRAME, SPECFRAME, ' ', STATUS ) \end{terminalv} \normalsize f- In the resulting CmpFrame, axis 1 will be RA, axis 2 will be Dec and axis 3 will be Frequency. If this is not the order you want, you can permute the axes using c+ astPermAxes. c- f+ AST\_PERMAXES. f- There is one potential problem with this approach if you are interested in unusually high accuracy. Conversion between different standards of rest involves taking account of the Doppler shift caused by the relative motion of the two standards of rest. At some point this involves finding the component of the relative velocity in the direction of interest. For a SpecFrame, this direction is always given by the RefRA and RefDec attributes, even if the SpecFrame is embedded within a CmpFrame as above. It would be more appropriate if this ``direction of interest'' was specified by the values passed into the CmpFrame on the RA and DEC axes, allowing each pixel within a data cube to have a slightly different correction for Doppler shift. Unfortunately, the SpecFrame class cannot do this (since it is purely a 1-dimensional Frame), and so some small degree of error will be introduced when converting between standards of rest, the size of the error varying from pixel to pixel. It is hoped that at some point in the future a sub-class of CmpFrame (a SpecCubeFrame) will be added to AST which allows for this spatial variation in Doppler shift. The maximum velocity error introduced by this problem is of the order of $V*SIN(FOV)$, where $FOV$ is the angular field of view, and $V$ is the relative velocity of the two standards of rest. As an example, when correcting from the observers rest frame (i.e. the topocentric rest frame) to the kinematic local standard of rest the maximum value of $V$ is about 20 $km/s$, so for 5 arc-minute field of view the maximum velocity error introduced by the correction will be about 0.03 $km/s$. As another example, the maximum error when correcting from the observers rest frame to the local group is about 5 $km/s$ over a 1 degree field of view. \subsection{\label{ss:handlingdualsidebandspectra}Handling Dual-Sideband Spectra} Dual sideband super-heterodyne receivers produce spectra in which each channel contains contributions from two different frequencies, referred to as the ``upper sideband frequency'' and the ``lower sideband frequency''. In the rest frame of the observer (topocentric), these are related to each other as follows: \begin{quote} \begin{small} \begin{equation} \label{eqn:dsb} f_{lsb} = 2.f_{LO} - f_{usb} \end{equation} \end{small} \end{quote} where $f_{LO}$ is a fixed frequency known as the ``local oscillator frequency''. In other words, the local oscillator frequency is always mid-way between any pair of corresponding upper and lower sideband frequencies\footnote{Note, this simple relationship only applies if all frequencies are topocentric.}. If you want to describe the spectral axis of such a spectrum using a SpecFrame you must choose whether you want the SpecFrame to describe $f_{lsb}$ or $f_{usb}$ - a basic SpecFrame cannot describe both sidebands simultaneously. However, there is a sub-class of SpecFrame, called DSBSpecFrame, which overcomes this difficulty. A DSBSpecFrame has a SideBand attribute which indicates if the DSBSpecFrame is currently being used to describe the upper or lower sideband spectral axis. The value of this attribute can be changed at any time. If you use the c+ astConvert c- f+ AST\_CONVERT f- function to find the Mapping between two DSBSpecFrames, the setting for the two SideBand attributes will be taken into account. Thus, if you take a copy of a DSBSpecFrame, toggle its SideBand attribute, and then use c+ astConvert c- f+ AST\_CONVERT f- to find a Mapping from the original to the modified copy, the resulting Mapping will be of the form of equation \ref{eqn:dsb} (if the DSBSpecFrame has its StdOfRest attribute set to ``Topocentric''). In general, when finding a Mapping between two arbitrary DSBSpecFrames, the total Mapping is made of of three parts in series: \begin{enumerate} \item A Mapping which converts the first DSBSpecFrame into its upper sideband representation. If the DSBSpecFrame already represents its upper sideband, this Mapping will be a UnitMap. \item A Mapping which converts from the first to the second DSBSpecFrame, treating them as if they were both basic SpecFrames. This takes account of any difference in units, standard of rest, system, \emph{etc} between the two DSBSpecFrames. \item A Mapping which converts the second DSBSpecFrame from its upper sideband representation to its current sideband. If the DSBSpecFrame currently represents its upper sideband, this Mapping will be a UnitMap. \end{enumerate} If an attempt is made to find the Mapping between a DSBSpecFrame and a basic SpecFrame, then the DSBSpecFrame will be treated like a basic SpecFrame. In other words, the returned Mapping will not be affected by the setting of the SideBand attribute (or any of the other attributes specific to the DSBSpecFrame class). In practice, the local oscillator frequency for a dual sideband instrument may not be easily available to an observer. Instead, it is common practice to specify the spectral position of some central feature in the observation (commonly the centre of the instrument passband), together with an ``intermediate frequency''. Together, these two values allow the local oscillator frequency to be determined. The intermediate frequency is the difference between the topocentric frequency at the central spectral position and the topocentric frequency of the local oscillator. So: \begin{quote} \begin{small} \begin{equation} \label{eqn:dsb2} f_{LO} = f_{central} + f_{if} \end{equation} \end{small} \end{quote} The DSBSpecFrame class uses the DSBCentre attribute to specify the central spectral position ($f_{central}$), and the IF attribute to specify the intermediate frequency ($f_{if}$). The DSBCentre value is given and returned in the spectral system described by the DSBSpecFrame (thus you do not need to calculate the corresponding topocentric frequency yourself - this will be done automatically by the DSBSpecFrame when you assign a new value to the DSBCentre attribute). The value assigned to the IF attribute should always be a topocentric frequency in units of Hz, however a negative value may be given to indicate that the DSBCentre value is in the upper sideband (that is, if $IF < 0$ then $f_{central} > f_{LO}$). A positive value for IF indicates that the DSBCentre value is in the lower sideband (that is, if $IF > 0$ then $f_{central} < f_{LO}$). \cleardoublepage \section{\xlabel{ss_timeframes}\label{ss:timeframes}Time Systems (TimeFrames)} The TimeFrame is a Frame which is specialised for representing moments in time. In this section we examine the additional properties and behaviour of a TimeFrame that distinguish it from a basic Frame (\secref{ss:frames}). \subsection{The TimeFrame Model} As for a SkyFrame, a TimeFrame is a Frame (\secref{ss:frames}) and also a Mapping (\secref{ss:mappings}), so it inherits all the properties and behaviour of these two ancestral classes. When used as a Mapping, a TimeFrame implements a unit transformation, exactly like a basic Frame (\secref{ss:frameasmapping}) or a UnitMap, so this aspect of its behaviour is not of great importance. When used as a Frame, however, a TimeFrame represents a wide range of different 1-dimensional coordinate system which can be used to describe moments in time. Absolute times and relative (i.e. elapsed) times are supported (attribute TimeOrigin), as are a range of different time scales (attribute TimeScale). An absolute or relative value in any time scale can be represented in different forms such as Modified Julian Date, Julian Epoch, \emph{etc} (attribute System). AST extends the definition of these systems to allow them to be used with any unit of time (attribute Unit). The TimeFrame class also allows times to formatted as either a simple floating point value or as a Gregorian date and time of day (attribute Format). \subsection{Creating a TimeFrame} The TimeFrame constructor function is particularly simple and a TimeFrame with default attributes is created as follows: c+ \small \begin{terminalv} #include "ast.h" AstTimeFrame *timeframe; ... timeframe = astTimeFrame( "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INCLUDE 'AST_PAR' INTEGER TIMEFRAME, STATUS STATUS = 0 ... TIMEFRAME = AST_TIMEFRAME( ' ', STATUS ) \end{terminalv} \normalsize f- Such a TimeFrame would represent the default coordinate system which is Modified Julian Date (with the usual units of days) in the International Atomic Time (TAI) time scale. \subsection{Specifying a Particular Time System} By setting the System attribute appropriately, the TimeFrame can represent Julian Date, Modified Julian Date, Julian Epoch or Besselian Epoch (the time scale is specified by a separate attribute called TimeScale). Selection of a particular coordinate system is performed simply by setting a value for the TimeFrame's (character string) System attribute. This setting is most conveniently done when the TimeFrame is created. For example, a TimeFrame representing Julian Epoch would be created by: c+ \small \begin{terminalv} timeframe = astTimeFrame( "System=JEPOCH" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} TIMEFRAME = AST_TIMEFRAME( 'System=JEPOCH', STATUS ) \end{terminalv} \normalsize f- Note that specifying ``System$=$JEPOCH'' also changes the associated default Unit (from days to years). This is because the default value of the TimeFrame's Unit attribute depends on the System attribute setting. You may change the System value at any time, although this is not usually needed. The values supported are set out in the attribute's description in \appref{ss:attributedescriptions}. \subsection{Attributes which Qualify Time Coordinate Systems} Time coordinate systems require some additional free parameters to identify a particular coordinate system from amongst a broader class of related coordinate systems. For example, all TimeFrames are qualified by the time scale (that is, the physical process used to define the flow of time), and some require the position of the observer's clock. In AST, these free parameters are represented by additional TimeFrame attributes, each of which has a default appropriate to (\emph{i.e.}\ defined by) the setting of the main System attribute. Each of these \emph{qualifying attributes} may, however, be assigned an explicit value so as to select a particular coordinate system. Note, it is usually best to assign explicit values whenever possible rather than relying on defaults. Attribute should only be left at their default value if you ``don't care'' what value is used. In certain circumstances (particularly, when aligning two Frames), a default value for an attribute may be replaced by the value from another similar Frame. Such value replacement can be prevented by assigning an explicit value to the attribute, rather than simply relying on the default. The main TimeFrame attributes which qualify the System attribute are: \begin{quote} \begin{description} \item[TimeScale]\mbox{}\\ This specifies the time scale. \item[LTOffset]\mbox{}\\ This specifies the offset from Local Time to UTC in hours (time zones east of Greenwich have positive values). Note, AST uses the value as supplied without making any correction for daylight saving. \item[TimeOrigin]\mbox{}\\ This specifies the zero point from which time values are measured, within the system specified by the System attribute. Thus, a value of zero (the default) indicates that time values represent absolute times. Non-zero values may be used to indicate that the TimeFrame represents elapsed time since the specified origin. \end{description} \end{quote} For further details of these attributes you should consult their descriptions in \appref{ss:attributedescriptions} and for details of the System settings for which they are relevant, see the description of the System attribute (also in \appref{ss:attributedescriptions}). Note that it does no harm to assign values to qualifying attributes which are not relevant to the main System or TimeScale value. Any such values are stored, but are not used unless the System and/or TimeScale value is later set so that they become relevant. \cleardoublepage \section{\label{ss:cmpframes}Compound Frames (CmpFrames)} We now turn to a rather special form of Mapping, the CmpFrame. The Frames we have considered so far have been atomic, in the sense that they represent pre-defined elementary physical domains. A CmpFrame, however, is a compound Frame. In essence, it is a structure for containing other Frames and its purpose is to allow those Frames to work together in various combinations while appearing as a single Object. A CmpFrame's behaviour is therefore not pre-defined, but is determined by the other Frames it contains (its ``component'' Frames). As with compound Mappings, compound Frames can be nested within each other, forming arbitrarily complex Frames. \subsection{Creating a CmpFrame} A very common use for a CmpFrame within astronomy is to represent a ``spectral cube''. This is a 3-dimensional Frame in which one of the axes represents position within a spectrum, and the other two axes represent position on the sky (or some other spatial domain such as the focal plane of a telescope). As an example, we create such a CmpFrame in which axes 1 and 2 represent Right Ascension and Declination (ICRS), and axis 3 represents wavelength (these are the default coordinate Systems represented by a SkyFrame and a SpecFrame respectively): c+ \small \begin{terminalv} AstSkyFrame *skyframe; AstSpecFrame *specframe; AstCmpFrame *cmpframe; ... skyframe = astSkyFrame( "" ); specframe = astSpecFrame( "" ); cmpframe = astCmpFrame( skyframe, specframe, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER SKYFRAME INTEGER SPECFRAME INTEGER CMPFRAME ... SKYFRAME = AST_SKYFRAME( ' ', STATUS ) SPECFRAME = AST_SPECFRAME( ' ', STATUS ) CMPFRAME = AST_CMPFRAME( SKYFRAME, SPECFRAME, ' ', STATUS ) \end{terminalv} \normalsize f- If it was desired to make RA and Dec correspond to axes 1 and 3, with axis 2 being the spectral axis, then the axes of the CmpFrame created above would need to be permuted as follows: c+ \small \begin{terminalv} int perm[ 3 ]; ... perm[ 0 ] = 0; perm[ 1 ] = 2; perm[ 2 ] = 1; astPermAxes( cmpframe, perm ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER PERM(3) ... PERM( 1 ) = 1 PERM( 2 ) = 3 PERM( 3 ) = 2 CALL AST_PERMAXES( CMPFRAME, PERM, STATUS ) \end{terminalv} \normalsize f- \subsection{The Attributes of a CmpFrame} A CmpFrame \emph{is a} Frame and so has all the attributes of a Frame. The default value for the Domain attribute for a CmpFrame is formed by concatenating the Domains of the two component Frames, separated by a minus sign (``-'').\footnote{If both component Frames have blank Domains, then the default Domain for the CmpFrame is the string ``CMP''.} The (fixed) value for its System attribute is ``Compound''.\footnote{Any attempt to change the System value of a CmpFrame is ignored.} A CmpFrame has no further attributes over and above those common to all Frames. However, attributes of the two component Frames can be accessed as if they were attributes of the CmpFrame, as described below. Frame attributes which are specific to individual axes (such as Label(2), Format(1), \emph{etc}) simply mirror the corresponding axes of the relevant component Frame. That is, if the ``Label(2)'' attribute of a CmpFrame is accessed, the CmpFrame will forward the access request to the component Frame which contains axis 2. Thus, default values for axis attributes will be the same as those provided by the component Frames. An axis index can optionally be appended to the name of Frames attributes which do not normally have such an index (System, Domain, Epoch, Title, \emph{etc}). If this is done, the access request is forwarded to the component Frame containing the indicated axis. For instance, if a CmpFrame contains a SpecFrame and a SkyFrame in that order, and the axes have not been permuted, then getting the value of attribute ``System'' will return ``Compound'' as mentioned above (that is, the System value of the CmpFrame as a whole), whereas getting the value of attribute ``System(1)'' will return ``Spectral''(that is, the System value of the component Frame containing axis 1 --- the SpecFrame). This technique is not limited to attributes common to all Frames. For instance, the SkyFrame class defines an attribute called Equinox which is not held by other classes of Frames. To set a value for the Equinox attribute of the SkyFrame contained within the above CmpFrame, assign the value to the ``Equinox(2)'' attribute of the CmpFrame. Since the SkyFrame defines both axes 2 and 3 of the CmpFrame, we could equivalently have set a value for ``Equinox(3)'' since this would also result in the attribute access being forwarded to the SkyFrame. Finally, if an attribute is not qualified by a axis index, attempts will be made to access it using each of the CmpFrame axes in turn. Using the above example of the spectral cube, if an attempt was made to get the value of attribute ``Equinox'' (with no axis index), each axis in turn would be used. Since axis 1 is contained within a SpecFrame, the first attempt would fail since the SpecFrame class does not have an Equinox attribute. However, the second attempt would succeed because axis 2 is contained within a SkyFrame which \emph{does} have an Equinox attribute. Thus the returned attribute value would be that obtained from the SkyFrame containing axis 2. When getting or testing an attribute value, the returned value is determined by the \emph{first} axis which recognises the attribute. When setting an attribute value, \emph{all} axes which recognises the attribute have the attribute value set to the given value. Likewise, when clearing an attribute value, all axes which recognises the attribute have the attribute value cleared. \cleardoublepage \section{\label{ss:introducingconversion}An Introduction to Coordinate System Conversions} In this section, we start to look at techniques for converting between different coordinate systems. At this stage, the tools we have available are Frames (\secref{ss:frames}), SkyFrames (\secref{ss:skyframes}), SpecFrames (\secref{ss:specframes}), TimeFrames (\secref{ss:timeframes}) and various Mappings (\secref{ss:mappings}). These are sufficient to allow us to begin examining the problem, but more sophisticated approaches will also emerge later (\secref{ss:framesetconverting}). \subsection{\label{ss:convertingskyframes}Converting between Celestial Coordinate Systems} We begin by examining how to convert between two celestial coordinate systems represented by SkyFrames, as this is both an illuminating and practical example. Consider the problem of converting celestial coordinates between: \begin{enumerate} \item The old FK4 system, with no E terms, a Besselian epoch of 1958.0 and a Besselian equinox of 1960.0. \item An ecliptic coordinate system based on the mean equinox and ecliptic of Julian epoch 2010.5. \end{enumerate} This example is arbitrary but not completely unrealistic. Unless you already have expertise with such conversions, you are unlikely to find it straightforward. Using AST, we begin by creating two SkyFrames to represent these coordinate systems, as follows: c+ \small \begin{terminalv} #include "ast.h" AstSkyFrame *skyframe1, *skyframe2; ... skyframe1 = astSkyFrame( "System=FK4-NO-E, Epoch=B1958, Equinox=B1960" ); skyframe2 = astSkyFrame( "System=Ecliptic, Equinox=J2010.5" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INCLUDE 'AST_PAR' INTEGER SKYFRAME1, SKYFRAME2, STATUS STATUS = 0 ... SKYFRAME1 = AST_SKYFRAME( 'System=FK4-NO-E, Epoch=B1958, Equinox=B1960', STATUS ) SKYFRAME2 = AST_SKYFRAME( 'System=Ecliptic, Equinox=J2010.5', STATUS ) \end{terminalv} \normalsize f- c+ Note how specifying the coordinate systems consists simply of initialising the attributes of each SkyFrame appropriately. The next step is to find a way of converting between these SkyFrames. This is done using astConvert, as follows: c- f+ Note how specifying the coordinate systems consists simply of initialising the attributes of each SkyFrame appropriately. The next step is to find a way of converting between these SkyFrames. This is done using AST\_CONVERT, as follows: f- c+ \small \begin{terminalv} AstFrameSet *cvt; ... cvt = astConvert( skyframe1, skyframe2, "" ); if ( cvt == AST__NULL ) { } else { } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER CVT ... CVT = AST_CONVERT( SKYFRAME1, SKYFRAME2, ' ', STATUS ) IF ( CVT .EQ. AST__NULL ) THEN ELSE END IF \end{terminalv} \normalsize f- c+ The third argument of astConvert is not used here and should be an empty string. c- f+ The third argument of AST\_CONVERT is not used here and should be a blank string. f- c+ astConvert will return a null result, AST\_\_NULL (as defined in the ``ast.h'' header file), if conversion is not possible. In this example, conversion is possible, so it will return a pointer to a new Object that describes the conversion. c- f+ AST\_CONVERT will return a null result, AST\_\_NULL (as defined in the AST\_PAR include file), if conversion is not possible. In this example, conversion is possible, so it will return a pointer to a new Object that describes the conversion. f- The Object returned is called a FrameSet. We have not discussed FrameSets yet (\secref{ss:framesets}), but for the present purposes we can consider them simply as Objects that can behave both as Mappings and as Frames. It is the FrameSet's behaviour as a Mapping in which we are mainly interested here, because the Mapping it implements is the one we require---\emph{i.e.}\ it converts between the two celestial coordinate systems (\secref{ss:framesetsfromconvert}). c+ For example, if ``alpha1'' and ``delta1'' are two arrays containing the longitude and latitude, in radians, of N points on the sky in the original coordinate system (corresponding to ``skyframe1''), then they could be converted into the new coordinate system (represented by ``skyframe2'') as follows: c- f+ For example, if ALPHA1 and DELTA1 are two arrays containing the longitude and latitude, in radians, of N points on the sky in the original coordinate system (corresponding to SKYFRAME1), then they could be converted into the new coordinate system (represented by SKYFRAME2) as follows: f- c+ \small \begin{terminalv} #define N 10 double alpha1[ N ], delta1[ N ]; double alpha2[ N ], delta2[ N ]; ... astTran2( cvt, N, alpha1, delta1, 1, alpha2, delta2 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER N DOUBLE PRECISION ALPHA1( N ), DELTA1( N ) DOUBLE PRECISION ALPHA2( N ), DELTA2( N ) ... CALL AST_TRAN2( CVT, N, ALPHA1, DELTA1, .TRUE., ALPHA2, DELTA2, STATUS ) \end{terminalv} \normalsize f- c+ The new coordinates are returned \emph{via} the ``alpha2'' and ``delta2'' arrays. To transform coordinates in the opposite direction, we simply invert the 5th (boolean int) argument to astTran2, as follows: c- f+ The new coordinates are returned \emph{via} the ALPHA2 and DELTA2 arrays. To transform coordinates in the opposite direction, we simply invert the 5th (logical) argument to AST\_TRAN2, as follows: f- c+ \small \begin{terminalv} astTran2( cvt, N, alpha2, delta2, 0, alpha1, delta1 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_TRAN2( CVT, N, ALPHA2, DELTA2, .FALSE., ALPHA1, DELTA1, STATUS ) \end{terminalv} \normalsize f- c+ The FrameSet returned by astConvert also contains information about the SkyFrames used in the conversion (\secref{ss:framesetsfromconvert}). As we mentioned above, a FrameSet may be used as a Frame and in this case it behaves like the ``destination'' Frame used in the conversion (\emph{i.e.}\ like ``skyframe2''). We could therefore use the ``cvt'' FrameSet to calculate the distance between two points (with coordinates in radians) in the destination coordinate system, using astDistance: c- f+ The FrameSet returned by AST\_CONVERT also contains information about the SkyFrames used in the conversion (\secref{ss:framesetsfromconvert}). As we mentioned above, a FrameSet may be used as a Frame and in this case it behaves like the ``destination'' Frame used in the conversion (\emph{i.e.}\ like SKYFRAME2). We could therefore use the CVT FrameSet to calculate the distance between two points (with coordinates in radians) in the destination coordinate system, using AST\_DISTANCE: f- c+ \small \begin{terminalv} double distance, point1[ 2 ], point2[ 2 ]; ... distance = astDistance( cvt, point1, point2 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} DOUBLE PRECISION DISTANCE, POINT1( 2 ), POINT2( 2 ) ... DISTANCE = AST_DISTANCE( CVT, POINT1, POINT2, STATUS ) \end{terminalv} \normalsize f- c+ and the result would be the same as if the ``skyframe2'' SkyFrame had been used. c- f+ and the result would be the same as if the SKYFRAME2 SkyFrame had been used. f- Another way to see how the FrameSet produced by astConvert retains information about the coordinate systems involved is to set its Report attribute (inherited from the Mapping class) so that it displays the coordinates before and after conversion (\secref{ss:transforming}): c+ \small \begin{terminalv} astSet( cvt, "Report=1" ); astTran2( cvt, N, alpha1, delta1, 1, alpha2, delta2 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SET( CVT, 'Report=1', STATUS ) CALL AST_TRAN2( CVT, N, ALPHA1, DELTA1, .TRUE., ALPHA2, DELTA2, STATUS ) \end{terminalv} \normalsize f- The output from this might look like the following: \begin{terminalv} (2:06:03.0, 34:22:39) --> (42.1087, 20.2717) (2:08:20.6, 35:31:24) --> (43.0197, 21.1705) (2:10:38.1, 36:40:09) --> (43.9295, 22.0716) (2:12:55.6, 37:48:55) --> (44.8382, 22.9753) (2:15:13.1, 38:57:40) --> (45.7459, 23.8814) (2:17:30.6, 40:06:25) --> (46.6528, 24.7901) (2:19:48.1, 41:15:11) --> (47.5589, 25.7013) (2:22:05.6, 42:23:56) --> (48.4644, 26.6149) (2:24:23.1, 43:32:41) --> (49.3695, 27.5311) (2:26:40.6, 44:41:27) --> (50.2742, 28.4499) \end{terminalv} Here, we see that the input FK4 equatorial coordinate values (given in radians) have been formatted automatically in sexagesimal notation using the conventional hours for right ascension and degrees for declination. Conversely, the output ecliptic coordinates are shown in decimal degrees, as is conventional for ecliptic coordinates. Both are displayed using the default precision of 7 digits.\footnote{The leading digit is zero and is therefore not seen in this particular example.} c+ In fact, the ``cvt'' FrameSet has access to all the information in the original SkyFrames which were passed to astConvert. If you had set a new Digits attribute value for either of these, the formatting above would reflect the different precision you requested by displaying a greater or smaller number of digits. c- f+ In fact, the CVT FrameSet has access to all the information in the original SkyFrames which were passed to AST\_CONVERT. If you had set a new Digits attribute value for either of these, the formatting above would reflect the different precision you requested by displaying a greater or smaller number of digits. f- \subsection{\label{ss:convertingspecframes}Converting between Spectral Coordinate Systems} The principles described in the previous section for converting between celestial coordinate systems also apply to the task of converting between spectral coordinate systems. As an example, let's look at how we might convert between frequency measured in $GHz$ as measured in the rest frame of the telescope, and radio velocity measured in $km/s$ measured with respect the kinematic Local Standard of Rest. First we create a default SpecFrame, and then set its attributes to describe the required radio velocity system (this is slightly more convenient, given the relatively large number of attributes, than specifying the attribute values in a single string such as would be passed to the SpecFrame constructor). We then take a copy of this SpecFrame, and change the attribute values so that the copy describes the original frequency system (modifying a copy, rather than creating a new SpecFrame from scratch, avoids the need to specify the epoch, reference position, \emph{etc} a second time since they are all inherited by the copy): c+ \small \begin{terminalv} #include "ast.h" AstSpecFrame *specframe1, *specframe2; ... specframe1 = astSpecFrame( "" ); astSet( specframe1, "System=vradio" ); astSet( specframe1, "Unit=km/s" ); astSet( specframe1, "Epoch=1996-Oct-2 12:13:56.985" ); astSet( specframe1, "ObsLon=W155:28:18" ); astSet( specframe1, "ObsLat=N19:49:34" ); astSet( specframe1, "RefRA=18:14:50.6" ); astSet( specframe1, "RefDec=-4:40:49" ); astSet( specframe1, "RestFreq=230.538 GHz" ); astSet( specframe1, "StdOfRest=LSRK" ); specframe2 = astCopy( specframe1 ); astSet( specframe1, "System=freq" ); astSet( specframe1, "Unit=GHz" ); astSet( specframe1, "StdOfRest=Topocentric" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INCLUDE 'AST_PAR' INTEGER SPECFRAME1, SPECFRAME2, STATUS STATUS = 0 ... SPECFRAME1 = AST_SPECFRAME( ' ', STATUS ) CALL AST_SETC( SPECFRAME1, 'System=vradio', STATUS ) CALL AST_SETC( SPECFRAME1, 'Unit=km/s', STATUS ) CALL AST_SETC( SPECFRAME1, 'Epoch=1996-Oct-2 12:13:56.985', : STATUS ) CALL AST_SETC( SPECFRAME1, 'ObsLon=W155:28:18', STATUS ) CALL AST_SETC( SPECFRAME1, 'ObsLat=N19:49:34', STATUS ) CALL AST_SETC( SPECFRAME1, 'RefRA=18:14:50.6', STATUS ) CALL AST_SETC( SPECFRAME1, 'RefDec=-4:40:49', STATUS ) CALL AST_SETC( SPECFRAME1, 'RestFreq=230.538 GHz', STATUS ) CALL AST_SETC( SPECFRAME1, 'StdOfRest=LSRK', STATUS ) SPECFRAME2 = AST_COPY( SPECFRAME1, STATUS ) CALL AST_SETC( SPECFRAME1, 'System=freq', STATUS ) CALL AST_SETC( SPECFRAME1, 'Unit=GHz', STATUS ) CALL AST_SETC( SPECFRAME1, 'StdOfRest=Topocentric', STATUS ) \end{terminalv} \normalsize f- Note, the fact that a SpecFrame has only a single axis means that we were able to refer to the Unit attribute without an axis index. The other attributes are: the time of of observation (Epoch), the geographical position of the telescope (ObsLat \& ObsLon), the position of the source on the sky (RefRA \& RefDec), the rest frequency (RestFreq) and the standard of rest (StdOfRest). The next step is to find a way of converting between these SpecFrames. We use exactly the same code that we did in the previous section where we were converting between celestial coordinate systems: c+ \small \begin{terminalv} AstFrameSet *cvt; ... cvt = astConvert( specframe1, specframe2, "" ); if ( cvt == AST__NULL ) { } else { } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER CVT ... CVT = AST_CONVERT( SPECFRAME1, SPECFRAME2, ' ', STATUS ) IF ( CVT .EQ. AST__NULL ) THEN ELSE END IF \end{terminalv} \normalsize f- A before, this will give us a FrameSet (assuming conversion is possible, which should always be the case for our example), and we can use the FrameSet to convert between the two spectral coordinate systems. We use c+ astTran1 in place of astTran2 c- f+ AST\_TRAN1 in place of AST\_TRAN2 f- since a SpecFrame has only one axis (unlike a SkyFrame which has two). c+ For example, if ``frq'' is an array containing the observed frequency, in GHz, of N spectral channels (describe by ``specframe1''), then they could be converted into the new coordinate system (represented by ``specframe2'') as follows: c- f+ For example, if FRQ is an array containing the observed frequency, in GHz, of N spectral channels (describe by SPECFRAME1), then they could be converted into the new coordinate system (represented by SPECFRAME2) as follows: f- c+ \small \begin{terminalv} #define N 10 double frq[ N ]; double vel[ N ]; ... astTran1( cvt, N, frq, 1, vel ); \end{terminalv} \normalsize The radio velocity values are returned in the ``vel'' array. c- f+ \small \begin{terminalv} INTEGER N DOUBLE PRECISION FRQ( N ) DOUBLE PRECISION VEL( N ) ... CALL AST_TRAN1( CVT, N, FRQ, .TRUE., VEL, STATUS ) \end{terminalv} \normalsize The radio velocity values are returned in the VEL array. f- \subsection{Converting between Time Coordinate Systems} All the principles outlined in the previous section about aligning spectral cocordinate systems (SpecFrames) can be applied directly to the problem of aligning time coordinate systems (TimeFrames). \subsection{\label{ss:convertingpermutedaxes}Handling SkyFrame Axis Permutations} c+ We can illustrate an important point if we swap the axis order of either SkyFrame in the example above (\secref{ss:convertingskyframes}) before identifying the conversion. Let's assume we use astPermAxes (\secref{ss:permutingaxes}) to do this to the second SkyFrame, before applying astConvert, as follows: c- f+ We can illustrate an important point if we swap the axis order of either SkyFrame in the example above (\secref{ss:convertingskyframes}) before identifying the conversion. Let's assume we use AST\_PERMAXES (\secref{ss:permutingaxes}) to do this to the second SkyFrame, before applying AST\_CONVERT, as follows: f- c+ \small \begin{terminalv} int perm[ 2 ] = { 2, 1 }; ... astPermAxes( skyframe2, perm ); cvt = astConvert( skyframe1, skyframe2, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER PERM( 2 ) DATA PERM / 2, 1 / ... CALL AST_PERMAXES( SKYFRAME2, PERM, STATUS ) CVT = AST_CONVERT( SKYFRAME1, SKYFRAME2, ' ', STATUS ) \end{terminalv} \normalsize f- Now, the destination SkyFrame system no longer represents the coordinate system: \begin{quote} (ecliptic~longitude, ecliptic~latitude) \end{quote} but instead represents the transposed system: \begin{quote} (ecliptic~latitude, ecliptic~longitude) \end{quote} c+ As a consequence, when we use the FrameSet returned by astConvert to apply a coordinate transformation, we obtain something like the following: c- f+ As a consequence, when we use the FrameSet returned by AST\_CONVERT to apply a coordinate transformation, we obtain something like the following: f- \begin{terminalv} (2:06:03.0, 34:22:39) --> (20.2717, 42.1087) (2:08:20.6, 35:31:24) --> (21.1705, 43.0197) (2:10:38.1, 36:40:09) --> (22.0716, 43.9295) (2:12:55.6, 37:48:55) --> (22.9753, 44.8382) (2:15:13.1, 38:57:40) --> (23.8814, 45.7459) (2:17:30.6, 40:06:25) --> (24.7901, 46.6528) (2:19:48.1, 41:15:11) --> (25.7013, 47.5589) (2:22:05.6, 42:23:56) --> (26.6149, 48.4644) (2:24:23.1, 43:32:41) --> (27.5311, 49.3695) (2:26:40.6, 44:41:27) --> (28.4499, 50.2742) \end{terminalv} When compared to the original (\secref{ss:convertingskyframes}), the output coordinate order has been swapped to compensate for the different destination SkyFrame axis order. c+ In all, there are four possible axis combinations, corresponding to two possible axis orders for each of the source and destination SkyFrames, and astConvert will convert correctly between any of these. c- f+ In all, there are four possible axis combinations, corresponding to two possible axis orders for each of the source and destination SkyFrames, and AST\_CONVERT will convert correctly between any of these. f- The point to note is that a SkyFrame contains knowledge about how to convert to and from other SkyFrames. Since its two axes (longitude and latitude) are distinguishable, the conversion is able to take account of the axis order. If you need to identify the axes of a SkyFrame explicitly, taking into account any axis permutations, the LatAxis and LonAxis attributes can be used. These are read-only attributes which give the indices of the latitude and longitude axes respectively. \subsection{\label{ss:convertingframes}Converting Between Frames} c+ Having seen how clever SkyFrames are (\secref{ss:convertingskyframes} and \secref{ss:convertingpermutedaxes}), we will next examine how dumb a basic Frame can be in comparison. For example, if we create two 2-dimensional Frames and use astConvert to derive a conversion between them, as follows: c- f+ Having seen how clever SkyFrames are (\secref{ss:convertingskyframes} and \secref{ss:convertingpermutedaxes}), we will next examine how dumb a basic Frame can be in comparison. For example, if we create two 2-dimensional Frames and use AST\_CONVERT to derive a conversion between them, as follows: f- c+ \small \begin{terminalv} AstFrame *frame1, *frame2; ... frame1 = astFrame( 2, "" ); frame2 = astFrame( 2, "" ); cvt = astConvert( frame1, frame2, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER FRAME1, FRAME2 ... FRAME1 = AST_FRAME( 2, ' ', STATUS ) FRAME2 = AST_FRAME( 2, ' ', STATUS ) CVT = AST_CONVERT( FRAME1, FRAME2, ' ', STATUS ) \end{terminalv} \normalsize f- c+ then the coordinate transformation which the ``cvt'' FrameSet performs will be as follows: c- f+ then the coordinate transformation which the ``cvt'' FrameSet performs will be as follows: f- \begin{terminalv} (1, 2) --> (1, 2) (2, 4) --> (2, 4) (3, 6) --> (3, 6) (4, 8) --> (4, 8) (5, 10) --> (5, 10) \end{terminalv} This is an identity transformation, exactly the same as a UnitMap (\secref{ss:unitmapexample}). Even if we permute the axis order of our Frames, as we did above (\secref{ss:convertingpermutedaxes}), we will fare no better. The conversion between our two basic Frames will always be an identity transformation. The reason for this is that, unlike a SkyFrame, all basic Frames start life the same and have axes that are indistinguishable. Therefore, permuting their axes doesn't make them look any different---they still represent the same coordinate system. %Actually, this behaviour isn't as dumb as it seems and can actually be %very useful, as the following example illustrates. % %\subsection{Distinguishable and Indistinguishable Axes} % %c+ %Imagine you have two Frames which represent the pixel coordinates of %two 2-dimensional images. Let's call their axes ``X'' and ``Y''. %Suppose you now transpose the second image and swap its Frame axes %(with astPermAxes) to take account of this. %c- %f+ %Imagine you have two Frames which represent the pixel coordinates of %two 2-dimensional images. Let's call their axes ``X'' and ``Y''. %Suppose you now transpose the second image and swap its Frame axes %(with astPermAxes) to take account of this. %f- % %Next, consider what happens if you want to subtract one image from the %other. If you have a ``subtract'' program that is intelligent and %tries to align the two images for you, one of two things could happen: % %\begin{enumerate} %c+ %\item If the axes are distinguishable, when your program invokes %astConvert it will derive a transformation between the two images %which swaps the X and Y coordinates (corresponding to the transposition %you applied to the second image). However, in aligning X-with-X and %Y-with-Y, this will completely undo the effects of your transposition! %c- %f+ %\item If the axes are distinguishable, when your program invokes %AST\_CONVERT it will derive a transformation between the two images %which swaps the X and Y coordinates (corresponding to the transposition %you applied to the second image). However, in aligning X-with-X and %Y-with-Y, this will completely undo the effects of your transposition! %f- % %\item If the axes are indistinguishable, the transformation between %the two images will always be an identity %(\secref{ss:convertingframes}). Therefore, your program will align %X-with-Y and Y-with-X, so that you see the effects of your earlier %transposition of the second image. %\end{enumerate} % %Clearly, if we are considering pixel coordinates, the latter behaviour %is preferable, since there would be no point in implementing an image %transposition program if we could never see the effects of it. This %indicates that a basic Frame, with is indistinguishable axes, is the %correct type of Object to represent a pixel coordinate system, where %this behaviour is necessary. % %Conversely, the former behaviour would be more useful if the axes we %were considering were, say, wavelength (in nm) and slit position (in %mm). In this case, we would expect our ``subtract'' program to %subtract data at corresponding wavelengths and slit positions, not %just at corresponding pixels. This case requires distinguishable axes, %so that corresponding axes in the two images can be matched up, just %as happens with a SkyFrame (\secref{ss:convertingpermutedaxes}). % %Of course, there may also be intermediate cases, where some axes are %distinguishable and others aren't. \subsection{\label{ss:alignmentsystem}The Choice of Alignment System} In practice, when AST is asked to find a conversion between two Frames describing two different coordinate systems on a given physical domain, it uses an intermediate ``alignment'' system. Thus, when finding a conversion from system A to system B, AST first finds the Mapping from system A to some alignment system, system C, and then finds the Mapping from this system C to the required system B. It finally concatenates these two Mappings to get the Mapping from system A to system B. One advantage of this is that it cuts down the number of conversion algorithms required. If there are $N$ different Systems which may be used to describe positions within the Domain, then this approach requires about $2*N$ conversion algorithms to be written. The alternative approach of going directly from system A to system B would require about $N*N$ conversion algorithms. In addition, the use of an intermediate alignment system highlights the nature of the conversion process. What do we mean by saying that a Mapping ``converts a position in one coordinate system into the corresponding position in another''? In practice, it means that the input and output coordinates correspond to the same coordinates \emph{in some third coordinate system}. The choice of this third coordinate system, the ``alignment'' system, can completely alter the nature of the Mapping. The Frame class has an attribute called AlignSystem which can be used to specify the alignment system. As an example, consider the case of aligning two spectra calibrated in radio velocity, but each with a different rest frequency (each spectrum will be described by a SpecFrame). Since the rest frequencies differ, a given velocity will correspond to different frequencies in the two spectra. So when we come to ``align'' these two spectra (that is, find a Mapping which converts positions in one SpecFrame to the corresponding positions in the other), we have the choice of aligning the frequencies or aligning the velocities. Different Mappings will be required to describe these two forms of alignment. If we set AlignSystem to ``Freq'' then the returned Mapping will align the frequencies described by the two SpecFrames. On the other hand, if we set AlignSystem to ``Vradio'' then the returned Mapping will align the velocities. Some choices of alignment system are redundant. For instance, in the above example, changing the alignment system from frequency to wavelength has no effect on the returned Mapping: if two spectra are aligned in frequency they will also be aligned in wavelength (assuming the speed of light doesn't change). The default value for AlignSystem depends on the class of Frame. For a SpecFrame, the default is wavelength (or equivalently, frequency) since this is the system in which observations are usually made. The SpecFrame class also has an attribute called AlignStdOfRest which allows the standard of rest of the alignment system to be specified. Similarly, the TimeFrame class has an attribute called AlignTimeScale which allows the time scale of the alignment system to be specified. Currently, the SkyFrame uses ICRS as the default for AlignSystem, since this is a close approximation to an inertial frame of rest. \cleardoublepage \section{\label{ss:framesets}Coordinate System Networks (FrameSets)} c+ We saw in \secref{ss:introducingconversion} how astConvert could be used to find a Mapping that inter-relates a pair of coordinate systems represented by Frames. There is a limitation to this, however, in that it can only be applied to coordinate systems that are inter-related by suitable conventions. In the case of celestial coordinates, the relevant conventions are standards set out by the International Astronomical Union, and others, that define what these coordinate systems mean. In practice, however, the relationships between many other coordinate systems are also of practical importance. c- f+ We saw in \secref{ss:introducingconversion} how AST\_CONVERT could be used to find a Mapping that inter-relates a pair of coordinate systems represented by Frames. There is a limitation to this, however, in that it can only be applied to coordinate systems that are inter-related by suitable conventions. In the case of celestial coordinates, the relevant conventions are standards set out by the International Astronomical Union, and others, that define what these coordinate systems mean. In practice, however, the relationships between many other coordinate systems are also of practical importance. f- Consider, for example, the focal plane of a telescope upon which an image of the sky is falling. We could measure positions in this focal plane in millimetres or, if there were a detector system such as a CCD present, we could count pixels. We could also use celestial coordinates of many different kinds. All of these systems are equivalent in their effectiveness at specifying positions in the focal plane, but some are more convenient than others for particular purposes. Although we could, in principle, convert between all of these focal plane coordinate systems, there is no pre-defined convention for doing so. This is because the conversions required depend on where the telescope is pointing and how the CCD is mounted in the focal plane. Clearly, knowledge about this cannot be built into the AST library and must be supplied in some other way. Note that this is exactly the same problem as we met in \secref{ss:framedomains} when discussing the Domain attribute---\emph{i.e.}\ coordinate systems that apply to different physical domains require that extra information be supplied before we can convert between them. What we need, therefore, is a general way to describe how coordinate systems are inter-related, so that when there is no convention already in place, we can define our own. We can then look forward to converting, say, from pixels into galactic coordinates and {\emph{vice versa.} In AST, the FrameSet class provides this capability. \subsection{The FrameSet Model} Consider a coordinate system (call it number 1) which is represented by a Frame of some kind. Now consider a Mapping which, when applied to the coordinates in system 1 yields coordinates in another system, number 2. The Mapping therefore inter-relates coordinate systems 1 and 2. Now consider a second Mapping which inter-relates system 1 and a further coordinate system, number 3. If we wanted to convert coordinates between systems 2 and 3, we could do so by: \begin{enumerate} \item Applying our first Mapping in reverse, so as to convert between systems 2 and 1. \item Applying the second Mapping, as given, to convert between systems 1 and 3. \end{enumerate} We are not limited to three coordinate systems, of course. In fact, we could continue to introduce any number of further coordinate systems, so long as we have a suitable Mapping for each one which relates it to one of the Frames already present. Continuing in this way, we can build up a network in which Frames are inter-related by Mappings in such a way that there is always a way of converting between any pair of coordinate systems. The FrameSet (Figure~\ref{fig:frameset}) encapsulates these ideas. It is a network composed of Frames and associated Mappings, in which there is always exactly one path, \emph{via} Mappings, between any pair of Frames. Since we assemble FrameSets ourselves, they can be used to represent any coordinate systems we choose and to set up the particular relationships between them that we want. \subsection{\label{ss:creatingaframeset}Creating a FrameSet} Before we can create a FrameSet, we must have a Frame of some kind to put into it, so let's create a simple one: c+ \small \begin{terminalv} #include "ast.h" AstFrame *frame1; ... frame1 = astFrame( 2, "Domain=A" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INCLUDE 'AST_PAR' INTEGER FRAME1, STATUS STATUS = 0 ... FRAME1 = AST_FRAME( 2, 'Domain=A', STATUS ) \end{terminalv} \normalsize f- We have set this Frame's Domain attribute (\secref{ss:framedomains}) to A so that it will be distinct from the others we will be using. We can now create a new FrameSet containing just this Frame, as follows: c+ \small \begin{terminalv} AstFrameSet *frameset; ... frameset = astFrameSet( frame1, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER FRAMESET ... FRAMESET = AST_FRAMESET( FRAME1, ' ', STATUS ) \end{terminalv} \normalsize f- So far, however, this Frame isn't related to any others. \subsection{\label{ss:addingframes}Adding New Frames to a FrameSet} We can now add further Frames to the FrameSet created above (\secref{ss:creatingaframeset}). To do so, we must supply a new Frame and an associated Mapping that relates it to any of the Frames that are already present (there is only one present so far). To keep the example simple, we will just use a ZoomMap that multiplies coordinates by 10. The required Objects are created as follows: c+ \small \begin{terminalv} AstFrame *frame2; AstMapping *mapping12; ... frame2 = astFrame( 2, "Domain=B" ); mapping12 = astZoomMap( 2, 10.0, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER FRAME2, MAPPING12 ... FRAME2 = AST_FRAME( 2, 'Domain=B', STATUS ) MAPPING12 = AST_ZOOMMAP( 2, 10.0D0, ' ', STATUS ) \end{terminalv} \normalsize f- c+ To add the new Frame into our FrameSet, we use the astAddFrame function: c- f+ To add the new Frame into our FrameSet, we use the AST\_ADDFRAME routine: f- c+ \small \begin{terminalv} astAddFrame( frameset, 1, mapping12, frame2 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_ADDFRAME( FRAMESET, 1, MAPPING12, FRAME2, STATUS ) \end{terminalv} \normalsize f- Whenever a Frame is added to a FrameSet, it is assigned an integer index. This index starts with 1 for the initial Frame used to create the FrameSet (\secref{ss:creatingaframeset}) and increments by one every time a new Frame is added. This index is the primary way of identifying the Frames within a FrameSet. When a Frame is added, we also have to specify which of the existing ones the new Frame is related to. Here, we chose number 1, the only one present so far, and the new one we added became number 2. c+ Note that a FrameSet does not make copies of the Frames and Mappings that you insert into it. Instead, it holds pointers to them. This means that if you retain the original pointers to these Objects and alter them, you will indirectly be altering the FrameSet's contents. You can, of course, always use astCopy (\secref{ss:copyingobjects}) to make a separate copy of any Object if you need to ensure its independence. c- f+ Note that a FrameSet does not make copies of the Frames and Mappings that you insert into it. Instead, it holds pointers to them. This means that if you retain the original pointers to these Objects and alter them, you will indirectly be altering the FrameSet's contents. You can, of course, always use AST\_COPY (\secref{ss:copyingobjects}) to make a separate copy of any Object if you need to ensure its independence. f- c+ We could also add a third Frame into our FrameSet, this time defining a coordinate system which is reached by multiplying the original coordinates (of ``frame1'') by 5: c- f+ We could also add a third Frame into our FrameSet, this time defining a coordinate system which is reached by multiplying the original coordinates (of FRAME1) by 5: f- c+ \small \begin{terminalv} astAddFrame( frameset, 1, astZoomMap( 2, 5.0, "" ), astFrame( 2, "Domain=C" ) ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_ADDFRAME( FRAMESET, 1, : AST_ZOOMMAP( 2, 5.0D0, ' ', STATUS ), : AST_FRAME( 2, 'Domain=C', STATUS ), : STATUS ) \end{terminalv} \normalsize f- c+ Here, we have avoided storing unnecessary pointer values by using function invocations directly as arguments for astAddFrame. This assumes that we are using astBegin and astEnd (\secref{ss:contexts}) to ensure that Objects are correctly deleted when no longer required. c- f+ Here, we have avoided storing unnecessary pointer values by using function invocations directly as arguments for AST\_ADDFRAME. This assumes that we are using AST\_BEGIN and AST\_END (\secref{ss:contexts}) to ensure that Objects are correctly deleted when no longer required. f- Our example FrameSet now contains three Frames and two Mappings with the arrangement shown in Figure~\ref{fig:fsexample}. \begin{figure} \begin{center} c+ \includegraphics[width=0.7\textwidth]{sun211_figures/fsexample} c- f+ \includegraphics[width=0.7\textwidth]{sun210_figures/fsexample} f- \caption[An example FrameSet.]{An example FrameSet, in which Frames~2 and 3 are related to Frame~1 by multiplying its coordinates by factors of 10 and 5 respectively. The FrameSet's Base attribute has the value 1 and its Current attribute has the value 3. The transformation performed when the FrameSet is used as a Mapping (\emph{i.e.}\ from its base to its current Frame) is shown in bold.} \label{fig:fsexample} \end{center} \end{figure} The total number of Frames is given by its read-only Nframe attribute. \subsection{\label{ss:baseandcurrent}The Base and Current Frames} At all times, one of the Frames in a FrameSet is designated to be its \emph{base} Frame and one to be its \emph{current} Frame (Figure~\ref{fig:fsexample}). These Frames are identified by two integer FrameSet attributes, Base and Current, which hold the indices of the nominated Frames within the FrameSet. The existence of the base and current Frames reflects an important application of FrameSets, which is to attach coordinate systems to entities such as data arrays, data files, plotting surfaces (for graphics), \emph{etc.} In this context, the base Frame represents the ``native'' coordinate system of the attached entity---for example, the pixel coordinates of an image or the intrinsic coordinates of a plotting surface. The other Frames within the FrameSet represent alternative coordinate systems which may also be used to refer to positions within that entity. The current Frame represents the particular coordinate system which is currently selected for use. For instance, if an image were being displayed, you would aim to label it with coordinates corresponding to the current Frame. In order to see a different coordinate system, a software user would arrange for a different Frame to be made current. The choice of base and current Frames may be changed at any time, simply by assigning new values to the FrameSet's Base and Current attributes. For example, to make the Frame with index 3 become the current Frame, you could use: c+ \small \begin{terminalv} astSetI( frameset, "Current", 3 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SETI( FRAMESET, 'Current', 3, STATUS ) \end{terminalv} \normalsize f- You can nominate the same Frame to be both the base and current Frame if you wish. \label{ss:baseandcurrentdefault} By default (\emph{i.e.}\ if the Base or Current attribute is un-set), the first Frame added to a FrameSet becomes its base Frame and the last one added becomes its current Frame.\footnote{Although this is reversed if the FrameSet's Invert attribute is non-zero.} Whenever a new Frame is added to a FrameSet, the Current attribute is modified so that the new Frame becomes the current one. This behaviour is reflected in the state of the example FrameSet in Figure~\ref{fig:fsexample}. \subsection{\label{ss:astbaseandastcurrent}Referring to the Base and Current Frames} c+ It is often necessary to refer to the base and current Frames (\secref{ss:baseandcurrent}) within a FrameSet, but it can be cumbersome having to obtain their indices from the Base and Current attributes on each occasion. To make this easier, two macros, AST\_\_BASE and AST\_\_CURRENT, are defined in the ``ast.h'' header file and may be used to represent the indices of the base and current Frames respectively. They may be used whenever a Frame index is required. c- f+ It is often necessary to refer to the base and current Frames (\secref{ss:baseandcurrent}) within a FrameSet, but it can be cumbersome having to obtain their indices from the Base and Current attributes on each occasion. To make this easier, two parameter constants, AST\_\_BASE and AST\_\_CURRENT, are defined in the AST\_PAR include file and may be used to represent the indices of the base and current Frames respectively. They may be used whenever a Frame index is required. f- For example, when adding a new Frame to a FrameSet (\secref{ss:addingframes}), you could use the following to indicate that the new Frame is related to the existing current Frame, whatever its index happens to be: c+ \small \begin{terminalv} AstFrame *frame; AstMapping *mapping; ... astAddFrame( frameset, AST__CURRENT, mapping, frame ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER FRAME, MAPPING ... CALL AST_ADDFRAME( FRAMESET, AST__CURRENT, MAPPING, FRAME, STATUS ) \end{terminalv} \normalsize f- Of course, the Frame you added would then become the new current Frame. \subsection{\label{ss:framesetasmapping}Using a FrameSet as a Mapping} The FrameSet class inherits properties and behaviour from the Frame class (\secref{ss:frames}) and, in turn, from the Mapping class (\secref{ss:mappings}). Its behaviour when used as a Mapping is particularly important. c+ Consider, for instance, passing a FrameSet pointer to a coordinate transformation function such as astTran2: c- f+ Consider, for instance, passing a FrameSet pointer to a coordinate transformation routine such as AST\_TRAN2: f- c+ \small \begin{terminalv} #define N 10 double xin[ N ], yin[ N ], xout[ N ], yout[ N ]; ... astTran2( frameset, N, xin, yin, 1, xout, yout ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER N DOUBLE PRECISION XIN( N ), YIN( N ) DOUBLE PRECISION XOUT( N ), YOUT( N ) ... CALL AST_TRAN2( FRAMESET, N, XIN, YIN, .TRUE., XOUT, YOUT, STATUS ) \end{terminalv} \normalsize f- The coordinate transformation applied by this FrameSet would be the one which converts between its base and current Frames. Using the FrameSet in Figure~\ref{fig:fsexample}, for example, the coordinates would be multiplied by a factor of 5. If we instead requested the FrameSet's inverse transformation, we would be transforming from its current Frame to its base Frame, so our example FrameSet would then multiply by a factor of 0.2. Whenever the choice of base and current Frames changes, the transformations which a FrameSet performs when used as a Mapping also change to reflect this. The Nin and Nout attributes may also change in consequence, because they are determined by the numbers of axes in the FrameSet's base and current Frames respectively. These numbers need not necessarily be equal, of course. c+ Like any Mapping, a FrameSet may also be inverted by changing the boolean sense of its Invert attribute, \emph{e.g.}\ using astInvert (\secref{ss:invertingmappings}). If this is happens, the values of the FrameSet's Base and Current attributes are interchanged, along with its Nin and Nout attributes, so that its base and current Frames swap places. When used as a Mapping, the FrameSet will therefore perform the inverse transformation to that which it performed previously. c- f+ Like any Mapping, a FrameSet may also be inverted by changing the boolean sense of its Invert attribute, \emph{e.g.}\ using AST\_INVERT (\secref{ss:invertingmappings}). If this is happens, the values of the FrameSet's Base and Current attributes are interchanged, along with its Nin and Nout attributes, so that its base and current Frames swap places. When used as a Mapping, the FrameSet will therefore perform the inverse transformation to that which it performed previously. f- To summarise, a FrameSet may be used exactly like any other Mapping which inter-relates the coordinate systems described by its base and current Frames. \subsection{\label{ss:extractingamapping}Extracting a Mapping from a FrameSet} Although it is very convenient to use a FrameSet when a Mapping is required (\secref{ss:framesetasmapping}), a FrameSet necessarily contains additional information and sometimes this might cause inefficiency or confusion. For example, if you wanted to use a Mapping contained in one FrameSet and insert it into another, it would probably not be efficient to insert the whole of the first FrameSet into the second one, although it would work. c+ In such a situation, the astGetMapping function allows you to extract a Mapping from a FrameSet. You do this by specifying the two Frames which the Mapping should inter-relate using their indices within the FrameSet. For example: c- f+ In such a situation, the AST\_GETMAPPING function allows you to extract a Mapping from a FrameSet. You do this by specifying the two Frames which the Mapping should inter-relate using their indices within the FrameSet. For example: f- c+ \small \begin{terminalv} map = astGetMapping( frameset, 2, 3 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} MAP = AST_GETMAPPING( FRAMESET, 2, 3, STATUS ) \end{terminalv} \normalsize f- c+ would return a pointer to a Mapping that converted between Frames~2 and 3 in the FrameSet. Its inverse transformation would then convert in the opposite direction, \emph{i.e.}\ between Frames~3 and 2. Note that this Mapping might not be independent of the Mappings contained within the FrameSet---\emph{i.e.}\ they may share sub-Objects---so astCopy should be used to make a copy if you need to guarantee independence (\secref{ss:copyingobjects}). c- f+ would return a pointer to a Mapping that converted between Frames~2 and 3 in the FrameSet. Its inverse transformation would then convert in the opposite direction, \emph{i.e.}\ between Frames~3 and 2. Note that this Mapping might not be independent of the Mappings contained within the FrameSet---\emph{i.e.}\ they may share sub-Objects---so AST\_COPY should be used to make a copy if you need to guarantee independence (\secref{ss:copyingobjects}). f- c+ Very often, the Mapping returned by astGetMapping will be a compound Mapping, or CmpMap (\secref{ss:cmpmaps}). This reflects the fact that conversion between the two Frames may need to be done \emph{via} an intermediate coordinate system so that several stages may be involved. You can, however, easily simplify this Mapping (where this is possible) by using the astSimplify function (\secref{ss:simplifyingcmpmaps}) and this is recommended if you plan to use it for transforming a large amount of data. c- f+ Very often, the Mapping returned by AST\_GETMAPPING will be a compound Mapping, or CmpMap (\secref{ss:cmpmaps}). This reflects the fact that conversion between the two Frames may need to be done \emph{via} an intermediate coordinate system so that several stages may be involved. You can, however, easily simplify this Mapping (where this is possible) by using the AST\_SIMPLIFY function (\secref{ss:simplifyingcmpmaps}) and this is recommended if you plan to use it for transforming a large amount of data. f- \subsection{\label{ss:framesetasframe}Using a FrameSet as a Frame} A FrameSet can also be used as a Frame, in which capacity it almost always behaves as if its current Frame had been used instead. For example, if you request the Title attribute of a FrameSet using: c+ \small \begin{terminalv} const char *title; ... title = astGetC( frameset, "Title" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CHARACTER * ( 80 ) TITLE ... TITLE = AST_GETC( FRAMESET, 'Title', STATUS ) \end{terminalv} \normalsize f- the result will be the Title of the current Frame, or a suitable default if the current Frame's Title attribute is un-set. The same also applies to other attribute operations---\emph{i.e.}\ setting, clearing and testing attributes. Most attributes shared by both Frames and FrameSets behave in this way, such as Naxes, Label(axis), Format(axis), \emph{etc.} There are, however, a few exceptions: \begin{quote} \begin{description} \item[Class]\mbox{}\\ Has the value ``FrameSet''. \item[ID]\mbox{}\\ Identifies the particular FrameSet (not its current Frame). \item[Nin]\mbox{}\\ Equals the number of axes in the FrameSet's base Frame. \item[Invert]\mbox{}\\ Is independent of any of the Objects within the FrameSet. \item[Nobject]\mbox{}\\ Counts the number of active FrameSets. \item[RefCount]\mbox{}\\ Counts the number of active pointers to the FrameSet (not to its current Frame). \end{description} \end{quote} Note that the set of attributes possessed by a FrameSet can vary, depending on the nature of its current Frame. For example, if the current Frame is a SkyFrame (\secref{ss:skyframes}), then the FrameSet will acquire an Equinox attribute from it which can be set, enquired, \emph{etc.} However, if the current Frame is changed to be a basic Frame, which does not have an Equinox attribute, then this attribute will be absent from the FrameSet as well. Any attempt to reference it will then result in an error. \subsection{Extracting a Frame from a FrameSet} c+ Although a FrameSet may be used in place of its current Frame in most situations, it is sometimes convenient to have direct access to a specified Frame within it. This may be obtained using the astGetFrame function, as follows: c- f+ Although a FrameSet may be used in place of its current Frame in most situations, it is sometimes convenient to have direct access to a specified Frame within it. This may be obtained using the AST\_GETFRAME function, as follows: f- c+ \small \begin{terminalv} frame = astGetFrame( frameset, AST__BASE ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} FRAME = AST_GETFRAME( FRAMESET, AST__BASE, STATUS ) \end{terminalv} \normalsize f- This would return a pointer (not a copy) to the base Frame within the FrameSet. Note the use of AST\_\_BASE (\secref{ss:astbaseandastcurrent}) as shorthand for the value of the FrameSet's Base attribute, which gives the base Frame's index. \subsection{Removing a Frame from a FrameSet} c+ Removing a Frame from a FrameSet is straightforward and is performed using the astRemoveFrame function. You identify the Frame you wish to remove in the usual way, by giving its index within the FrameSet. For example, the following would remove the Frame with index 1: c- f+ Removing a Frame from a FrameSet is straightforward and is performed using the AST\_REMOVEFRAME routine. You identify the Frame you wish to remove in the usual way, by giving its index within the FrameSet. For example, the following would remove the Frame with index 1: f- c+ \small \begin{terminalv} astRemoveFrame( frameset, 1 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_REMOVEFRAME( FRAMESET, 1, STATUS ); \end{terminalv} \normalsize f- The only restriction is that you cannot remove the last remaining Frame because a FrameSet must always contain at least one Frame. When a Frame is removed, the Frames which follow it are re-numbered (\emph{i.e.}\ their indices are reduced by one) so as to preserve the sequence of consecutive Frame indices. The FrameSet's Nframe attribute is also decremented. c+ If appropriate, astRemoveFrame will modify the FrameSet's Base and/or Current attributes so that they continue to identify the same Frames as previously. If either the base or current Frame is removed, however, the corresponding attribute will become un-set, so that it reverts to its default value (\secref{ss:baseandcurrentdefault}) and therefore identifies an alternative Frame. c- f+ If appropriate, AST\_REMOVEFRAME will modify the FrameSet's Base and/or Current attributes so that they continue to identify the same Frames as previously. If either the base or current Frame is removed, however, the corresponding attribute will become un-set, so that it reverts to its default value (\secref{ss:baseandcurrentdefault}) and therefore identifies an alternative Frame. f- Note that it is quite permissible to remove any Frame from a FrameSet, even although other Frames may appear to depend on it. For example, in Figure~\ref{fig:fsexample}, if Frame~1 were removed, the correct relationship between Frames~2 and 3 would still be preserved, although they would be re-numbered as Frames~1 and 2. \cleardoublepage \section{\label{ss:fshigher}Higher Level Operations on FrameSets} c+ \subsection{\label{ss:framesetsfromconvert}Creating FrameSets with astConvert} c- f+ \subsection{\label{ss:framesetsfromconvert}Creating FrameSets with AST\_CONVERT} f- c+ Before considering the important subject of using FrameSets to convert between coordinate systems (\secref{ss:framesetconverting}), let us return briefly to reconsider the output generated by astConvert. We used this function earlier (\secref{ss:introducingconversion}), when converting between the coordinate systems represented by various kinds of Frame, and indicated that it returns a FrameSet to represent the coordinate conversion it identifies. We are now in a position to examine the structure of this FrameSet. c- f+ Before considering the important subject of using FrameSets to convert between coordinate systems (\secref{ss:framesetconverting}), let us return briefly to reconsider the output generated by AST\_CONVERT. We used this function earlier (\secref{ss:introducingconversion}), when converting between the coordinate systems represented by various kinds of Frame, and indicated that it returns a FrameSet to represent the coordinate conversion it identifies. We are now in a position to examine the structure of this FrameSet. f- Take our earlier example (\secref{ss:convertingskyframes}) of converting between the celestial coordinate systems represented by two SkyFrames: c+ \small \begin{terminalv} #include "ast.h" AstFrameSet *cvt; AstSkyFrame *skyframe1, *skyframe2; ... skyframe1 = astSkyFrame( "System=FK4-NO-E, Epoch=B1958, Equinox=B1960" ); skyframe2 = astSkyFrame( "System=Ecliptic, Equinox=J2010.5" ); cvt = astConvert( skyframe1, skyframe2, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INCLUDE 'AST_PAR' INTEGER SKYFRAME1, SKYFRAME2, STATUS STATUS = 0 ... SKYFRAME1 = AST_SKYFRAME( 'System=FK4-NO-E, Epoch=B1958, Equinox=B1960', STATUS ) SKYFRAME2 = AST_SKYFRAME( 'System=Ecliptic, Equinox=J2010.5', STATUS ) CVT = AST_CONVERT( SKYFRAME1, SKYFRAME2, ' ', STATUS ) \end{terminalv} \normalsize f- c+ This will produce a pointer, ``cvt'', to the FrameSet shown in Figure~\ref{fig:fsconvert}. c- f+ This will produce a pointer, CVT, to the FrameSet shown in Figure~\ref{fig:fsconvert}. f- \begin{figure}[bhtp] \begin{center} c+ \includegraphics[width=0.7\textwidth]{sun211_figures/fsconvert} c- f+ \includegraphics[width=0.7\textwidth]{sun210_figures/fsconvert} f- c+ \caption[FrameSet produced when converting between two SkyFrames.]{The FrameSet produced when astConvert is used to convert c- f+ \caption[FrameSet produced when converting between two SkyFrames.]{The FrameSet produced when AST\_CONVERT is used to convert f- between the coordinate systems represented by two SkyFrames. The source SkyFrame becomes the base Frame, while the destination SkyFrame becomes the current Frame. The Mapping between them implements the required conversion.} \label{fig:fsconvert} \end{center} \end{figure} c+ As can be seen, this FrameSet contains just two Frames. The source Frame supplied to astConvert becomes its base Frame, while the destination Frame becomes its current Frame. (The FrameSet, of course, simply holds pointers to these Frames, rather than making copies.) The Mapping which relates the base Frame to the current Frame is the one which implements the required conversion. c- f+ As can be seen, this FrameSet contains just two Frames. The source Frame supplied to AST\_CONVERT becomes its base Frame, while the destination Frame becomes its current Frame. (The FrameSet, of course, simply holds pointers to these Frames, rather than making copies.) The Mapping which relates the base Frame to the current Frame is the one which implements the required conversion. f- c+ As we noted earlier (\secref{ss:convertingskyframes}), the FrameSet returned by astConvert may be used both as a Mapping and as a Frame to perform most of the functions you are likely to need. However, the Mapping may be extracted for use on its own if necessary, using astGetMapping (\secref{ss:extractingamapping}), for example: c- f+ As we noted earlier (\secref{ss:convertingskyframes}), the FrameSet returned by AST\_CONVERT may be used both as a Mapping and as a Frame to perform most of the functions you are likely to need. However, the Mapping may be extracted for use on its own if necessary, using AST\_GETMAPPING (\secref{ss:extractingamapping}), for example: f- c+ \small \begin{terminalv} AstMapping *mapping; ... mapping = astGetMapping( cvt, AST__BASE, AST__CURRENT ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER MAPPING ... MAPPING = AST_GETMAPPING( CVT, AST__BASE, AST__CURRENT, STATUS ) \end{terminalv} \normalsize f- \subsection{\label{ss:framesetconverting}Converting between FrameSet Coordinate Systems} We now consider the process of converting between the coordinate systems represented by two FrameSets. This is a most important operation, as a subsequent example (\secref{ss:registeringimages}) will show, and is illustrated in Figure~\ref{fig:fsalign}. \begin{figure} \begin{center} c+ \includegraphics[width=0.7\textwidth]{sun211_figures/fsalign} c- f+ \includegraphics[width=0.7\textwidth]{sun210_figures/fsalign} f- \caption[Conversion between two FrameSets is performed by establishin a link between a pair of Frames, one from each FrameSet.]{Conversion between two FrameSets is performed by establishing a link between a pair of Frames, one from each FrameSet. If conversion between these two Frames is possible, then a route for converting between the current Frames of both FrameSets can also be found. In practice, there may be many ways of pairing Frames to find the ``missing link'', so the Frames' Domain attribute may be used to narrow the choice.} \label{fig:fsalign} \end{center} \end{figure} c+ Recalling (\secref{ss:framesetasframe}) that a FrameSet will behave like its current Frame when necessary, conversion between two FrameSets is performed using astConvert (\secref{ss:convertingskyframes}), but supplying pointers to FrameSets instead of Frames. The effect of this is to convert between the coordinate systems represented by the current Frames of each FrameSet: c- f+ Recalling (\secref{ss:framesetasframe}) that a FrameSet will behave like its current Frame when necessary, conversion between two FrameSets is performed using AST\_CONVERT (\secref{ss:convertingskyframes}), but supplying pointers to FrameSets instead of Frames. The effect of this is to convert between the coordinate systems represented by the current Frames of each FrameSet: f- c+ \small \begin{terminalv} AstFrameSet *frameseta, *framesetb; ... cvt = astConvert( frameseta, framesetb, "SKY" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER FRAMESETA, FRAMESETB ... CVT = AST_CONVERT( FRAMESETA, FRAMESETB, 'SKY', STATUS ) \end{terminalv} \normalsize f- When using FrameSets, we are presented with considerably more conversion options than when using Frames alone. This is because each current Frame is related to all the other Frames in its respective FrameSet. Therefore, if we can establish a link between any pair of Frames, one from each FrameSet, we can form a complete conversion path between the two current Frames (Figure~\ref{fig:fsalign}). This expanded range of options is, of course, precisely the intention. By connecting Frames together within a FrameSet, we have extended the range of coordinate systems that can be reached from any one of them. We are therefore no longer restricted to converting between Frames with the same Domain value (\secref{ss:framedomains}), but can go \emph{via} a range of intermediate coordinate systems in order to make the connection we require. Transformation between different domains has therefore become possible because, in assembling the FrameSets, we provided the additional information needed to inter-relate them. It is important to appreciate, however, that the choice of ``missing link'' is crucial in determining the conversion that results. Although each FrameSet may be perfectly self-consistent internally, this does not mean that all conversion paths through the combined network of Mappings are equivalent. Quite the contrary in fact: everything depends on where the inter-connecting link between the two FrameSets is made. In practice, there may be a large number of possible pairings of Frames and hence of possible links. Other factors must therefore be used to restrict the choice. These are: \begin{enumerate} \item Not every possible pairing of Frames is legitimate. For example, you cannot convert directly between a basic Frame and a SkyFrame which belong to different classes, so such pairings will be ignored. \item In a similar way, you cannot convert directly between Frames with different Domain values (\secref{ss:framedomains}). If the Domain attribute is used consistently (typically only one Frame in each FrameSet will have a particular Domain value), then this further restricts the choice. c+ \item The third argument of astConvert may then be used to specify c- f+ \item The third argument of AST\_CONVERT may then be used to specify f- explicitly which Domain value the paired Frames should have. You may also supply a comma-separated list of preferences here (see below). \item If the above steps fail to uniquely identify the link, then the first suitable pairing of Frames is used, so that any ambiguity is resolved by the order in which Frames are considered for pairing (see c+ the description of the astConvert function in c- f+ the description of the AST\_CONVERT function in f- \appref{ss:functiondescriptions} for details of the search order).\footnote{If you find that how this ambiguity is resolved actually makes a difference to the conversion that results, then you have probably constructed a FrameSet which lacks internal self-consistency. For example, you might have two Frames representing indistinguishable coordinate systems but inter-related by a non-null Mapping.} \end{enumerate} In the example above we supplied the string ``SKY'' as the third c+ argument of astConvert. This constitutes a request that a pair of Frames with c- f+ argument of AST\_CONVERT. This constitutes a request that a pair of Frames with f- the Domain value SKY (\emph{i.e.}\ representing celestial coordinate systems) should be used to inter-relate the two FrameSets. Note that this does not specify which celestial coordinate system to use, but is a general request that the two FrameSets be inter-related using coordinates on the celestial sphere. Of course, it may be that this request cannot be met because there may not be a celestial coordinate system in both FrameSets. If this is likely to happen, we can supply a list of preferences, or a \emph{domain search path}, c+ as the third argument to astConvert, such as c- f+ as the third argument to AST\_CONVERT, such as f- the following: c+ \small \begin{terminalv} cvt = astConvert( frameseta, framesetb, "SKY,PIXEL,GRID," ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CVT = AST_CONVERT( FRAMESETA, FRAMESETB, 'SKY,PIXEL,GRID,', STATUS ) \end{terminalv} \normalsize f- Now, if the two FrameSets cannot be inter-related using the SKY domain, c+ astConvert will attempt to use the PIXEL domain instead. If this c- f+ AST\_CONVERT will attempt to use the PIXEL domain instead. If this f- also fails, it will try the GRID domain. A blank field in the domain search path (here indicated by the final comma) allows any Domain value to be used. This can be employed as a last resort when all else has failed. c+ If astConvert succeeds in identifying a conversion, it will return a c- f+ If astConvert succeeds in identifying a conversion, it will return a f- pointer to a FrameSet (\secref{ss:framesetsfromconvert}) in which the source and destination Frames are inter-connected by the required Mapping. In this case, of course, these Frames will be the current Frames of the two FrameSets, but in all other respects the returned FrameSet is the same as when converting between Frames. c+ Very importantly, however, astConvert may modify the FrameSets you are converting between. It does this, in order to indicate which pairing of Frames was used to inter-relate them, by changing the Base attribute for each FrameSet so that the Frame used in the pairing becomes its base Frame (\secref{ss:baseandcurrent}). c- f+ Very importantly, however, AST\_CONVERT may modify the FrameSets you are converting between. It does this, in order to indicate which pairing of Frames was used to inter-relate them, by changing the Base attribute for each FrameSet so that the Frame used in the pairing becomes its base Frame (\secref{ss:baseandcurrent}). f- c+ Finally, note that astConvert may also be used to convert between a FrameSet and a Frame, or \emph{vice versa}. If a pointer to a Frame is supplied for either the first or second argument, it will behave like a FrameSet containing only a single Frame. c- f+ Finally, note that AST\_CONVERT may also be used to convert between a FrameSet and a Frame, or \emph{vice versa}. If a pointer to a Frame is supplied for either the first or second argument, it will behave like a FrameSet containing only a single Frame. f- \subsection{\label{ss:registeringimages}Example---Registering Two Images} Consider two images which have been calibrated by attaching FrameSets to them, such that the base Frame of each FrameSet corresponds to the raw data grid coordinates of each image (the GRID domain of \secref{ss:domainconventions}). Suppose, also, that these FrameSets contain an unknown number of other Frames, representing alternative world coordinate systems. What we wish to do is register these two images, such that we can transform from a position in the data grid of one into the corresponding position in the data grid of the other. This is a very practical example because images will typically be calibrated using FrameSets in precisely this way. c+ The first step will probably involve making a copy of both FrameSets (using astCopy---\secref{ss:copyingobjects}), since we will be modifying them. Let ``frameseta'' and ``framesetb'' be pointers to these copies. Since we want to convert between the base Frames of these FrameSets (\emph{i.e.}\ their data grid coordinates), the next step is to make these Frames current. This is simply done by inverting both FrameSets, which interchanges their base and current Frames. astInvert will perform this task: c- f+ The first step will probably involve making a copy of both FrameSets (using AST\_COPY---\secref{ss:copyingobjects}), since we will be modifying them. Let ``frameseta'' and ``framesetb'' be pointers to these copies. Since we want to convert between the base Frames of these FrameSets (\emph{i.e.}\ their data grid coordinates), the next step is to make these Frames current. This is simply done by inverting both FrameSets, which interchanges their base and current Frames. astInvert will perform this task: f- c+ \small \begin{terminalv} astInvert( frameseta ); astInvert( framesetb ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_INVERT( FRAMESETA, STATUS ) CALL AST_INVERT( FRAMESETB, STATUS ) \end{terminalv} \normalsize f- c+ To identify the required conversion, we now use astConvert, supplying a suitable domain search path with which we would like our two images to be registered: c- f+ To identify the required conversion, we now use AST\_CONVERT, supplying a suitable domain search path with which we would like our two images to be registered: f- c+ \small \begin{terminalv} cvt = astConvert( frameseta, framesetb, "SKY,PIXEL,GRID" ); if ( cvt == AST__NULL ) { } else { } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CVT = AST_CONVERT( FRAMESETA, FRAMESETB, 'SKY,PIXEL,GRID', STATUS ) IF ( CVT .EQ. AST__NULL ) THEN ELSE END IF \end{terminalv} \normalsize f- The effects of this are: \begin{enumerate} c+ \item astConvert first attempts to register the two images on the c- f+ \item AST\_CONVERT first attempts to register the two images on the f- celestial sphere (\emph{i.e.}\ using the SKY domain). To do this, it searches for a celestial coordinate system, although not necessarily the same one, attached to each image. If it finds a suitable pair of coordinate systems, it then registers the images by matching corresponding positions on the sky. c+ \item If this fails, astConvert next tries to match positions in the c- f+ \item If this fails, AST\_CONVERT next tries to match positions in the f- PIXEL domain (\secref{ss:framedomains}). If it succeeds, the two images will then be registered so that their corresponding pixel positions correspond. If the PIXEL domain is offset from the data grid (as typically happens in data reduction systems which implement a ``pixel origin''), then this will be correctly accounted for. \item If this also fails, the GRID domain is finally used. This will result in image registration by matching corresponding points in the data grids used by both images. This means they will be aligned so that the first element their data arrays correspond. c+ \item If all of the above fail, astConvert will return the value c- f+ \item If all of the above fail, AST\_CONVERT will return the value f- AST\_\_NULL. Otherwise a pointer to a FrameSet will be returned. \end{enumerate} c+ The resulting ``cvt'' FrameSet may then be used directly (\secref{ss:convertingskyframes}) to convert between positions in the data grid of the first image and corresponding positions in the data grid of the second image. c- f+ The resulting CVT FrameSet may then be used directly (\secref{ss:convertingskyframes}) to convert between positions in the data grid of the first image and corresponding positions in the data grid of the second image. f- To determine which domain was used to achieve registration, we can use the fact that the Base attribute of each FrameSet is set by c+ astConvert to indicate which intermediate Frames were used. We c- f+ AST\_CONVERT to indicate which intermediate Frames were used. We f- can therefore simply invert either FrameSet (to make its base Frame become the current one) and then enquire the Domain value: c+ \small \begin{terminalv} const char *domain; ... astInvert( frameseta ); domain = astGetC( frameseta, "Domain" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CHARACTER * ( 20 ) DOMAIN ... CALL AST_INVERT( FRAMESETA, STATUS ) DOMAIN = AST_GETC( FRAMESETA, 'Domain', STATUS ) \end{terminalv} \normalsize f- If conversion was successful, the result will be one of the strings ``SKY'', ``PIXEL'' or ``GRID''. \subsection{\label{ss:remapframe}Re-Defining a FrameSet Coordinate System} As discussed earlier (\secref{ss:baseandcurrent}), an important application of a FrameSet is to allow coordinate system information to be attached to entities such as images in order to calibrate them. In addition, one of the main objectives of AST is to simplify the propagation of such information through successive stages of data processing, so that it remains consistent with the associated image data. In such a situation, the FrameSet's base Frame would correspond with the image's data grid coordinates and its other Frames (if any) with the various alternative world coordinate systems associated with the image. If the data processing being performed does not change the relationship between the image's data grid coordinates and any of the associated world coordinate systems, then propagation of the WCS information is straightforward and simply involves copying the FrameSet associated with the image. If any of these relationships change, however, then corresponding changes must be made to the way Frames within the FrameSet are inter-related. By far the most common case occurs when the image undergoes some geometrical transformation resulting in ``re-gridding'' on to another data grid, but the same principles can be applied to any re-definition of a coordinate system. To pursue the re-gridding example, we would need to modify our FrameSet to account for the fact that the image's data grid coordinate system (corresponding to the FrameSet's base Frame) has changed. Looking at the steps needed in detail, we might proceed as follows: \begin{enumerate} \item Create a Mapping which represents the relationship between the original data grid coordinate system and the new one. c+ \item Obtain a Frame to represent the new data grid coordinate system (we could re-use the original base Frame here, using astGetFrame to obtain a pointer to it). c- f+ \item Obtain a Frame to represent the new data grid coordinate system (we could re-use the original base Frame here, using AST\_GETFRAME to obtain a pointer to it). f- \item Add the new Frame to the FrameSet, related to the original base Frame by the new Mapping. This Frame now represents the new data grid coordinate system and is correctly related to all the other Frames present.\footnote{This is because any transformation to or from this new Frame must go \emph{via} the base Frame representing the original data grid coordinate system, which we assume was correctly related to all the other Frames present.} \item Remove the original base Frame (representing the old data grid coordinate system). \item Make the new Frame the base Frame and restore the original current Frame. \end{enumerate} The effect of these steps is to change the relationship between the base Frame and all the other Frames present. It is as if a new Mapping has been interposed between the Frame we want to alter and all the other Frames within the FrameSet (Figure~\ref{fig:fsremap}). \begin{figure}[hbtp] \begin{center} c+ \includegraphics[width=0.7\textwidth]{sun211_figures/fsremap} c- f+ \includegraphics[width=0.7\textwidth]{sun210_figures/fsremap} f- \caption[Interposing a Mapping into a FrameSet]{The effect c+ of astRemapFrame is to interpose a Mapping between c- f+ of AST\_REMAPFRAME is to interpose a Mapping between f- a nominated Frame within a FrameSet and the remaining contents of the FrameSet. This effectively ``re-defines'' the coordinate system represented by the affected Frame. It may be used to compensate (say) for geometrical changes made to an associated image. The inter-relationships between all the other Frames within the FrameSet remain unchanged.} \label{fig:fsremap} \end{center} \end{figure} c+ Performing the steps above is rather lengthy, however, so the astRemapFrame function is provided to perform all of these operations in one go. A practical example of its use is given below (\secref{ss:wcsprocessingexample}). c- f+ Performing the steps above is rather lengthy, however, so the AST\_REMAPFRAME function is provided to perform all of these operations in one go. A practical example of its use is given below (\secref{ss:wcsprocessingexample}). f- \subsection{\label{ss:wcsprocessingexample}Example---Binning an Image} c+ As an example of using astRemapFrame, consider a case where the pixels of a 2-dimensional image have been binned 2$\times$2, so as to reduce the image size by a factor of two in each dimension. We must now modify the associated FrameSet to reflect this change to the image. Much the same process would be needed for any other geometrical change the image might undergo. c- f+ As an example of using AST\_REMAPFRAME, consider a case where the pixels of a 2-dimensional image have been binned 2$\times$2, so as to reduce the image size by a factor of two in each dimension. We must now modify the associated FrameSet to reflect this change to the image. Much the same process would be needed for any other geometrical change the image might undergo. f- We first set up a Mapping (a WinMap in this case) which relates the data grid coordinates in the original image to those in the new one: c+ \small \begin{terminalv} AstWinMap *winmap; double ina[ 2 ] = { 0.5, 0.5 }; double inb[ 2 ] = { 2.5, 2.5 }; double outa[ 2 ] = { 0.5, 0.5 }; double outb[ 2 ] = { 1.5, 1.5 }; ... winmap = astWinMap( 2, ina, inb, outa, outb, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER WINMAP DOUBLE PRECISION INA( 2 ), INB( 2 ), OUTA( 2 ), OUTB( 2 ) DATA INA / 0.5D0, 0.5D0 / DATA INB / 2.5D0, 2.5D0 / DATA OUTA / 0.5D0, 0.5D0 / DATA OUTB / 1.5DO, 1.5DO / ... WINMAP = AST_WINMAP( 2, INA, INB, OUTA, OUTB, ' ', STATUS ) \end{terminalv} \normalsize f- c+ Here, we have simply set up arrays containing the data grid coordinates of the bottom left and top right corners of the first element in the output image (``outa'' and ``outb'') and the corresponding coordinates in the input image (``ina'' and ``inb''). astWinMap then creates a WinMap which performs the required transformation. We do not need to know the size of the image. c- f+ Here, we have simply set up arrays containing the data grid coordinates of the bottom left and top right corners of the first element in the output image (OUTA and OUTB) and the corresponding coordinates in the input image (INA and INB). AST\_WINMAP then creates a WinMap which performs the required transformation. We do not need to know the size of the image. f- c+ We can then pass this WinMap to astRemapFrame. This modifies the relationship between our FrameSet's base Frame and the other Frames in the FrameSet, so that the base Frame represents the data grid coordinate system of the new image rather than the old one: c- f+ We can then pass this WinMap to AST\_REMAPFRAME. This modifies the relationship between our FrameSet's base Frame and the other Frames in the FrameSet, so that the base Frame represents the data grid coordinate system of the new image rather than the old one: f- c+ \small \begin{terminalv} AstFrameSet *frameset; ... astRemapFrame( frameset, AST__BASE, winmap ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER FRAMESET ... CALL AST_REMAPFRAME( FRAMESET, AST__BASE, WINMAP, STATUS ) \end{terminalv} \normalsize f- Any other coordinate systems described by the FrameSet, no matter how many of these there might be, are now correctly associated with the new image. \subsection{\label{ss:framesetintegrity}Maintaining the Integrity of FrameSets} When constructing a FrameSet, you are provided with a framework into which you can place any combination of Frames and Mappings that you wish. There are relatively few constraints on this process and no checks are performed to see whether the FrameSet you construct makes physical sense. It is quite possible, for example, to construct a FrameSet containing two identical SkyFrames which are inter-related by a non-unit Mapping. AST will not object if you do this, but it makes no sense, because applying a non-unit Mapping to any set of celestial coordinates cannot yield positions that are still in the original coordinate system. If you use such a FrameSet to perform coordinate conversions, you are likely to get unpredictable results because the information in the FrameSet is corrupt. It is, of course, your responsibility as a programmer to ensure the validity of any information which you insert into a FrameSet. Normally, this is straightforward and simply consists of formulating your problem correctly (a diagram can often help to clarify how coordinate systems are inter-related) and writing the appropriate bug-free code to construct the FrameSet. However, once you start to modify an existing FrameSet, there are new opportunities for corrupting it! c+ Consider, for example, a FrameSet whose current Frame is a SkyFrame. We can set a new value for this SkyFrame's Equinox attribute simply by using astSet on the FrameSet, as follows: c- f+ Consider, for example, a FrameSet whose current Frame is a SkyFrame. We can set a new value for this SkyFrame's Equinox attribute simply by using AST\_SET on the FrameSet, as follows: f- c+ \small \begin{terminalv} astSet( frameset, "Equinox=J2010" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SET( FRAMESET, 'Equinox=J2010', STATUS ) \end{terminalv} \normalsize f- The effect of this will be to change the celestial coordinate system which the current Frame represents. You can see, however, that this has the potential to make the FrameSet corrupt unless corresponding changes are also made to the Mapping which relates this SkyFrame to the other Frames within the FrameSet. In fact, it is a general rule that any change to a FrameSet which affects its current Frame can potentially require corresponding changes to the FrameSet's Mappings in order to maintain its overall integrity. c+ Fortunately, once you have stored valid information in a FrameSet, AST will look after these details for you automatically, so that the FrameSet's integrity is maintained. In the example above, it would do this by appropriately re-mapping the current Frame (as if astRemapFrame had been used---\secref{ss:remapframe}) in response to the use of astSet. One way of illustrating this process is as follows: c- f+ Fortunately, once you have stored valid information in a FrameSet, AST will look after these details for you automatically, so that the FrameSet's integrity is maintained. In the example above, it would do this by appropriately re-mapping the current Frame (as if AST\_REMAPFRAME had been used---\secref{ss:remapframe}) in response to the use of AST\_SET. One way of illustrating this process is as follows: f- c+ \small \begin{terminalv} AstSkyFrame *skyframe; ... skyframe = astSkyFrame( "" ); frameSet = astFrameSet( skyframe ); astAddFrame( frameset, 1, astUnitMap( 2, "" ), skyframe ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER SKYFRAME ... SKYFRAME = AST_SKYFRAME( ' ', STATUS ) FRAMESET = AST_FRAMESET( SKYFRAME, STATUS ) CALL AST_ADDFRAME( FRAMESET, 1, AST_UNITMAP( 2, ' ', STATUS ) : SKYFRAME, STATUS ) \end{terminalv} \normalsize f- This constructs a trivial FrameSet whose base and current Frames are both the same SkyFrame connected by a UnitMap. You can think of this as a ``pipe'' connecting two coordinate systems. At present, these two systems represent identical ICRS coordinates, so the FrameSet implements a unit Mapping. We can change the coordinate system on the current end of this pipe as follows: c+ \small \begin{terminalv} astSet( frameset, "System=Ecliptic, Equinox=J2010" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SET( FRAMESET, 'System=Ecliptic, Equinox=J2010', STATUS ) \end{terminalv} \normalsize f- and the Mapping which the FrameSet implements would change accordingly. To change the coordinate system on the base end of the pipe, we might use: c+ \small \begin{terminalv} astInvert( frameset ); astSet( frameset, "System=Galactic" ); astInvert( frameset ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_INVERT( FRAMESET ) CALL AST_SET( FRAMESET, 'System=Galactic', STATUS ) CALL AST_INVERT( FRAMESET ) \end{terminalv} \normalsize f- The FrameSet would then convert between galactic and ecliptic coordinates. c+ Note that astSet is not the only function which has this effect: astClear behaves similarly, as also does astPermAxes (\secref{ss:permutingaxes}). If you need to circumvent this mechanism for any reason, this can be done by going behind the scenes and obtaining a pointer directly to the Frame you wish to modify. Consider the following, for example: c- f+ Note that AST\_SET is not the only function which has this effect: AST\_CLEAR behaves similarly, as also does AST\_PERMAXES (\secref{ss:permutingaxes}). If you need to circumvent this mechanism for any reason, this can be done by going behind the scenes and obtaining a pointer directly to the Frame you wish to modify. Consider the following, for example: f- c+ \small \begin{terminalv} skyframe = astGetFrame( frameset, AST__CURRENT ); astSet( skyframe, "Equinox=J2010" ); skyframe = astAnnul( skyframe ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} SKYFRAME = AST_GETFRAME( FRAMESET, AST__CURRENT, STATUS ) CALL AST_SET( SKYFRAME, 'Equinox=J2010', STATUS ) CALL AST_ANNUL( SKYFRAME, STATUS ) \end{terminalv} \normalsize f- c+ Here, astSet is applied to the SkyFrame pointer rather than the FrameSet pointer, so the usual checks on FrameSet integrity do not occur. The SkyFrame's Equinox attribute will therefore be modified without any corresponding change to the FrameSet's Mappings. In this case you must take responsibility yourself for maintaining the FrameSet's integrity, perhaps through appropriate use of astRemapFrame. c- f+ Here, AST\_SET is applied to the SkyFrame pointer rather than the FrameSet pointer, so the usual checks on FrameSet integrity do not occur. The SkyFrame's Equinox attribute will therefore be modified without any corresponding change to the FrameSet's Mappings. In this case you must take responsibility yourself for maintaining the FrameSet's integrity, perhaps through appropriate use of AST\_REMAPFRAME. f- \subsection{Merging FrameSets} As well as adding individual Frames to a FrameSet (\secref{ss:addingframes}), it is also possible to add complete sets of inter-related Frames which are contained within another FrameSet. This, of course, corresponds to the process of merging two FrameSets (Figure~\ref{fig:fsmerge}). \begin{figure}[hbtp] \begin{center} c+ \includegraphics[width=0.7\textwidth]{sun211_figures/fsmerge} c- f+ \includegraphics[width=0.7\textwidth]{sun210_figures/fsmerge} f- \caption[Two FrameSets in the process of being merged.]{Two FrameSets in the process of being merged using c+ astAddFrame. FrameSet~B is being added to FrameSet~A by supplying a c- f+ AST\_ADDFRAME. FrameSet~B is being added to FrameSet~A by supplying a f- new Mapping which inter-relates a nominated Frame in A (here number~1) and the current Frame of B. In the merged FrameSet, the Frames contributed by B will be re-numbered to become Frames~4, 5 and 6. The base Frame will remain unchanged, but the current Frame of B becomes the new current Frame. Note that FrameSet~B itself is not altered by this process.} \label{fig:fsmerge} \end{center} \end{figure} c+ This process is performed by adding one FrameSet to another using astAddFrame, in much the same manner as when adding a new Frame to an existing FrameSet (\secref{ss:addingframes}). It is simply a matter of providing a FrameSet pointer, instead of a Frame pointer, for the 4th argument. In performing the merger you must, as usual, supply a Mapping, but in this case the Mapping should relate the current Frame of the FrameSet being added to one of the Frames already present. For example, you might perform the merger shown in Figure~\ref{fig:fsmerge} as follows: c- f+ This process is performed by adding one FrameSet to another using AST\_ADDFRAME, in much the same manner as when adding a new Frame to an existing FrameSet (\secref{ss:addingframes}). It is simply a matter of providing a FrameSet pointer, instead of a Frame pointer, for the 4th argument. In performing the merger you must, as usual, supply a Mapping, but in this case the Mapping should relate the current Frame of the FrameSet being added to one of the Frames already present. For example, you might perform the merger shown in Figure~\ref{fig:fsmerge} as follows: f- c+ \small \begin{terminalv} AstMapping *mapping; ... astAddFrame( frameseta, 1, mapping, framesetb ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER MAPPING ... CALL AST_ADDFRAME( FRAMESETA, 1, MAPPING, FRAMESETB, STATUS ) \end{terminalv} \normalsize f- c+ The Frames acquired by ``frameseta'' from the FrameSet being added (``framesetb'') are re-numbered so that they retain their original order and follow on consecutively after the Frames that were already present, whose indices remain unchanged. The base Frame of ``frameseta'' remains unchanged, but the current Frame of ``framesetb'' becomes its new current Frame. All the inter-relationships between Frames in both FrameSets remain in place and are preserved in the merged FrameSet. c- f+ The Frames acquired by FRAMESETA from the FrameSet being added (FRAMESETB) are re-numbered so that they retain their original order and follow on consecutively after the Frames that were already present, whose indices remain unchanged. The base Frame of FRAMESETA remains unchanged, but the current Frame of FRAMESETB becomes its new current Frame. All the inter-relationships between Frames in both FrameSets remain in place and are preserved in the merged FrameSet. f- c+ Note that while this process modifies the first FrameSet (``frameseta''), it leaves the original contents of the one being added (``framesetb'') unchanged. c- f+ Note that while this process modifies the first FrameSet (FRAMESETA), it leaves the original contents of the one being added (FRAMESETB) unchanged. f- %\cleardoublepage %\section{\label{ss:searching}TBW - Searching for Coordinate Systems} \cleardoublepage \section{\label{ss:channels}Saving and Restoring Objects (Channels)} Facilities are provided by the AST library for performing input and output (I/O) with any kind of Object. This means it is possible to write any Object into various external representations for storage, and then to read these representations back in, so as to restore the original Object. Typically, an Object would be written by one program and read back in by another. We refer to ``external representations'' in the plural because AST is designed to function independently of any particular data storage system. This means that Objects may need converting into a number of different external representations in order to be compatible with (say) the astronomical data storage system in which they will reside. In this section, we discuss the basic I/O facilities which support external representations based on a textual format referred to as the AST ``native format''. These are implemented using a new kind of Object---a Channel. We will examine later how to use other representations, based on an XML format or on the use of FITS headers, for storing Objects. These are implemented using more specialised forms of Channel called XmlChan (\secref{ss:xmlchan}) and FitsChan (\secref{ss:nativefits}). \subsection{The Channel Model} c+ The best way to start thinking about a Channel is like a C file stream, and to think of the process of creating a Channel as that of opening a file and obtaining a FILE pointer. Subsequently, you can read and write Objects \emph{via} the Channel. c- f+ The best way to start thinking about a Channel is like a Fortran I/O unit (also represented by an integer, as it happens) and to think of the process of creating a Channel as the combined process of allocating a unit number and attaching it to a file by opening the file on that unit. Subsequently, you can read and write Objects \emph{via} the Channel. f- c+ This analogy is not quite perfect, however, because a Channel has, in principle, two ``files'' attached to it. One is used when reading, and the other when writing. These are termed the Channel's \emph{source} and \emph{sink} respectively. In practice, the source and sink may both be the same, in which case the analogy with the C file stream is correct, but this need not always be so. It is not necessarily so with the basic Channel, as we will now see (\secref{ss:creatingachannel}). c- f+ This analogy is not quite perfect, however, because a Channel has, in principle, two ``files'' attached to it. One is used when reading, and the other when writing. These are termed the Channel's \emph{source} and \emph{sink} respectively. In practice, the source and sink may both be the same, in which case the analogy with the Fortran I/O unit is correct, but this need not always be so. It is not necessarily so with the basic Channel, as we will now see (\secref{ss:creatingachannel}). f- \subsection{\label{ss:creatingachannel}Creating a Channel} c+ The process of creating a Channel is straightforward. As you might expect, it uses the constructor function astChannel: \small \begin{terminalv} #include "ast.h" AstChannel *channel; ... channel = astChannel( NULL, NULL, "" ); \end{terminalv} \normalsize The first two arguments to astChannel specify the external source and sink that the Channel is to use. There arguments are pointers to C functions and we will examine their use in more detail later (\secref{ss:channelsource} and \secref{ss:channelsink}). c- f+ The process of creating a Channel is straightforward. As you might expect, it uses the constructor function AST\_CHANNEL: \small \begin{terminalv} INCLUDE 'AST_PAR' INTEGER CHANNEL, STATUS STATUS = 0 ... CHANNEL = AST_CHANNEL( AST_NULL, AST_NULL, ' ', STATUS ) \end{terminalv} \normalsize The first two arguments to AST\_CHANNEL specify the external source and sink that the Channel is to use. There arguments are the names of Fortran subroutines and we will examine their use in more detail later (\secref{ss:channelsource} and \secref{ss:channelsink}). f- c+ In this very simple example we have supplied NULL pointers for both the source and sink functions. This requests the default behaviour, which means that textual input will be read from the program's standard input stream (typically, this means your keyboard) while textual output will go to the standard output stream (typically appearing on your screen). On UNIX systems, of course, either of these streams can easily be redirected to files. This default behaviour can be changed by assigning values to the Channel's SinkFile and/or SourceFile attributes. These attributes specify the paths to text files that are to be used in place of the standard input and output streams. c- f+ In this very simple example we have supplied the name of the null routine AST\_NULL\footnote{Note that AST\_NULL (one underscore) is a routine name and is distinct from AST\_\_NULL (two underscores) which is a null Object pointer. Since we are passing the name of one routine to another routine, AST\_NULL would normally have to appear in a Fortran EXTERNAL statement. In this example, however, a suitable statement is already present in the AST\_PAR include file.} for both the source and sink routines. This requests the default behaviour, which means that textual input will be read from the program's standard input stream (typically, this means your keyboard) while textual output will go to the standard output stream (typically appearing on your screen). On UNIX systems, of course, either of these streams can easily be redirected to files. f- \subsection{\label{ss:writingtoachannel}Writing Objects to a Channel} c+ The process of saving Objects is very straightforward. You can simply write any Object to a Channel using the astWrite function, as follows: \small \begin{terminalv} int nobj; AstObject *object; ... nobj = astWrite( channel, object ); \end{terminalv} \normalsize c- f+ The process of saving Objects is very straightforward. You can simply write any Object to a Channel using the AST\_WRITE function, as follows: \small \begin{terminalv} INTEGER NOBJ, OBJECT ... NOBJ = AST_WRITE( CHANNEL, OBJECT, STATUS ) \end{terminalv} \normalsize f- The effect of this will be to produce a textual description of the Object which will appear, by default, on your program's standard output stream. Any class of Object may be converted into text in this way. c+ astWrite returns a count of the number of Objects written. Usually, this will be one, unless the Object supplied cannot be represented. With a basic Channel all Objects can be represented, so a value of one will always be returned unless there has been an error. We will see later, however, that more specialised forms of Channel may impose restrictions on the kind of Object you can write (\secref{ss:foreignfitslimitations}). In such cases, astWrite may return zero to indicate that the Object was not acceptable. c- f+ AST\_WRITE returns a count of the number of Objects written. Usually, this will be one, unless the Object supplied cannot be represented. With a basic Channel all Objects can be represented, so a value of one will always be returned unless there has been an error. We will see later, however, that more specialised forms of Channel may impose restrictions on the kind of Object you can write (\secref{ss:foreignfitslimitations}). In such cases, AST\_WRITE may return zero to indicate that the Object was not acceptable. f- \subsection{\label{ss:readingfromachannel}Reading Objects from a Channel} Before discussing the format of the output produced above (\secref{ss:writingtoachannel}), let us consider how to read it back, so as to reconstruct the original Object. Naturally, we would first need to save the output in a file. We can do that either by using the SinkFile attribute, or (on UNIX systems), by redirecting standard output to a file using a shell command like: \small \begin{terminalv} program1 >file \end{terminalv} \normalsize c+ Within a subsequent program, we can read this Object back in by using the astRead function, having first created a suitable Channel: \small \begin{terminalv} object = astRead( channel ); \end{terminalv} \normalsize c- f+ Within a subsequent program, we can read this Object back in by using the AST\_READ function, having first created a suitable Channel: \small \begin{terminalv} OBJECT = AST_READ( CHANNEL, STATUS ) \end{terminalv} \normalsize f- By default, this function will read from the standard input stream (the default source for a basic Channel), so we would need to ensure that our second program reads its input from the file in which the Object description is stored. On UNIX systems, we could again use a shell redirection command such as: \small \begin{terminalv} program2 $ family of functions: \small \begin{terminalv} int ok; ... ok = astIsAFrame( object ); \end{terminalv} \normalsize c- f+ The pointer returned by AST\_READ (\secref{ss:readingfromachannel}) could identify any class of Object---this is determined entirely by the external data being read. If it is necessary to test for a particular class (say a Frame), this may be done as follows using the appropriate member of the AST\_ISA$<$CLASS$>$ family of functions: \small \begin{terminalv} LOGICAL OK ... OK = AST_ISAFRAME( OBJECT, STATUS ) \end{terminalv} \normalsize f- Note, however, that this will accept any Frame, so would be equally happy with a basic Frame or a SkyFrame. An alternative validation strategy would be to obtain the value of the Object's Class attribute and then test this character string, as follows: c+ \small \begin{terminalv} #include ... ok = !strcmp( astGetC( object, "Class" ), "Frame" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} OK = AST_GETC( OBJECT, 'Class', STATUS ) .EQ. 'Frame' \end{terminalv} \normalsize f- This would only accept a basic Frame and would reject a SkyFrame. \subsection{Storing an ID String with an Object} Occasionally, you may want to store a number of Objects and later retrieve them and use each for a different purpose. If the Objects are of the same class, you cannot use the Class attribute to distinguish them when you read them back (\emph{c.f.}~\secref{ss:validatinginput}). Although relying on the order in which they are stored is a possible solution, this becomes complicated if some of the Objects are optional and may not always be present. It also makes extending your data format in future more difficult. To help with this, every AST Object has an ID attribute and an Ident attribute, both of which allows you, in effect, to attach a textual identification label to it. You simply set the ID or Ident attribute before writing the Object: c+ \small \begin{terminalv} astSet( object, "ID=Calibration" ); nobj = astWrite( channel, object ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SET( OBJECT, 'ID=Calibration', STATUS ) NOBJ = AST_WRITE( CHANNEL, OBJECT, STATUS ) \end{terminalv} \normalsize f- You can then test its value after you read the Object back: c+ \small \begin{terminalv} object = astRead( channel ); if ( !strcmp( astGetC( object, "ID" ), "Calibration" ) ) { } else { } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} OBJECT = AST_READ( CHANNEL, STATUS ) IF ( AST_GETC( OBJECT, 'ID', STATUS ) .EQ. 'Calibration' ) THEN ELSE END IF \end{terminalv} \normalsize f- The only difference between the ID and Ident attributes is that the ID attribute is unique to a particular Object and is lost if, for example, you make a copy of the Object. The Ident attrubute, on the other hand, is transferred to the new Object when a copy is made. Consequently, it is safest to set the value of the ID attribute immediately before you perform the write. \subsection{\label{ss:textualoutputformat}The Textual Output Format} Let us now examine the format of the textual output produced by writing an Object to a basic Channel (\secref{ss:writingtoachannel}). To give a concrete example, suppose the Object in question is a SkyFrame, written out as follows: c+ \small \begin{terminalv} AstSkyFrame *skyframe; ... nobj = astWrite( channel, skyframe ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER SKYFRAME ... NOBJ = AST_WRITE( CHANNEL, SKYFRAME, STATUS ) \end{terminalv} \normalsize f- The output should then look like the following: \small \begin{terminalv} Begin SkyFrame # Description of celestial coordinate system # Title = "FK4 Equatorial Coordinates, no E-terms, Mean Equinox B1950.0, Epoch B1958.0" # Title of coordinate system Naxes = 2 # Number of coordinate axes # Domain = "SKY" # Coordinate system domain # Lbl1 = "Right Ascension" # Label for axis 1 # Lbl2 = "Declination" # Label for axis 2 # Uni1 = "hh:mm:ss.s" # Units for axis 1 # Uni2 = "ddd:mm:ss" # Units for axis 2 # Dir1 = 0 # Plot axis 1 in reverse direction (hint) Ax1 = # Axis number 1 Begin SkyAxis # Celestial coordinate axis End SkyAxis Ax2 = # Axis number 2 Begin SkyAxis # Celestial coordinate axis End SkyAxis IsA Frame # Coordinate system description System = "FK4-NO-E" # Celestial coordinate system type Epoch = 1958 # Besselian epoch of observation # Eqnox = 1950 # Besselian epoch of mean equinox End SkyFrame \end{terminalv} \normalsize c+ You will notice that this output is designed both for a human reader, in that it is formatted, and also to be read back by a computer in order to reconstruct the SkyFrame. In fact, this is precisely the way that astShow works (\secref{ss:displayingobjects}), this function being roughly equivalent to the following use of a Channel: \small \begin{terminalv} channel = astChannel( NULL, NULL, "" ); (void) astWrite( channel, object ); channel = astAnnul( channel ); \end{terminalv} \normalsize c- f+ You will notice that this output is designed both for a human reader, in that it is formatted, and also to be read back by a computer in order to reconstruct the SkyFrame. In fact, this is precisely the way that AST\_SHOW works (\secref{ss:displayingobjects}), this routine being roughly equivalent to the following use of a Channel: \small \begin{terminalv} CHANNEL = AST_CHANNEL( AST_NULL, AST_NULL, ' ', STATUS ) NOBJ = AST_WRITE( CHANNEL, OBJECT, STATUS ) CALL AST_ANNUL( CHANNEL, STATUS ) \end{terminalv} \normalsize f- c+ Some lines of the output start with a ``\verb?#?'' comment character, which turns the rest of the line into a comment. These lines will be ignored when read back in by astRead. They typically contain default values, or values that can be derived in some way from the other data present, so that they do not actually need to be stored in order to reconstruct the original Object. They are provided purely for human information. The same comment character is also used to append explanatory comments to most output lines. c- f+ Some lines of the output start with a ``\verb?#?'' comment character, which turns the rest of the line into a comment. These lines will be ignored when read back in by AST\_READ. They typically contain default values, or values that can be derived in some way from the other data present, so that they do not actually need to be stored in order to reconstruct the original Object. They are provided purely for human information. The same comment character is also used to append explanatory comments to most output lines. f- It is not sensible to attempt a complete description of this output format because every class of Object is potentially different and each can define how its own data should be represented. However, there are some basic rules, which mean that the following common features will usually be present: \begin{enumerate} \item Each Object is delimited by matching ``Begin'' and ``End'' lines, which also identify the class of Object involved. \item Within each Object description, data values are represented by a simple ``keyword~$=$~value'' syntax, with one value to a line. \item Lines beginning ``IsA'' are used to mark the divisions between data belonging to different levels in the class hierarchy (\appref{ss:classhierarchy}). Thus, ``IsA~Frame'' marks the end of data associated with the Frame class and the start of data associated with some derived class (a SkyFrame in the above example). ``IsA'' lines may be omitted if associated data values are absent and no confusion arises. \item Objects may contain other Objects as data. This is indicated by an absent value, with the description of the data Object following on subsequent lines. \item Indentation is used to clarify the overall structure. \end{enumerate} Beyond these general principles, the best guide to what a particular line of output represents will generally be the comment which accompanies it together with a general knowledge of the class of Object being described. \subsection{\label{ss:controllingchanneloutput}Controlling the Amount of Output} c+ It is not always necessary for the output from astWrite (\secref{ss:writingtoachannel}) to be human-readable, so a Channel has attributes that allow the amount of detail in the output to be controlled. c- f+ It is not always necessary for the output from AST\_WRITE (\secref{ss:writingtoachannel}) to be human-readable, so a Channel has attributes that allow the amount of detail in the output to be controlled. f- The first of these is the integer attribute Full, which controls the extent to which optional, commented out, output lines are produced. By default, Full is zero, and this results in the standard style of output (\secref{ss:textualoutputformat}) where default values that may be helpful to humans are included. To suppress these optional lines, Full should be set to $-$1. This is most conveniently done when the Channel is created, so that: c+ \small \begin{terminalv} channel = astChannel( NULL, NULL, "Full=-1" ); (void) astWrite( channel, skyframe ); channel = astAnnul( channel ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CHANNEL = AST_CHANNEL( AST_NULL, AST_NULL, 'Full=-1', STATUS ) NOBJ = AST_WRITE( CHANNEL, SKYFRAME, STATUS ) CALL AST_ANNUL( CHANNEL, STATUS ) \end{terminalv} \normalsize f- would result in output containing only the essential information, such as: \small \begin{terminalv} Begin SkyFrame # Description of celestial coordinate system Naxes = 2 # Number of coordinate axes Ax1 = # Axis number 1 Begin SkyAxis # Celestial coordinate axis End SkyAxis Ax2 = # Axis number 2 Begin SkyAxis # Celestial coordinate axis End SkyAxis IsA Frame # Coordinate system description System = "FK4-NO-E" # Celestial coordinate system type Epoch = 1958 # Besselian epoch of observation End SkyFrame \end{terminalv} \normalsize In contrast, setting Full to $+$1 will result in additional output lines which will reveal every last detail of the Object's construction. Often this will be rather more than you want, especially for more complex Objects, but it can sometimes help when debugging programs. This is how a SkyFrame appears at this level of detail: \small \begin{terminalv} Begin SkyFrame # Description of celestial coordinate system # RefCnt = 1 # Count of active Object pointers # Nobj = 1 # Count of active Objects in same class IsA Object # Astrometry Object # Nin = 2 # Number of input coordinates # Nout = 2 # Number of output coordinates # Invert = 0 # Mapping not inverted # Fwd = 1 # Forward transformation defined # Inv = 1 # Inverse transformation defined # Report = 0 # Don't report coordinate transformations IsA Mapping # Mapping between coordinate systems # Title = "FK4 Equatorial Coordinates, no E-terms, Mean Equinox B1950.0, Epoch B1958.0" # Title of coordinate system Naxes = 2 # Number of coordinate axes # Domain = "SKY" # Coordinate system domain # Lbl1 = "Right Ascension" # Label for axis 1 # Lbl2 = "Declination" # Label for axis 2 # Sym1 = "RA" # Symbol for axis 1 # Sym2 = "Dec" # Symbol for axis 2 # Uni1 = "hh:mm:ss.s" # Units for axis 1 # Uni2 = "ddd:mm:ss" # Units for axis 2 # Dig1 = 7 # Individual precision for axis 1 # Dig2 = 7 # Individual precision for axis 2 # Digits = 7 # Default formatting precision # Fmt1 = "hms.1" # Format specifier for axis 1 # Fmt2 = "dms" # Format specifier for axis 2 # Dir1 = 0 # Plot axis 1 in reverse direction (hint) # Dir2 = 1 # Plot axis 2 in conventional direction (hint) # Presrv = 0 # Don't preserve target axes # Permut = 1 # Axes may be permuted to match # MinAx = 2 # Minimum number of axes to match # MaxAx = 2 # Maximum number of axes to match # MchEnd = 0 # Match initial target axes # Prm1 = 1 # Axis 1 not permuted # Prm2 = 2 # Axis 2 not permuted Ax1 = # Axis number 1 Begin SkyAxis # Celestial coordinate axis # RefCnt = 1 # Count of active Object pointers # Nobj = 2 # Count of active Objects in same class IsA Object # Astrometry Object # Label = "Angle on Sky" # Axis Label # Symbol = "delta" # Axis symbol # Unit = "ddd:mm:ss" # Axis units # Digits = 7 # Default formatting precision # Format = "dms" # Format specifier # Dirn = 1 # Plot in conventional direction IsA Axis # Coordinate axis # Format = "dms" # Format specifier # IsLat = 0 # Longitude axis (not latitude) # AsTime = 0 # Display values as angles (not times) End SkyAxis Ax2 = # Axis number 2 Begin SkyAxis # Celestial coordinate axis # RefCnt = 1 # Count of active Object pointers # Nobj = 2 # Count of active Objects in same class IsA Object # Astrometry Object # Label = "Angle on Sky" # Axis Label # Symbol = "delta" # Axis symbol # Unit = "ddd:mm:ss" # Axis units # Digits = 7 # Default formatting precision # Format = "dms" # Format specifier # Dirn = 1 # Plot in conventional direction IsA Axis # Coordinate axis # Format = "dms" # Format specifier # IsLat = 0 # Longitude axis (not latitude) # AsTime = 0 # Display values as angles (not times) End SkyAxis IsA Frame # Coordinate system description System = "FK4-NO-E" # Celestial coordinate system type Epoch = 1958 # Besselian epoch of observation # Eqnox = 1950 # Besselian epoch of mean equinox End SkyFrame \end{terminalv} \normalsize \subsection{\label{ss:channelcommenting}Controlling Commenting} Another way of controlling output from a Channel is \emph{via} the boolean (integer) Comment attribute, which controls whether comments are appended to describe the purpose of each value. Comment has the value 1 by default but, if set to zero, will suppress these comments. This is normally appropriate only if you wish to minimise the amount of output, for example: c+ \small \begin{terminalv} astSet( channel, "Full=-1, Comment=0" ); nobj = astWrite( channel, skyframe ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SET( CHANNEL, 'Full=-1, Comment=0', STATUS ) NOBJ = AST_WRITE( CHANNEL, SKYFRAME, STATUS ) \end{terminalv} \normalsize f- might result in the following more compact output: \small \begin{terminalv} Begin SkyFrame Naxes = 2 Ax1 = Begin SkyAxis End SkyAxis Ax2 = Begin SkyAxis End SkyAxis IsA Frame System = "FK4-NO-E" Epoch = 1958 End SkyFrame \end{terminalv} \normalsize \subsection{Editing Textual Output} c+ The safest advice about editing the textual output from astWrite (or astShow) is ``don't!''---unless you know what you are doing. c+ f+ The safest advice about editing the textual output from AST\_WRITE (or AST\_SHOW) is ``don't!''---unless you know what you are doing. f- Having given that warning, however, it is sometimes possible to make changes to the text, or even to write entire Object descriptions from scratch, and to read the results back in to construct new Objects. Normally, simple changes to numerical values are safest, but be aware that this is a back door method of creating Objects, so you are on your own! There are a number of potential pitfalls. In particular: \begin{itemize} c+ \item astRead is intended for retrieving data written by astWrite and not for reading data input by humans. As such, the data validation provided is very limited and is certainly not foolproof. This makes it quite easy to construct Objects that are internally inconsistent by this means. In contrast, the normal programming interface incorporates numerous checks designed to make it impossible to construct invalid Objects. You should not necessarily think you have found a bug if your changes to an Object's textual description fail to produce the results you expected! c- f+ \item AST\_READ is intended for retrieving data written by AST\_WRITE and not for reading data input by humans. As such, the data validation provided is very limited and is certainly not foolproof. This makes it quite easy to construct Objects that are internally inconsistent by this means. In contrast, the normal programming interface incorporates numerous checks designed to make it impossible to construct invalid Objects. You should not necessarily think you have found a bug if your changes to an Object's textual description fail to produce the results you expected! f- \item In many instances the names associated with values in textual output will correspond with Object attributes. Sometimes, however, these names may differ from the attribute name. This is mainly because of length restrictions imposed by other common external formats, such as FITS headers. Some of the names used do not correspond with attributes at all. \item It is safest to change single numerical or string values. Beware of changing the size or shape of Objects (\emph{e.g.}\ the number of axes in a Frame). Often, these values must match others stored elsewhere within the Object and changing them in a haphazard fashion will not produce useful results. \item Be wary about un-commenting default values. Sometimes this will work, but often these values are derived from other Objects stored more deeply in the structure and the proper place to insert a new value is not where the default itself appears. \end{itemize} \subsection{\label{ss:mixingchanneltext}Mixing Objects with other Text} c+ By default, when you use astRead to read from a basic Channel (\secref{ss:readingfromachannel}), it is assumed that you are reading a stream of text containing only AST Objects, which follow each other end-to-end. If any extraneous input data are encountered which do not appear to form part of the textual description of an Object, then an error will result. In particular, the first input line must identify the start of an Object description, so you cannot start reading half way through an Object. c- f+ By default, when you use AST\_READ to read from a basic Channel (\secref{ss:readingfromachannel}), it is assumed that you are reading a stream of text containing only AST Objects, which follow each other end-to-end. If any extraneous input data are encountered which do not appear to form part of the textual description of an Object, then an error will result. In particular, the first input line must identify the start of an Object description, so you cannot start reading half way through an Object. f- Sometimes, however, you may want to store AST Object descriptions intermixed with other textual data. You can do this by setting the Channel's boolean (integer) Skip attribute to 1. This will cause every read to skip over extraneous data until the start of a new AST Object description, if any, is found. So long as your other data do not mimic the appearance of an AST Object description, the two sets of data can co-exist. c+ For example, by setting Skip to 1, the following complete C program will read all the AST Objects whose descriptions appear in the source of this document, ignoring the other text. astShow is used to display those found: \small \begin{terminalv} #include "ast.h" main() { AstChannel *channel; AstObject *object; channel = astChannel( NULL, NULL, "Skip=1" ); while ( ( object = astRead( channel ) ) != AST__NULL ) { astShow( object ); object = astAnnul( object ); } channel = astAnnul( channel ); } \end{terminalv} \normalsize c- f+ For example, by setting Skip to 1, the following complete Fortran program will read all the AST Objects whose descriptions appear in the source of this document, ignoring the other text. AST\_SHOW is used to display those found: \small \begin{terminalv} INCLUDE 'AST_PAR' INTEGER CHANNEL, OBJECT, STATUS STATUS = 0 CHANNEL = AST_CHANNEL( AST_NULL, AST_NULL, 'Skip=1', STATUS ) 1 OBJECT = AST_READ( CHANNEL, STATUS ) IF ( OBJECT .NE. AST__NULL ) THEN CALL AST_SHOW( OBJECT, STATUS ) CALL AST_ANNUL( OBJECT, STATUS ) GO TO 1 END IF CALL AST_ANNUL( CHANNEL, STATUS ) END \end{terminalv} \normalsize f- \subsection{\label{ss:channelsource}Reading Objects from Files} Thus far, we have only considered the default behaviour of a Channel in reading and writing Objects through a program's standard input and output streams. We will now consider how to access Objects stored in files more directly. The simple approach is to use the SinkFile and SourceFile attributes of the Channel. For instance, the following will read a pair of Objects from a text file called ``fred.txt'': c+ \small \begin{terminalv} astSet( channel, "SourceFile=fred.txt" ); obj1 = astRead( channel ); obj2 = astRead( channel ); astClear( channel, "SourceFile" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SET( CHANNEL, 'SourceFile=fred.txt', STATUS ) OBJ1 = AST_READ( CHANNEL, STATUS ) OBJ2 = AST_READ( CHANNEL, STATUS ) CALL AST_CLEAR( CHANNEL, 'SourceFile', STATUS ) \end{terminalv} \normalsize f- Note, the act of clearing the attribute tells AST that no more Objects are to be read from the file and so the file is then closed. If the attribute is not cleared, the file will remain open and further Objects can be read from it. The file will always be closed when the Channel is deleted. This simple approach will normally be sufficient. However, because the AST library is designed to be used from more than one language, it has to be a little careful about reading and writing to files. This is due to incompatibilities that may exist between the file I/O facilities provided by different languages. If such incompatibilities prevent the above simple system being used, we need to adopt a system that off-loads all file I/O to external code. c+ What this means in practice is that if the above simple approach cannot be used, you must instead provide some simple C functions that perform the actual transfer of data to and from files and similar external data stores. The functions you provide are supplied as the source and/or sink function arguments to astChannel when you create a Channel (\secref{ss:creatingachannel}). An example is the best way to illustrate this. c- f+ What this means in practice is that if the above simple approach cannot be used, you must instead provide some simple Fortran routines that perform the actual transfer of data to and from files and similar external data stores. The routines you provide are supplied as the source and/or sink routine arguments to AST\_CHANNEL when you create a Channel (\secref{ss:creatingachannel}). An example is the best way to illustrate this. f- c+ Consider the following simple function called Source. It reads a single line of text from a C input stream and returns a pointer to it, or NULL if there is no more input: \small \begin{terminalv} #include #define LEN 200 static FILE *input_stream; const char *Source( void ) { static char buffer[ LEN + 2 ]; return fgets( buffer, LEN + 2, input_stream ); } \end{terminalv} \normalsize c- f+ Consider the following simple subroutine called SOURCE. It reads a single line of text from a Fortran I/O unit and then calls AST\_PUTLINE to pass it to the AST library, together with its length. It sets this length to be negative if there is no more input: \small \begin{terminalv} SUBROUTINE SOURCE( STATUS ) INTEGER STATUS CHARACTER * ( 200 ) BUFFER READ( 1, '(A)', END = 99 ) BUFFER CALL AST_PUTLINE( BUFFER, LEN( BUFFER ), STATUS ) RETURN 99 CALL AST_PUTLINE( BUFFER, -1, STATUS ) END \end{terminalv} \normalsize f- c+ Note that the input stream is a static variable which we will also access from our main program. This might look something like this (omitting error checking for brevity): \small \begin{terminalv} /* Open the input file. */ input_stream = fopen( "infile.ast", "r" ); /* Create a Channel and read an Object from it. */ channel = astChannel( Source, NULL, "" ); object = astRead( channel ); ... /* Annul the Channel and close the file when done. */ channel = astAnnul( channel ); (void) fclose( input_stream ); \end{terminalv} \normalsize c- f+ Our main program might then look something like this (omitting error checking for brevity): \small \begin{terminalv} EXTERNAL SOURCE ... * Open the input file. OPEN( UNIT = 1, FILE = 'infile.ast', STATUS = 'OLD' ) * Create the Channel and read an Object from it. CHANNEL = AST_CHANNEL( SOURCE, AST_NULL, ' ', STATUS ) OBJECT = AST_READ( CHANNEL, STATUS ) ... * Annul the Channel and close the file when done. CALL AST_ANNUL( CHANNEL, STATUS ) CLOSE( 1 ) \end{terminalv} \normalsize f- c+ Here, we first open the required input file, saving the resulting FILE pointer. We then pass a pointer to our Source function as the first argument to astChannel when creating a new Channel. When we read an Object from this Channel with astRead, the Source function will be called to obtain the textual data from the file, the end-of-file being detected when this function returns NULL. c- f+ Here, we first open the required input file. We then pass the name of our SOURCE routine as the first argument to AST\_CHANNEL when creating a new Channel (ensuring that SOURCE also appears in an EXTERNAL statement). When we read an Object from this Channel using AST\_READ, the SOURCE routine will be called to obtain the textual data from the file, the end-of-file being detected when it yields a negative line length. f- Note, if a value is set for the SourceFile attribute, c+ the astRead function will ignore any source function c- f+ the AST\_READ function will ignore any source routine f- specified when the Channel was created. \subsection{\label{ss:channelsink}Writing Objects to Files} As for reading, writing Objects to files can be done in two different ways. Again, the simple approach is to use the SinkFile attribute of the Channel. For instance, the following will write a pair of Objects to a text file called ``fred.txt'': c+ \small \begin{terminalv} astSet( channel, "SinkFile=fred.txt" ); nobj = astWrite( channel, object1 ); nobj = astWrite( channel, object2 ); astClear( channel, "SinkFile" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_SET( CHANNEL, 'SinkFile=fred.txt', STATUS ) NOBJ = AST_WRITE( CHANNEL, OBJECT1, STATUS ) NOBJ = AST_WRITE( CHANNEL, OBJECT2, STATUS ) CALL AST_CLEAR( CHANNEL, 'SinkFile', STATUS ) \end{terminalv} \normalsize f- Note, the act of clearing the attribute tells AST that no more output will be written to the file and so the file is then closed. If the attribute is not cleared, the file will remain open and further Objects can be written to it. The file will always be closed when the Channel is deleted. c+ If the details of the language's I/O system on the computer you are using means that the above approach cannot be used, then we can write a Sink function, that writes a line of output text to a file, and use it in basically the same way as the Source function in the previous section (\secref{ss:channelsource}): \small \begin{terminalv} static FILE *output_stream; void Sink( const char *line ) { (void) fprintf( output_stream, "%s\n", line ); } \end{terminalv} \normalsize Note that we must supply the final newline character ourselves. c- f+ If the details of the language's I/O system on the computer you are using means that the above approach cannot be used, then we can write a SINK routine, that obtains a line of output text from the AST library by calling AST\_GETLINE and then writes it to a file. We can use this in basically the same way as the SOURCE routine in the previous section (\secref{ss:channelsource}): \small \begin{terminalv} SUBROUTINE SINK( STATUS ) INTEGER L, STATUS CHARACTER * ( 200 ) BUFFER CALL AST_GETLINE( BUFFER, L, STATUS ) IF ( L .GT. 0 ) WRITE( 2, '(A)' ) BUFFER( : L ) END \end{terminalv} \normalsize f- c+ In this case, our main program would supply a pointer to this Sink function as the second argument to astChannel, as follows: \small \begin{terminalv} /* Open the output file. */ output_stream = fopen( "outfile.ast", "w" ); /* Create a Channel and write an Object to it. */ channel = astChannel( Source, Sink, "" ); nobj = astWrite( channel, object ); ... /* Annul the Channel and close the file when done. */ channel = astAnnul( channel ); (void) fclose( output_stream ); \end{terminalv} \normalsize c- f+ In this case, our main program would supply the name of this SINK routine as the second argument to AST\_CHANNEL (ensuring that it also appears in an EXTERNAL statement), as follows: \small \begin{terminalv} EXTERNAL SINK ... * Open the output file. OPEN( UNIT = 2, FILE = 'outfile.ast', STATUS = 'NEW' ) * Create a Channel and write an Object to it. CHANNEL = AST_CHANNEL( SOURCE, SINK, ' ', STATUS ) NOBJ = AST_WRITE( CHANNEL, OBJECT, STATUS ) ... * Annul the Channel and close the file when done. CALL AST_ANNUL( CHANNEL, STATUS ) CLOSE( 2 ) \end{terminalv} \normalsize f- c+ Note that we can specify a source and/or a sink function for the Channel, and that these may use either the same file, or different files according to whether we are reading or writing. AST has no knowledge of the underlying file system, nor of file positioning. It just reads and writes sequentially. If you wish, for example, to reposition a file at the beginning in between reads and writes, then this can be done directly (and completely independently of AST) using standard C functions. c- f+ Note that we can specify a source and/or a sink routine for the Channel, and that these may use either the same file, or different files according to whether we are reading or writing. AST has no knowledge of the underlying file system, nor of file positioning. It just reads and writes sequentially. If you wish, for example, to reposition a file at the beginning in between reads and writes, then this can be done directly (and completely independently of AST) using standard Fortran statements. f- c+ If an error occurs in your source or sink function, you can communicate this to the AST library by setting its error status to any error value using astSetStatus (\secref{ss:errordetection}). This will immediately terminate the read or write operation. c- f+ If an error occurs in your source or sink routine, you can communicate this to the AST library by setting the STATUS argument to any error value. This will immediately terminate the read or write operation. f- Note, if a value is set for the SinkFile attribute, c+ the astWrite function will ignore any sink function c- f+ the AST\_WRITE function will ignore any sink routine f- specified when the Channel was created. \subsection{\label{ss:otherplaces}Reading and Writing Objects to other Places} c+ It should be obvious from the above (\secref{ss:channelsource} and \secref{ss:channelsink}) that a Channel's source and sink functions provide a flexible means of intercepting textual data that describes AST Objects as it flows in and out of your program. In fact, you might like to regard a Channel simply as a filter for converting AST Objects to and from a stream of text which is then handled by your source and sink functions, where the real I/O occurs. c- f+ It should be obvious from the above (\secref{ss:channelsource} and \secref{ss:channelsink}) that a Channel's source and sink routines provide a flexible means of intercepting textual data that describes AST Objects as it flows in and out of your program. In fact, you might like to regard a Channel simply as a filter for converting AST Objects to and from a stream of text which is then handled by your source and sink routines, where the real I/O occurs. f- This gives you the ability to store AST Objects in virtually any data system, so long as you can convert a stream of text into something that can be stored (it need no longer be text) and retrieve it again. There is generally no need to retain comments. Other possibilities, such as inter-process and network communication, could also be implemented \emph{via} source and sink functions in basically the same way. \cleardoublepage \section{\label{ss:nativefits}Storing AST Objects in FITS Headers (FitsChans)} A FITS header is a sequence of 80-character strings, formatted according to particular rules defined by the Flexible Image Transport System (FITS). \htmladdnormallinkfoot{FITS}{http://fits.gsfc.nasa.gov/} is a widely-used standard for data interchange in astronomy and has also been adopted as a data processing format in some astronomical data reduction systems. The individual 80-character strings in a FITS header are usually called \emph{cards} or \emph{header cards} (for entirely anachronistic reasons). A sequence of FITS cards appears as a header at the start of every FITS data file, and sometimes also at other points within it, and is used to provide ancillary information which qualifies or describes the main array of data stored in the file. As such, FITS headers are prime territory for storing information about the coordinate systems associated with data held in FITS files. In this section, we will examine how to store information in FITS headers directly in the form of AST Objects---a process which is supported by a specialised class of Channel called a FitsChan. Our discussion here will turn out to be a transitional step that emphasises the similarities between a FitsChan and a Channel (\secref{ss:channels}). At the same time, it will prepare us for the next section (\secref{ss:foreignfits}), where we will examine how to use a FitsChan to tackle some of the more difficult problems that FITS headers can present. \subsection{\label{ss:nativeencoding}The Native FITS Encoding} As it turns out, we are not the first to have thought of storing WCS information in FITS headers. In fact, the original FITS standard (1981 vintage) defined a set of header keywords for this purpose which have been widely used, although they have proved too limited for many practical purposes. At the time of writing, a number of different ways of using FITS headers for storing WCS information are in use, most (although not all) based on the original standard. We will refer to these alternative ways of storing the information as FITS \emph{encodings} but will defer a discussion of their advantages and limitations until the next section (\secref{ss:foreignfits}). Here, we will examine how to store AST Objects directly in FITS headers. In effect, this defines a new encoding, which we will term the \emph{native encoding}. This is a special kind of encoding, because not only does it allow us to associate conventional WCS calibration information with FITS data, but it also allows any other information that can be expressed in terms of AST Objects to be stored as well. In fact, the native encoding provides us with facilities roughly analogous to those of the Channel (\secref{ss:channels})---\emph{i.e.}\ a lossless way of transferring AST Objects from program to program---but based on FITS headers instead of free-format text. \subsection{The FitsChan Model} I/O between AST Objects and FITS headers is supported by a specialised form of Channel called a FitsChan. A FitsChan contains a buffer which may hold any number, including zero, of FITS header cards. This buffer forms a workspace in which you can assemble FITS cards and manipulate them before writing them out to a file. By default, when a FitsChan is first created, it contains no cards and there are five ways of inserting cards into it: \begin{enumerate} c+ \item You may add cards yourself, one at a time, using astPutFits (\secref{ss:addingfitscards}). c- f+ \item You may add cards yourself, one at a time, using AST\_PUTFITS (\secref{ss:addingfitscards}). f- c+ \item You may add cards yourself, supplying all cards concatenated into a single string, using astPutCards (\secref{ss:addingmulticards}). c- f+ \item You may add cards yourself, supplying all cards concatenated into a single string, using AST\_PUTCARDS. (\secref{ss:addingmulticards}). f- c+ \item You may write an AST Object to the FitsChan (using astWrite), which will have the effect of creating new cards within the FitsChan which describe the Object (\secref{ss:writingnativefits}). c- f+ \item You may write an AST Object to the FitsChan (using AST\_WRITE), which will have the effect of creating new cards within the FitsChan which describe the Object (\secref{ss:writingnativefits}). f- \item You may assign a value to the SourceFile attribute of the FitsChan. The value should be the path to a text file holding a set of FITS header cards, one per line. When the SourceFile value is set (using c+ astSetC or astSet), c- f+ AST\_SETC or AST\_SET). f- the file is opened and the headers copied from it into the FitsChan. The file is then immediately closed. c+ \item You may specify a source function which reads data from some external store of FITS cards, just like the source associated with a basic Channel (\secref{ss:channelsource}). If you supply a source function, it will be called when the FitsChan is created in order to fill it with an initial set of cards (\secref{ss:fitssourceandsink}). c- f+ \item You may specify a source routine which reads data from some external store of FITS cards, just like the source associated with a basic Channel (\secref{ss:channelsource}). If you supply a source routine, it will be called when the FitsChan is created in order to fill it with an initial set of cards (\secref{ss:fitssourceandsink}). f- \end{enumerate} There are also four ways of removing cards from a FitsChan: \begin{enumerate} c+ \item You may delete cards yourself, one at a time, using astDelFits (\secref{ss:findingandchangingfits}). c- f+ \item You may delete cards yourself, one at a time, using AST\_DELFITS (\secref{ss:findingandchangingfits}). f- c+ \item You may read an AST Object from the FitsChan (using astRead), which will have the effect of removing those cards from the FitsChan which describe the Object (\secref{ss:readingnativefits}). c- f+ \item You may read an AST Object from the FitsChan (using AST\_READ), which will have the effect of removing those cards from the FitsChan which describe the Object (\secref{ss:readingnativefits}). f- \item You may assign a value to the FitsChan's SinkFile attribute. When the FitsChan is deleted, any remaining headers are written out to a text file with path equal to the value of the SinkFile attribute. c+ \item Alternatively, you may specify a sink function which writes data to some external store of FITS cards, just like the sink associated with a basic Channel (\secref{ss:channelsink}). If you supply a sink function, it will be called when the FitsChan is deleted in order to write out any FITS cards that remain in it (\secref{ss:fitssourceandsink}). Note, the sink function is not called if the SinkFile attribute has been set. c- f+ \item Alternatively, You may specify a sink routine which writes data to some external store of FITS cards, just like the sink associated with a basic Channel (\secref{ss:channelsink}). If you supply a sink routine, it will be called when the FitsChan is deleted in order to write out any FITS cards that remain in it (\secref{ss:fitssourceandsink}). Note, the sink routine is not called if the SinkFile attribute has been set. f- \end{enumerate} Note, in particular, that reading an AST Object from a FitsChan is \emph{destructive}. That is, it deletes the FITS cards that describe the Object. The reason for this is explained in \secref{ss:destructiveread}. c+ In addition to the above, you may also read individual cards from a FitsChan using the function astFindFits (which is not destructive). This is the main means of writing out FITS cards if you have not supplied a sink function. astFindFits also provides a means of searching for particular FITS cards (by keyword, for example) and there are other facilities for overwriting cards when required (\secref{ss:findingandchangingfits}). c- f+ In addition to the above, you may also read individual cards from a FitsChan using the function AST\_FINDFITS (which is not destructive). This is the main means of writing out FITS cards if you have not supplied a sink routine. AST\_FINDFITS also provides a means of searching for particular FITS cards (by keyword, for example) and there are other facilities for overwriting cards when required (\secref{ss:findingandchangingfits}). f- \subsection{\label{ss:creatingafitschan}Creating a FitsChan} c+ The FitsChan constructor function, astFitsChan, is straightforward to use: c- f+ The FitsChan constructor function, AST\_FITSCHAN, is straightforward to use: f- c+ \small \begin{terminalv} #include "ast.h" AstFitsChan *fitschan; ... fitschan = astFitsChan( NULL, NULL, "Encoding=NATIVE" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INCLUDE 'AST_PAR' INTEGER FITSCHAN, STATUS STATUS = 0 ... FITSCHAN = AST_FITSCHAN( AST_NULL, AST_NULL, 'Encoding=NATIVE', STATUS ) \end{terminalv} \normalsize f- c+ Here, we have omitted any source or sink functions by supplying NULL pointers for the first two arguments. c- f+ Here, we have omitted any source or sink functions by supplying the AST\_NULL routine for the first two arguments (remember to include the AST\_PAR include file which contains the required EXTERNAL statement for this routine). f- We have also initialised the FitsChan's Encoding attribute to NATIVE. This indicates that we will be using the native encoding (\secref{ss:nativeencoding}) to store and retrieve Objects. If this was left unspecified, the default would depend on the FitsChan's contents. An attempt is made to use whatever encoding appears to have been used previously. For an empty FitsChan, the default is NATIVE, but it does no harm to be sure. \subsection{\label{ss:addressingfitscards}Addressing Cards in a FitsChan} Because a FitsChan contains an ordered sequence of header cards, a mechanism is needed for addressing them. This allows you to specify where new cards are to be added, for example, or which card is to be deleted. This role is filled by the FitsChan's integer Card attribute, which gives the index of the \emph{current card} in the FitsChan. You can nominate any card you like to be current, simply by setting a new value for the Card attribute, for example: c+ \small \begin{terminalv} int icard; ... astSetI( fitschan, "Card", icard ) \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER ICARD ... CALL AST_SETI( FITSCHAN, 'Card', ICARD, STATUS ) \end{terminalv} \normalsize f- c+ where ``icard'' contains the index of the card on which you wish to operate next. Some functions will update the Card attribute as a means of advancing through the sequence of cards, when reading them for example, or to indicate which card matches a search criterion. c- f+ where ICARD contains the index of the card on which you wish to operate next. Some functions will update the Card attribute as a means of advancing through the sequence of cards, when reading them for example, or to indicate which card matches a search criterion. f- The default value for Card is one, which is the index of the first card. This means that you can ``rewind'' a FitsChan to access its first card by clearing the Card attribute: c+ \small \begin{terminalv} astClear( fitschan, "Card" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_CLEAR( FITSCHAN, 'Card', STATUS ) \end{terminalv} \normalsize f- The total number of cards in a FitsChan is given by the integer Ncard attribute. This is a read-only attribute whose value is automatically updated as you add or remove cards. It means you can address all the cards in sequence using a loop such as the following: c+ \small \begin{terminalv} int ncard; ... ncard = astGetI( fitschan, "Ncard" ); for ( icard = 1; icard <= ncard; icard++ ) { astSetI( fitschan, "Card", icard ); } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} DO 1 ICARD = 1, AST_GETI( FITSCHAN, 'Ncard', STATUS ) CALL AST_SETI( FITSCHAN, 'Card', ICARD, STATUS ) 1 CONTINUE \end{terminalv} \normalsize f- c+ However, it is usually possible to write slightly tidier loops based on the astFindFits function described later (\secref{ss:extractingfitscards} and \secref{ss:findingandchangingfits}). c- f+ However, it is usually possible to write slightly tidier loops based on the AST\_FINDFITS function described later (\secref{ss:extractingfitscards} and \secref{ss:findingandchangingfits}). f- If you set the Card attribute to a value larger than Ncard, the FitsChan is regarded as being positioned at its \emph{end-of-file}. In this case there is no current card and an attempt to obtain a value for the Card attribute will always return the value Ncard~$+$~1. When a FitsChan is empty, it is always at the end-of-file. \subsection{\label{ss:writingnativefits}Writing Native Objects to a FitsChan} c+ Having created an empty FitsChan (\secref{ss:creatingafitschan}), you can write any AST Object to it in the native encoding using the astWrite function. Let us assume we are writing a SkyFrame,\footnote{More probably, you would want to write a FrameSet, but for purposes of illustration a SkyFrame contains a more manageable amount of data.} as follows: c- f+ Having created an empty FitsChan (\secref{ss:creatingafitschan}), you can write any AST Object to it in the native encoding using the AST\_WRITE function. Let us assume we are writing a SkyFrame,\footnote{More probably, you would want to write a FrameSet, but for purposes of illustration a SkyFrame contains a more manageable amount of data.} as follows: f- c+ \small \begin{terminalv} AstSkyFrame *skyframe; int nobj; ... nobj = astWrite( fitschan, skyframe ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER NOBJ, SKYFRAME ... NOBJ = AST_WRITE( FITSCHAN, SKYFRAME, STATUS ) \end{terminalv} \normalsize f- c+ Since we have selected the native encoding (\secref{ss:nativeencoding}), there are no restrictions on the class of Object we may write, so astWrite should always return a value of one, unless an error occurs. Unlike a basic Channel (\secref{ss:writingtoachannel}), this write operation will not produce any output from our program. The FITS headers produced are simply stored inside the FitsChan. c- f+ Since we have selected the native encoding (\secref{ss:nativeencoding}), there are no restrictions on the class of Object we may write, so AST\_WRITE should always return a value of one, unless an error occurs. Unlike a basic Channel (\secref{ss:writingtoachannel}), this write operation will not produce any output from our program. The FITS headers produced are simply stored inside the FitsChan. f- After this write operation, the Ncard attribute will be updated to reflect the number of new cards added to the FitsChan and the Card attribute will point at the card immediately after the last one written. Since our FitsChan was initially empty, the Card attribute will, in this example, point at the end-of-file (\secref{ss:addressingfitscards}). The FITS standard imposes a limit of 68 characters on the length of strings which may be stored in a single header card. Sometimes, a description of an AST Object involves the use of strings which exceed this limit (\emph{e.g.}\ a Frame title can be of arbitrary length). If this occurs, the long string will be split over two or more header cards. Each ``continuation'' card will have the keyword \texttt{CONTINUE} in columns 1 to 8, and will contain a space in column 9 (instead of the usual equals sign). An ampersand (``\texttt{\&}'') is appended to the end of each of the strings (except the last one) to indicate that the string is continued on the next card. c+ Note, this splitting of long strings over several cards only occurs when writing AST Objects to a FitsChan using the astWrite function and the \emph{native} encoding. If a long string is stored in a FitsChan using (for instance) the astPutFits or astPutCards function, it will simply be truncated. c- f+ Note, this splitting of long strings over several cards only occurs when writing AST Objects to a FitsChan using the AST\_WRITE routine and the \emph{native} encoding. If a long string is stored in a FitsChan using (for instance) the AST\_PUTFITS or AST\_PUTCARDS routine, it will simply be truncated. f- \subsection{\label{ss:extractingfitscards}Extracting Individual Cards from a FitsChan} c+ To examine the contents of the FitsChan after writing the SkyFrame above (\secref{ss:writingnativefits}), we must write a simple loop to extract each card in turn and print it out. We must also remember to rewind the FitsChan first, \emph{e.g.}\ using astClear. The following loop would do: c- f+ To examine the contents of the FitsChan after writing the SkyFrame above (\secref{ss:writingnativefits}), we must write a simple loop to extract each card in turn and print it out. We must also remember to rewind the FitsChan first, \emph{e.g.}\ using AST\_CLEAR. The following loop would do: f- c+ \small \begin{terminalv} #include char card[ 81 ]; ... astClear( fitschan, "Card" ); while ( astFindFits( fitschan, "%f", card, 1 ) ) (void) printf( "%s\n", card ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CHARACTER * ( 80 ) CARD ... CALL AST_CLEAR( FITSCHAN, 'Card', STATUS ) 2 CONTINUE IF ( AST_FINDFITS( FITSCHAN, '%f', CARD, .TRUE., STATUS ) ) THEN WRITE ( *, '(A)' ) CARD GO TO 2 END IF \end{terminalv} \normalsize f- c+ Here, we have used the astFindFits function to find a FITS card by keyword. It is given a keyword template of ``\%f'', which matches any FITS keyword, so it always finds the current card, which it returns. Its fourth argument is set to 1, to indicate that the Card attribute should be incremented afterwards so that the following card will be found the next time around the loop. astFindFits returns zero when it reaches the end-of-file and this terminates the loop. c- f+ Here, we have used the AST\_FINDFITS function to find a FITS card by keyword. It is given a keyword template of ``\%f'', which matches any FITS keyword, so it always finds the current card, which it returns. Its fourth argument is set to .TRUE., to indicate that the Card attribute should be incremented afterwards so that the following card will be found the next time around the loop. AST\_FINDFITS returns .FALSE.\ when it reaches the end-of-file and this terminates the loop. f- c+ If we were storing the FITS headers in an output FITS file instead of printing them out, we might use a loop like this but replace ``printf'' with a suitable data storage operation. This would only be necessary if we had not provided a sink function for the FitsChan (\secref{ss:fitssourceandsink}). c- f+ If we were storing the FITS headers in an output FITS file instead of printing them out, we might use a loop like this but replace the WRITE statement with a call to a suitable data access routine to store the header card. This would only be necessary if we had not provided a sink routine for the FitsChan (\secref{ss:fitssourceandsink}). f- \subsection{The Native FitsChan Output Format} If we print out the FITS header cards describing the SkyFrame we wrote earlier (\secref{ss:writingnativefits}), we should obtain something like the following: \small \begin{terminalv} COMMENT AST ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ AST COMMENT AST Beginning of AST data for SkyFrame object AST COMMENT AST ................................................................ AST BEGAST_A= 'SkyFrame' / Description of celestial coordinate system NAXES_A = 2 / Number of coordinate axes AX1_A = ' ' / Axis number 1 BEGAST_B= 'SkyAxis ' / Celestial coordinate axis ENDAST_A= 'SkyAxis ' / End of object definition AX2_A = ' ' / Axis number 2 BEGAST_C= 'SkyAxis ' / Celestial coordinate axis ENDAST_B= 'SkyAxis ' / End of object definition ISA_A = 'Frame ' / Coordinate system description SYSTEM_A= 'FK4-NO-E' / Celestial coordinate system type EPOCH_A = 1958.0 / Besselian epoch of observation ENDAST_C= 'SkyFrame' / End of object definition COMMENT AST ................................................................ AST COMMENT AST End of AST data for SkyFrame object AST COMMENT AST ---------------------------------------------------------------- AST \end{terminalv} \normalsize As you can see, this resembles the information that would be written to a basic Channel to describe the same SkyFrame (\secref{ss:textualoutputformat}), except that it has been formatted into 80-character header cards according to FITS conventions. There are also a number of other differences worth noting: \begin{enumerate} \item There is no unnecessary information about default values provided for the benefit of the human reader. This is because the Full attribute for a FitsChan defaults to $-$1, thus suppressing this information (\emph{c.f.}~\secref{ss:controllingchanneloutput}). You can restore the information if you wish by setting Full to 0 or $+$1, in which case additional COMMENT cards will be generated to hold it. \item The information is not indented, because FITS does not allow this. However, if you change the Full attribute to 0 or $+$1, comments will be included that are intended to help break up the sequence of headers and highlight its structure. This will probably only be of use if you are attempting to track down a problem by examining the FITS cards produced in detail. \item The FITS keywords which appear to the left of the ``$=$'' signs have additional characters (``\_A'', ``\_B'', \emph{etc.}) appended to them. This is done in order to make each keyword unique. \end{enumerate} c+ This last point is worth further comment and is necessary because the FITS standard only allows for certain keywords (such as COMMENT and HISTORY) to appear more than once. astWrite therefore appends an arbitrary sequence of two characters to each new keyword it generates in order to ensure that it does not duplicate any already present in the FitsChan. c- f+ This last point is worth further comment and is necessary because the FITS standard only allows for certain keywords (such as COMMENT and HISTORY) to appear more than once. AST\_WRITE therefore appends an arbitrary sequence of two characters to each new keyword it generates in order to ensure that it does not duplicate any already present in the FitsChan. f- c+ The main risk from not following this convention is that some software might ignore (say) all but the last occurrence of a keyword before passing the FITS headers on. Such an event is unlikely, but would obviously destroy the information present, so astWrite enforces the uniqueness of the keywords it uses. The extra characters added are ignored when the information is read back. c- f+ The main risk from not following this convention is that some software might ignore (say) all but the last occurrence of a keyword before passing the FITS headers on. Such an event is unlikely, but would obviously destroy the information present, so AST\_WRITE enforces the uniqueness of the keywords it uses. The extra characters added are ignored when the information is read back. f- As with a basic Channel, you can also suppress the comments produced in a FitsChan by setting the boolean (integer) Comment attribute to zero (\secref{ss:channelcommenting}). However, FITS headers are traditionally generously commented, so this is not recommended. \subsection{\label{ss:addingfitscards}Adding Individual Cards to a FitsChan} c+ To insert individual cards into a FitsChan, prior to reading them back as Objects for example, you should use the astPutFits function. You can insert a card in front of the current one as follows: c- f+ To insert individual cards into a FitsChan, prior to reading them back as Objects for example, you should use the AST\_PUTFITS routine. You can insert a card in front of the current one as follows: f- c+ \small \begin{terminalv} astPutFits( fitschan, card, 0 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_PUTFITS( FITSCHAN, CARD, .FALSE., STATUS ) \end{terminalv} \normalsize f- c+ where the third argument of zero indicates that the current card should not be overwritten. Note that facilities are not provided by AST for formatting the card contents. c- f+ where the third argument of .FALSE.\ indicates that the current card should not be overwritten. Note that facilities are not provided by AST for formatting the card contents. f- c+ After inserting a card, the FitsChan's Card attribute points at the original Card, or at the end-of-file if the FitsChan was originally empty. Entering a sequence of cards is therefore straightforward. If ``cards'' is an array of pointers to strings containing FITS header cards and ``ncards'' is the number of cards, then a loop such as the following will insert the cards in sequence into a FitsChan: c- f+ After inserting a card, the FitsChan's Card attribute points at the original Card, or at the end-of-file if the FitsChan was originally empty. Entering a sequence of cards is therefore straightforward. If CARDS is an array of character strings containing FITS header cards and NCARDS is the number of cards, then a loop such as the following will insert the cards in sequence into a FitsChan: f- c+ \small \begin{terminalv} #define MAXCARD 100 char *cards[ MAXCARD ]; int ncard; ... for ( icard = 0; icard < ncard; icard++ ) astPutFits( fitschan, cards[ icard ], 0 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER NCARD CHARACTER * ( 80 ) CARDS( NCARD ) ... DO 3 ICARD = 1, NCARD CALL AST_PUTFITS( FITSCHAN, CARDS( ICARD ), .FALSE., STATUS ) 3 CONTINUE \end{terminalv} \normalsize f- c+ The string containing a card need not be null terminated if it is at least 80 characters long (we have not allocated space for the strings themselves in this brief example). c- c+ Note that astPutFits enforces the validity of a FitsChan by rejecting any cards which do not adhere to the FITS standard. If any such cards are detected, an error will result. c- f+ Note that AST\_PUTFITS enforces the validity of a FitsChan by rejecting any cards which do not adhere to the FITS standard. If any such cards are detected, an error will result. f- \subsection{\label{ss:addingmulticards}Adding Concatenated Cards to a FitsChan} If you have all your cards concatenated together into a single long string, each occupying 80 characters (with no delimiters), you can insert them into a FitsChan in a single call using c+ astPutCards. c- f+ AST\_PUTCARDS. f- This call first empties the supplied FitsChan of any existing cards, then inserts the new cards, and finally rewinds the FitsChan so that a subsequent call to c+ astRead c- f+ AST\_READ f- will start reading from the first supplied card. The c+ astPutCards function uses astPutFits c- f+ AST\_PUTCARDS routine uses AST\_PUTFITS f- internally to interpret and store each individual card, and so the caveats in \secref{ss:addingfitscards} should be read. c+ For instance, if you are using the CFITSIO library for access to FITS files, you can use the CFITSIO fits\_hdr2str function to obtain a string suitable for passing to astPutCards: \small \begin{terminalv} if( !fits_hdr2str( fptr, 0, NULL, 0, &header, &nkeys, &status ) ) fitschan = astFitsChan( NULL, NULL, "" ); astPutCards( fitschan, header ); header = free( header ); wcsinfo = astRead( fitschan ); ... } \end{terminalv} \normalsize c- \subsection{\label{ss:readingnativefits}Reading Native Objects From a FitsChan} c+ Once you have stored a FITS header description of an Object in a FitsChan using the native encoding (\secref{ss:writingnativefits}), you can read it back using astRead in much the same way as with a basic Channel (\secref{ss:readingfromachannel}). Similar comments about validating the Object you read also apply (\secref{ss:validatinginput}). If you have just written to the FitsChan, you must remember to rewind it first: c- f+ Once you have stored a FITS header description of an Object in a FitsChan using the native encoding (\secref{ss:writingnativefits}), you can read it back using AST\_READ in much the same way as with a basic Channel (\secref{ss:readingfromachannel}). Similar comments about validating the Object you read also apply (\secref{ss:validatinginput}). If you have just written to the FitsChan, you must remember to rewind it first: f- c+ \small \begin{terminalv} AstObject *object; ... astClear( fitschan, "Card" ); object = astRead( fitschan ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER OBJECT ... CALL AST_CLEAR( FITSCHAN, 'Card', STATUS ) OBJECT = AST_READ( FITSCHAN, STATUS ) \end{terminalv} \normalsize f- c+ An important feature of a FitsChan is that read operations are destructive. This means that if an Object description is found, it will be consumed by astRead which will remove all the cards involved, including associated COMMENT cards, from the FitsChan. Thus, if you write an Object to a FitsChan, rewind, and read the same Object back, you should end up with the original FitsChan contents. If you need to circumvent this behaviour for any reason, it is a simple matter to make a copy of a FitsChan using astCopy (\secref{ss:copyingobjects}). If you then read from the copy, the original FitsChan will remain untouched. c- f+ An important feature of a FitsChan is that read operations are destructive. This means that if an Object description is found, it will be consumed by AST\_READ which will remove all the cards involved, including associated COMMENT cards, from the FitsChan. Thus, if you write an Object to a FitsChan, rewind, and read the same Object back, you should end up with the original FitsChan contents. If you need to circumvent this behaviour for any reason, it is a simple matter to make a copy of a FitsChan using AST\_COPY (\secref{ss:copyingobjects}). If you then read from the copy, the original FitsChan will remain untouched. f- After a read completes, the FitsChan's Card attribute identifies the card immediately following the last card read, or the end-of-file of there are no more cards. c+ Since the \emph{native} encoding is being used, any long strings involved in the object description will have been split into two or more adjacent contuation cards when the Object was stored in the header using function astWrite. The astRead function reverses this process by concatenating any such adjacent continuation cards to re-create the original long string. c- f+ Since the \emph{native} encoding is being used, any long strings involved in the object description will have been split into two or more adjacent contuation cards when the Object was stored in the header using routine AST\_WRITE. The AST\_READ routine reverses this process by concatenating any such adjacent continuation cards to re-create the original long string. f- \subsection{Saving and Restoring Multiple Objects in a FitsChan} When using the native FITS encoding, multiple Objects may be stored and all I/O operations are sequential. This means that you can simply write a sequence of Objects to a FitsChan. After each write operation, the Card attribute will be updated so that the next write appends the next Object description to the previous one. If you then rewind the FitsChan, you can read the Objects back in the original order. Reading them back will, of course, remove their descriptions from the FitsChan (\secref{ss:readingnativefits}) but the behaviour of the Card attribute is such that successive reads will simply return each Object in sequence. The only thing that may require care, given that a FitsChan can always be addressed randomly by setting its Card attribute, is to avoid writing one Object on top of another. For obvious reasons, the Object descriptions in a FitsChan must remain separate if they are to make sense when read back. \subsection{Mixing Native Objects with Other FITS Cards} Of course, any real FITS header will contain other information besides AST Objects, if only the mandatory FITS cards that must accompany all FITS data. When FITS headers are read in from a real dataset, therefore, any native AST Object descriptions will be inter-mixed with many other cards. Because this is the normal state of affairs, the boolean (integer) Skip attribute for a FitsChan defaults to one. This means that when you read an Object From a FitsChan, any irrelevant cards will simply be skipped over until the start of the next Object description, if any, is found. If you start reading part way through an Object description, no error will result. The remainder of the description will simply be skipped. Setting Skip to zero will change this behaviour to resemble that of a basic Channel (\secref{ss:mixingchanneltext}), where extraneous data are not permitted by default, but this will probably rarely be useful. \subsection{\label{ss:findingandchangingfits}Finding and Changing Cards in a FitsChan} c+ You can search for, and retrieve, particular cards in a FitsChan by keyword, using the function astFindFits. This performs a search, starting at the current card, until it finds a card whose keyword matches the template you supply, or the end-of-file is reached. c- f+ You can search for, and retrieve, particular cards in a FitsChan by keyword, using the function AST\_FINDFITS. This performs a search, starting at the current card, until it finds a card whose keyword matches the template you supply, or the end-of-file is reached. f- c+ If a suitable card is found, astFindFits optionally returns the card's contents and then sets the FitsChan's Card attribute either to identify the card found, or the one following it. The way you want the Card attribute to be set is indicated by the final boolean (int) argument to astFindFits. A value of one is returned to indicate success. If a suitable card cannot be found, astFindFits returns a value of zero to indicate failure and sets the FitsChan's Card attribute to the end-of-file. c- f+ If a suitable card is found, AST\_FINDFITS returns the card's contents and then sets the FitsChan's Card attribute either to identify the card found, or the one following it. The way you want the Card attribute to be set is indicated by the fourth (logical) argument to AST\_FINDFITS. A value of .TRUE.\ is returned to indicate success. If a suitable card cannot be found, AST\_FINDFITS returns a value of .FALSE.\ to indicate failure and sets the FitsChan's Card attribute to the end-of-file. f- c+ Requesting that the Card attribute be set to indicate the card that astFindFits finds is useful if you want to replace that card with a new one, as in this example: c- f+ Requesting that the Card attribute be set to indicate the card that AST\_FINDFITS finds is useful if you want to replace that card with a new one, as in this example: f- c+ \small \begin{terminalv} char newcard[ 81 ]; ... (void) astFindFits( fitschan, "AIRMASS", NULL, 0 ); astPutFits( fitschan, newcard, 1 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CHARACTER * ( 80 ) NEWCARD LOGICAL JUNK ... JUNK = AST_FINDFITS( FITSCHAN, 'AIRMASS', CARD, .FALSE., STATUS ) CALL AST_PUTFITS( FITSCHAN, NEWCARD, .TRUE., STATUS ) \end{terminalv} \normalsize f- c+ Here, astFindFits is used to search for a card with the keyword AIRMASS, with a NULL pointer being given to indicate that we do not want the card's contents returned. If the card is found, astPutFits then overwrites it with a new card. Otherwise, the Card attribute ends up pointing at the end-of-file and the new card is simply appended to the end of the FitsChan. c- f+ Here, AST\_FINDFITS is used to search for a card with the keyword AIRMASS. If the card is found, AST\_PUTFITS then overwrites it with a new card. Otherwise, the Card attribute ends up pointing at the end-of-file and the new card is simply appended to the end of the FitsChan. f- c+ A similar approach can be used to delete selected cards from a FitsChan using astDelFits, which deletes the current card: c- f+ A similar approach can be used to delete selected cards from a FitsChan using AST\_DELFITS, which deletes the current card: f- c+ \small \begin{terminalv} if ( astFindFits( fitschan, "BSCALE", NULL, 0 ) ) astDelFits( fitschan ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} IF ( AST_FINDFITS( FITSCHAN, 'BSCALE', CARD, .FALSE., STATUS ) ) THEN CALL AST_DELFITS( FITSCHAN, STATUS ) END IF \end{terminalv} \normalsize f- This deletes the first card, if any, with the BSCALE keyword. c+ Requesting that astFindFits increments the Card attribute to identify the card following the one found is more useful when writing loops. For example, the following loop extracts each card whose keyword matches the template ``CD\%6d'' (that is, ``CD'' followed by six decimal digits): c- f+ Requesting that AST\_FINDFITS increments the Card attribute to identify the card following the one found is more useful when writing loops. For example, the following loop extracts each card whose keyword matches the template ``CD\%6d'' (that is, ``CD'' followed by six decimal digits): f- c+ \small \begin{terminalv} while ( astFindFits( fitschan, "CD%6d", card, 1 ) { } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} 4 CONTINUE IF ( AST_FINDFITS( FITSCHAN, 'CD%6d', CARD, .TRUE., STATUS ) ) THEN GO TO 4 END IF \end{terminalv} \normalsize f- c+ For further details of keyword templates, see the description of astFindFits in \appref{ss:functiondescriptions}. c- f+ For further details of keyword templates, see the description of AST\_FINDFITS in \appref{ss:functiondescriptions}. f- c+ \subsection{\label{ss:fitssourceandsink}Source and Sink Functions for FitsChans} c- f+ \subsection{\label{ss:fitssourceandsink}Source and Sink Routines for FitsChans} f- c+ The use of source and sink functions with a FitsChan is optional. This is because you can always arrange to explicitly fill a FitsChan with FITS cards (\secref{ss:addingfitscards} and \secref{ss:addingmulticards}) and you can also extract any cards that remain and write them out yourself (\secref{ss:extractingfitscards}) before you delete the FitsChan. c- f+ The use of source and sink routines with a FitsChan is optional. This is because you can always arrange to explicitly fill a FitsChan with FITS cards (\secref{ss:addingfitscards} and \secref{ss:addingmulticards}) and you can also extract any cards that remain and write them out yourself (\secref{ss:extractingfitscards}) before you delete the FitsChan. f- c+ If you choose to use these functions, however, they behave in a very similar manner to those used by a Channel (\secref{ss:channelsource} and \secref{ss:channelsink}). You supply pointers to these functions, as arguments to the constructor function astFitsChan when you create the FitsChan (\secref{ss:creatingafitschan}). The source function is invoked implicitly at this point to fill the FitsChan with FITS cards and the FitsChan is then rewound, so that the first card becomes current. The sink function is automatically invoked later, when the FitsChan is deleted, in order to write out any cards that remain in it. c- f+ If you choose to use these routines, however, they behave in a very similar manner to those used by a Channel (\secref{ss:channelsource} and \secref{ss:channelsink}). You supply these routines, as arguments to the constructor function AST\_FITSCHAN when you create the FitsChan (\secref{ss:creatingafitschan}). The source routine is invoked implicitly at this point to fill the FitsChan with FITS cards and the FitsChan is then rewound, so that the first card becomes current. The sink routine is automatically invoked later, when the FitsChan is deleted, in order to write out any cards that remain in it. f- c+ The only real difference between the source and sink functions for a FitsChan and a basic Channel is that FITS cards are limited in length to 80~characters, so the choice of buffer size is simplified. The ``Source'' and ``Sink'' functions in \secref{ss:channelsource} and \secref{ss:channelsink} could therefore be used to access FITS headers stored in text files simply by changing LEN to be 80. If you were not accessing a text file, however, appropriate changes to the I/O statements would be needed since the separating newline characters would be absent. The details obviously depend on the format of the file you are handling, which need not necessarily be a true FITS file. c- f+ The only real difference between the source and sink routines for a FitsChan and a basic Channel is that FITS cards are limited in length to 80~characters, so the choice of buffer size is simplified. This affects the way the card contents are passed, so the routines themselves are slightly different. The following is therefore the FitsChan equivalent of the Channel SOURCE routine given in \secref{ss:channelsource}: \small \begin{terminalv} INTEGER FUNCTION FITSSOURCE( CARD, STATUS ) CHARACTER * ( 80 ) CARD INTEGER STATUS READ( 1, '(A)', END = 99 ) CARD FITSSOURCE = 1 RETURN 99 FITSSOURCE = 0 END \end{terminalv} \normalsize Here, the FITS card contents are returned \emph{via} the CARD argument (the AST\_PUTLINE routine should not be used) and the function returns 1 to indicate that a card has been read. A value of zero is returned if there are no more cards to read. The sink routine for a FitsChan is also a little different (\emph{c.f.}\ the SINK routine in~\secref{ss:channelsink}), as follows: \small \begin{terminalv} SUBROUTINE FITSSINK( CARD, STATUS ) CHARACTER * ( 80 ) CARD INTEGER STATUS WRITE( 2, '(A)' ) CARD END \end{terminalv} \normalsize The contents of the FITS card being written are passed \emph{via}\ the CARD argument (the AST\_GETLINE routine should not be used). Of course, both of these examples assume that you are accessing text files. If this is not the case, then appropriate changes to the I/O statements would be needed. The details obviously depend on the format of the file you are handling, which need not necessarily be a true FITS file. f- \cleardoublepage \section{\label{ss:foreignfits}Using Foreign FITS Encodings} We saw in the previous section (\secref{ss:nativefits}) how to store and retrieve any kind of AST Object in a FITS header by using a FitsChan. To achieve this, we set the FitsChan's Encoding attribute to NATIVE. However, the Objects we wrote could then only be read back by other programs that use AST. In practice, we will also encounter FITS headers containing WCS information written by other software systems. We will probably also need to write FITS headers in a format that can be understood by these systems. Indeed, this interchange of data is one of the main reasons for the existence of FITS, so in this section we will examine how to accommodate these requirements. \subsection{\label{ss:foreignencodings}The Foreign FITS Encodings} As mentioned previously (\secref{ss:nativeencoding}), there are a number of conventions currently in use for storing WCS information in FITS headers, which we call \emph{encodings}. Here, we are concerned with those encodings defined by software systems other than AST, which we term \emph{foreign encodings}. Currently, AST supports six foreign encodings, which may be selected by setting the Encoding attribute of a FitsChan to one of the following (character string) values: \begin{quote} \begin{description} \item[DSS]\mbox{}\\ This encoding stores WCS information using the convention developed at the Space Telescope Science Institute for the Digitised Sky Survey (DSS) astrometric plate calibrations. DSS images which use this convention are widely available and it is understood by a number of important and well-established astronomy applications. However, the calibration model used (based on a polynomial fit) is not easily applicable to other types of data and creating the polynomial coefficients needed to calibrate your own images can prove difficult. For this reason, the DSS encoding is probably best viewed as a ``read-only'' format. It is possible, however, to read in WCS information using this encoding and then to write it back out again, so long as only minor changes have been made. \item[FITS-WCS]\mbox{}\\ This encoding is very important because it is based on a new FITS standard which should, for the first time, address the problem of celestial coordinate systems in a proper manner, by considerably extending the original FITS standard. The conventions used are described in a series of papers by E.W.\,Greisen, M.\,Calabretta, \emph{et. al.}, often referred to as the ``FITS-WCS papers''. They are described at \url{http://fits.gsfc.nasa.gov/fits_wcs.html}. Now that the first two papers in this series have been agreed, this encoding should be understood by any FITS-WCS compliant software and it is likely to be adopted widely for FITS data in future. For details of the coverage of these conventions provided by the FitsChan class, see \appref{ss:fitswcscoverage}. \item[FITS-IRAF]\mbox{}\\ This encoding is based on the conventions described in the document ``World Coordinate Systems Representations Within the FITS Format'' by R.J. Hanisch and D.G. Wells, 1988.\footnote{Available by ftp from fits.cv.nrao.edu /fits/documents/wcs/wcs88.ps.Z} It is employed by the IRAF data analysis facility, so its use will facilitate data exchange with IRAF. This encoding is in effect a sub-set of the current FITS-WCS encoding. \item[FITS-PC]\mbox{}\\ This encoding is based on a previous version of the proposed new FITS WCS standard which used \texttt{PCjjjjiii} and \texttt{CDELTj} keywords to describe axis rotation and scaling. Versions of AST prior to V1.5 used this scheme for the FITS-WCS encoding. As of V1.5, FITS-WCS uses \texttt{CDi\_j} keywords instead.\footnote{There are many other differences between the previous and the current FITS-WCS encodings. The keywords to describe axis rotation and scaling is used purely as a label to identify the scheme.} The FITS-PC encoding is included in AST V1.5 only to allow FITS-WCS data created with previous versions to be read. It should not, in general, be used to create new data sets. \item[FITS-AIPS]\mbox{}\\ This encoding is based on the conventions described in the document ``Non-linear Coordinate Systems in AIPS'' by Eric W. Greisen (revised 9th September, 1994).\footnote{Available by ftp from fits.cv.nrao.edu /fits/documents/wcs/aips27.ps.Z} It is currently employed by the AIPS data analysis facility, so its use will facilitate data exchange with AIPS. This encoding uses \texttt{CROTAi} and \texttt{CDELTi} keywords to describe axis rotation and scaling. \item[FITS-AIPS++]\mbox{}\\ Encodes coordinate system information in FITS header cards using the conventions used by the AIPS++ project. This is an extension of FITS-AIPS which includes some of the features of FITS-PC and FITS-IRAF. \end{description} \end{quote} For more detail about the above encodings, see the description of the Encoding attribute in \appref{ss:attributedescriptions}. \subsection{\label{ss:foreignfitslimitations}Limitations of Foreign Encodings} The foreign encodings available for storing WCS information in FITS headers have a number of limitations when compared with the native encoding of AST Objects (\secref{ss:nativefits}). The main ones are: \begin{enumerate} \item Only one class of AST Object, the FrameSet, may be represented using a foreign FITS encoding. This should not come as a surprise, because the purpose of storing WCS information in FITS headers is to attach coordinate systems to an associated array of data. Since the FrameSet is the AST Object designed for the same purpose (\secref{ss:baseandcurrent}), there is a natural correspondence. The way in which a FrameSet is translated to and from the foreign encoding also follows from this correspondence. The FrameSet's base Frame identifies the data grid coordinates of the associated FITS data. These are the same as FITS pixel coordinates, in which the first pixel (in 2 dimensions) has coordinates (1,1) at its centre. Similarly, the current Frame of the FrameSet identifies the FITS world coordinate system associated with the data. \item You may store a representation of only a single FrameSet in any individual set of FITS header cards (\emph{i.e.}\ in a single FitsChan) at one time. If you attempt to store more than one, you may over-write the previous one or generate an invalid representation of your WCS information. This is mainly a consequence of the use of fixed FITS keywords by foreign encodings and the fact that you cannot, in general, have multiple FITS cards with the same keyword. \item In general, it will not be possible to store every possible FrameSet that you might construct. Depending on the encoding, only certain FrameSets that conform to particular restrictions can be represented and, even then, some of their information may be lost. See the description of the Encoding attribute in \appref{ss:attributedescriptions} for more details of these limitations. \end{enumerate} It should be understood that using foreign encodings to read and write information held in AST Objects is essentially a process of converting the data format. As such, it potentially suffers from the same problems faced by all such processes, \emph{i.e.}\ differences between the AST data model and that of the foreign encoding may cause some information to be lost. Because the AST model is extremely flexible, however, any data loss can largely be eliminated when reading. Instead, this effect manifests itself in the form of the above encoding-dependent restrictions on the kind of AST Objects which may be written. One of the aims of the AST library, of course, is to insulate you from the details of these foreign encodings and the restrictions they impose. We will see shortly, therefore, how AST provides a mechanism for determining whether your WCS information satisfies the necessary conditions and allows you to make an automatic choice of which encoding to use. \subsection{\label{ss:identifyingfitsencoding}Identifying Foreign Encodings on Input} Let us now examine the practicalities of extracting WCS information from a set of FITS header cards which have been written by some other software system. We will pretend that our program does not know which encoding has been used for the WCS information and must discover this for itself. In order to have a concrete example, however, we will use the following set of cards. These use the FITS-AIPS encoding and contain a typical mix of other FITS cards which are irrelevant to the WCS information in which we are interested: \small \begin{terminalv} SIMPLE = T / Written by IDL: 30-Jul-1997 05:35:42.00 BITPIX = -32 / Bits per pixel. NAXIS = 2 / Number of dimensions NAXIS1 = 300 / Length of x axis. NAXIS2 = 300 / Length of y axis. CTYPE1 = 'GLON-ZEA' / X-axis type CTYPE2 = 'GLAT-ZEA' / Y-axis type CRVAL1 = -149.56866 / Reference pixel value CRVAL2 = -19.758201 / Reference pixel value CRPIX1 = 150.500 / Reference pixel CRPIX2 = 150.500 / Reference pixel CDELT1 = -1.20000 / Degrees/pixel CDELT2 = 1.20000 / Degrees/pixel CROTA1 = 0.00000 / Rotation in degrees. SURVEY = 'COBE DIRBE' BUNITS = 'MJy/sr ' / ORIGIN = 'CDAC ' / Cosmology Data Analysis Center TELESCOP= 'COBE ' / COsmic Background Explorer satellite INSTRUME= 'DIRBE ' / COBE instrument [DIRBE, DMR, FIRAS] PIXRESOL= 9 / Quad tree pixel resolution [6, 9] DATE = '27/09/94' / FITS file creation date (dd/mm/yy) DATE-MAP= '16/09/94' / Date of original file creation (dd/mm/yy) COMMENT COBE specific keywords DATE-BEG= '08/12/89' / date of initial data represented (dd/mm/yy) DATE-END= '25/09/90' / date of final data represented (dd/mm/yy) \end{terminalv} \normalsize c+ The first step is to create a FitsChan and insert these cards into it. If ``cards'' is an array of pointers to character strings holding the header cards and ``ncards'' is the number of cards, this could be done as follows: c- f+ The first step is to create a FitsChan and insert these cards into it. If CARDS is an array of character strings holding the header cards and NCARDS is the number of cards, this could be done as follows: f- c+ \small \begin{terminalv} #include "ast.h" #define MAXCARD 100 AstFitsChan *fitschan; char *cards[ MAXCARD ]; int icard, ncard; ... fitschan = astFitsChan( NULL, NULL, "" ); for ( icard = 0; icard < ncard; icard++ ) astPutFits( fitschan, cards[ icard ], 0 ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INCLUDE 'AST_PAR' INTEGER FITSCHAN, ICARD, NCARD, STATUS CHARACTER * ( 80 ) CARDS( NCARD ) STATUS = 0 ... FITSCHAN = AST_FITSCHAN( AST_NULL, AST_NULL, ' ', STATUS ) DO 1 ICARD = 1, NCARD CALL AST_PUTFITS( FITSCHAN, CARDS( ICARD ), .FALSE., STATUS ) 1 CONTINUE \end{terminalv} \normalsize f- Note that we have not initialised the Encoding attribute of the FitsChan as we did in \secref{ss:creatingafitschan} when we wanted to use the native encoding. This is because we are pretending not to know which encoding to use and want AST to determine this for us. By leaving the Encoding attribute un-set, its default value will adjust to whichever encoding AST considers to be most appropriate, according to the FITS header cards present. For details of how this choice is made, see the description of the Encoding attribute in \appref{ss:attributedescriptions}. This approach has the obvious advantages of making our program simpler and more flexible and of freeing us from having to know about the different encodings available. As a bonus, it also means that the program will be able to read any new encodings that AST may support in future, without needing to be changed. At this point, we could enquire the default value of the Encoding attribute, which indicates which encoding AST intends to use, as follows: c+ \small \begin{terminalv} const char *encode; ... encode = astGetC( fitschan, "Encoding" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CHARACTER * ( 20 ) ENCODE ... ENCODE = AST_GETC( FITSCHAN, 'Encoding', STATUS ) \end{terminalv} \normalsize f- The result of this enquiry would be the string ``FITS-AIPS''. Note that we could also have set the FitsChan's Encoding attribute explicitly, such as when creating it: c+ \small \begin{terminalv} fitschan = astFitsChan( NULL, NULL, "Encoding=FITS-AIPS" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} FITSCHAN = AST_FITSCHAN( AST_NULL, AST_NULL, 'Encoding=FITS-AIPS', STATUS ) \end{terminalv} \normalsize f- If we tried to read information using this encoding (\secref{ss:readingforeignfits}), but failed, we could then change the encoding and try again. This would allow our program to take control of how the optimum choice of encoding is arrived at. However, it would also involve using explicit knowledge of the encodings available and this is best avoided if possible. \subsection{\label{ss:readingforeignfits}Reading Foreign WCS Information from a FITS Header} c+ Having stored a set of FITS header cards in a FitsChan and determined how the WCS information is encoded (\secref{ss:identifyingfitsencoding}), the next step is to read an AST Object from the FitsChan using astRead. We must also remember to rewind the FitsChan first, if necessary, such as by clearing its Card attribute, which defaults to 1: c- f+ Having stored a set of FITS header cards in a FitsChan and determined how the WCS information is encoded (\secref{ss:identifyingfitsencoding}), the next step is to read an AST Object from the FitsChan using AST\_READ. We must also remember to rewind the FitsChan first, if necessary, such as by clearing its Card attribute, which defaults to 1: f- c+ \small \begin{terminalv} AstObject *wcsinfo; ... astClear( fitschan, "Card" ); wcsinfo = astRead( fitschan ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER WCSINFO ... CALL AST_CLEAR( FITSCHAN, 'Card', STATUS ) WCSINFO = AST_READ( FITSCHAN, STATUS ) \end{terminalv} \normalsize f- c+ If the pointer returned by astRead is not equal to AST\_\_NULL, then an Object has been read successfully. Otherwise, there was either no information to read or the choice of FITS encoding (\secref{ss:identifyingfitsencoding}) was inappropriate. c- f+ If the pointer returned by AST\_READ is not equal to AST\_\_NULL, then an Object has been read successfully. Otherwise, there was either no information to read or the choice of FITS encoding (\secref{ss:identifyingfitsencoding}) was inappropriate. f- At this point you might like to indulge in a little data validation along the lines described in \secref{ss:validatinginput}, for example: c+ \small \begin{terminalv} if ( !strcmp( astGetC( wcsinfo, "Class" ), "FrameSet" ) ) { } else { } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} IF ( AST_GETC( WCSINFO, 'Class', STATUS ) .EQ. 'FrameSet' ) THEN ELSE END IF \end{terminalv} \normalsize f- If a foreign encoding has definitely been used, then the Object will automatically be a FrameSet (\secref{ss:foreignfitslimitations}), so this stage can be omitted. However, if the native encoding (\secref{ss:nativeencoding}) might have been employed, which is a possibility if you accept the FitsChan's default Encoding value, then any class of Object might have been read and a quick check would be worthwhile. c+ If you used astShow (\secref{ss:displayingobjects}) to examine the FrameSet which results from reading our example FITS header (\secref{ss:identifyingfitsencoding}), you would find that its base Frame describes the image's pixel coordinate system and that its current Frame is a SkyFrame representing galactic coordinates. These two Frames are inter-related by a Mapping (actually a CmpMap) which incorporates the effects of various rotations, scalings and a ``zenithal equal area'' sky projection, so that each pixel of the FITS image is mapped on to a corresponding sky position in galactic coordinates. c- f+ If you used AST\_SHOW (\secref{ss:displayingobjects}) to examine the FrameSet which results from reading our example FITS header (\secref{ss:identifyingfitsencoding}), you would find that its base Frame describes the image's pixel coordinate system and that its current Frame is a SkyFrame representing galactic coordinates. These two Frames are inter-related by a Mapping (actually a CmpMap) which incorporates the effects of various rotations, scalings and a ``zenithal equal area'' sky projection, so that each pixel of the FITS image is mapped on to a corresponding sky position in galactic coordinates. f- Because this FrameSet may be used both as a Mapping (\secref{ss:framesetasmapping}) and as a Frame (\secref{ss:framesetasframe}), it may be employed directly to perform many useful operations without any need to decompose it into its component parts. These include: \begin{itemize} \item Transforming data grid (FITS pixel) coordinates into galactic coordinates and \emph{vice versa} (\secref{ss:framesetasmapping}). \item Formatting coordinate values (either pixel or galactic coordinates) ready for display to a user (\secref{ss:formattingaxisvalues} and \secref{ss:normalising}). \item Enquiring about axis labels (or other axis information---\secref{ss:frameattributes}) which might be used, for example, to label columns of coordinates in a table (\secref{ss:frameaxisattributes}). \item Aligning the image with another image from which a similar FrameSet has been obtained (\secref{ss:registeringimages}). \item Creating a Plot (\secref{ss:plots}), which can be used to overlay a variety of graphical information (including a coordinate grid---Figure~\ref{fig:gridplot}) on the displayed image. \item Generating a new FrameSet which reflects any geometrical processing you perform on the associated image data (\secref{ss:wcsprocessingexample}). This new FrameSet could then be written out as FITS headers to describe the modified image (\secref{ss:writingforeignfits}). \end{itemize} If the FrameSet contains other Frames (apart from the base and current Frames), then you would also have access to information about other coordinate systems associated with the image. \subsection{\label{ss:destructiveread}Removing WCS Information from FITS Headers---the Destructive Read} It is instructive at this point to examine the contents of a FitsChan after we have read a FrameSet from it (\secref{ss:readingforeignfits}). The following would rewind our FitsChan and display its contents: c+ \small \begin{terminalv} #include char card[ 81 ]; ... astClear( fitschan, "Card" ); while ( astFindFits( fitschan, "%f", card, 1 ) ) (void) printf( "%s\n", card ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CHARACTER CARD * ( 80 ) ... CALL AST_CLEAR( FITSCHAN, 'Card', STATUS ) 2 CONTINUE IF ( AST_FINDFITS( FITSCHAN, '%f', CARD, .TRUE., STATUS ) ) THEN WRITE ( *, '(A)' ) CARD GO TO 2 END IF \end{terminalv} \normalsize f- The output, if we started with the example FITS header in \secref{ss:identifyingfitsencoding}, might look like this: \small \begin{terminalv} SIMPLE = T / Written by IDL: 30-Jul-1997 05:35:42.00 BITPIX = -32 / Bits per pixel. NAXIS = 2 / Number of dimensions NAXIS1 = 300 / Length of x axis. NAXIS2 = 300 / Length of y axis. SURVEY = 'COBE DIRBE' BUNITS = 'MJy/sr ' ORIGIN = 'CDAC ' / Cosmology Data Analysis Center TELESCOP= 'COBE ' / COsmic Background Explorer satellite INSTRUME= 'DIRBE ' / COBE instrument [DIRBE, DMR, FIRAS] PIXRESOL= 9 / Quad tree pixel resolution [6, 9] DATE = '27/09/94' / FITS file creation date (dd/mm/yy) DATE-MAP= '16/09/94' / Date of original file creation (dd/mm/yy) COMMENT COBE specific keywords DATE-BEG= '08/12/89' / date of initial data represented (dd/mm/yy) DATE-END= '25/09/90' / date of final data represented (dd/mm/yy) \end{terminalv} \normalsize c+ Comparing this with the original, you can see that all the FITS cards that represent WCS information have been removed. They have effectively been ``sucked out'' of the FitsChan by the destructive read that astRead performs and converted into an equivalent FrameSet. AST remembers where they were stored, however, so that if we later write WCS information back into the FitsChan (\secref{ss:writingforeignfits}) they will, as far as possible, go back into their original locations. This helps to preserve the overall layout of the FITS header. c- f+ Comparing this with the original, you can see that all the FITS cards that represent WCS information have been removed. They have effectively been ``sucked out'' of the FitsChan by the destructive read that AST\_READ performs and converted into an equivalent FrameSet. AST remembers where they were stored, however, so that if we later write WCS information back into the FitsChan (\secref{ss:writingforeignfits}) they will, as far as possible, go back into their original locations. This helps to preserve the overall layout of the FITS header. f- c+ You can now see why astRead performs destructive reads. It is a mechanism for removing WCS information from a FITS header while insulating you, as a programmer, from the details of the encoding being used. It means you can ensure that all relevant header cards have been removed, giving you a clean slate, without having to know which FITS keywords any particular encoding uses. c- f+ You can now see why AST\_READ performs destructive reads. It is a mechanism for removing WCS information from a FITS header while insulating you, as a programmer, from the details of the encoding being used. It means you can ensure that all relevant header cards have been removed, giving you a clean slate, without having to know which FITS keywords any particular encoding uses. f- Clearing this WCS information out of a FITS header is particularly important when considering how to write new WCS information back after processing (\secref{ss:writingforeignfits}). If any relevant FITS cards are left over from the input dataset and find their way into the new processed header, they could interfere with the new information being written.\footnote{This can happen if a particular keyword is present in the input header but is not used in the output header (whether particular keywords are used can depend on the WCS information being stored). In such a case, the original value would not be over-written by a new output value, so would remain erroneously present.} The destructive read mechanism ensures that this doesn't happen. \subsection{\label{ss:propagatingwcsinformation}Propagating WCS Information through Data Processing Steps} One of the purposes of AST is to make it feasible to propagate WCS information through successive stages of data processing, so that it remains consistent with the associated image data. As far as possible, this should happen regardless of the FITS encoding used to store the original WCS information. If the data processing being performed does not change the relationship between image pixel and world coordinates (whatever these may be), then propagation of the WCS information is straightforward. You can simply copy the FITS header from input to output. If this relationship changes, however, then the WCS information must be processed alongside the image data and a new FITS header generated to represent it. In this case, the sequence of operations within your program would probably be as follows: \begin{enumerate} \item Read the image data and associated FITS header from the input dataset, putting the header cards into a FitsChan (\secref{ss:identifyingfitsencoding}). \item Read an AST Object, a FrameSet, from the FitsChan (typically using a foreign FITS encoding---\secref{ss:readingforeignfits}). \item Process the image data and modify the FrameSet accordingly (\emph{e.g.}~\secref{ss:wcsprocessingexample}). \item Write the FrameSet back into the FitsChan (\secref{ss:writingforeignfits}). \item Perform any other modification of FITS header cards your program may require. \item Write the FitsChan contents (\emph{i.e.}\ processed header cards) and image data to the output dataset. \end{enumerate} In stage (2), the original WCS information will be removed from the FitsChan by a destructive read. Later, in stage (4), new WCS information is written to replace it. This is the process which we consider next (\secref{ss:writingforeignfits}). \subsection{\label{ss:writingforeignfits}Writing Foreign WCS Information to a FITS Header} Before we can write processed WCS information held in a FrameSet back into a FitsChan in preparation for output, we must select the FITS encoding to use. Unfortunately, we cannot simply depend on the default value of the Encoding attribute, as we did when reading the input information (\secref{ss:identifyingfitsencoding}), because the destructive action of reading the WCS data (\secref{ss:destructiveread}) will have altered the FitsChan's contents. This, in turn, will have changed the choice of default encoding, probably causing it to revert to NATIVE. c+ We will return to the question of the optimum choice of encoding below. For now, let's assume that we want to use the same encoding for output as we used for input. Since we enquired what that was before we read the input WCS data from the FitsChan (\secref{ss:identifyingfitsencoding}), we can now set that value explicitly. We can also set the FitsChan's Card attribute back to 1 at the same time (because the write will fail if the FitsChan is not rewound). astWrite can then be used to write the output WCS information into the FitsChan: c- f+ We will return to the question of the optimum choice of encoding below. For now, let's assume we want to use the same encoding for output as we used for input. Since we enquired what that was before we read the input WCS data from the FitsChan (\secref{ss:identifyingfitsencoding}), we can now set that value explicitly. We can also set the FitsChan's Card attribute back to 1 at the same time (because the write will fail if the FitsChan is not rewound). AST\_WRITE can then be used to write the output WCS information into the FitsChan: f- c+ \small \begin{terminalv} int nobj; ... astSet( fitschan, "Card=1, Encoding=%s", encode ); nobj = astWrite( fitschan, wcsinfo ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER NOBJ ... CALL AST_SET( FITSCHAN, 'Card=1, Encoding=' // ENCODE, STATUS ) NOBJ = AST_WRITE( FITSCHAN, WCSINFO, STATUS ) \end{terminalv} \normalsize f- c+ The value returned by astWrite (assigned to ``nobj'') indicates how many Objects were written. This will either be 1 or zero. A value of zero is used to indicate that the information could not be encoded in the form you requested. If this happens, nothing will have been written. c- f+ The value returned by AST\_WRITE (assigned to NOBJ) indicates how many Objects were written. This will either be 1 or zero. A value of zero is used to indicate that the information could not be encoded in the form you requested. If this happens, nothing will have been written. f- If your choice of encoding proves inadequate, the probable reason is that the changes you have made to the FrameSet have caused it to depart from the data model which the encoding assumes. AST knows about the data model used by each encoding and will attempt to simplify the FrameSet you provide so as to fit into that model, thus relieving you of the need to understand the details and limitations of each encoding yourself.\footnote{Storing values in the FitsChan for FITS headers NAXIS1, NAXIS2, \emph{etc.} (the grid dimensions in pixels), before invoking c+ astWrite c- f+ AST\_WRITE f- can sometimes help to produce a successful write.} When this attempt fails, however, you must consider what alternative encoding to use. Ideally, you would probably want to try a sequence of alternative encodings, using an approach such as the following: c+ \small \begin{terminalv} /* 1. */ astSet( fitschan, "Card=1, Encoding=FITS-IRAF" ); if ( !astWrite( fitschan, wcsinfo ) ) { /* 2. */ astSetC( fitschan, "Encoding", encode ); if ( !astWrite( fitschan, wcsinfo ) ) { /* 3. */ astSet( fitschan, "Encoding=NATIVE" ); (void) astWrite( fitschan, wcsinfo ); } } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} * 1. CALL AST_SET( FITSCHAN, 'Card=1, Encoding=FITS-WCS', STATUS ) IF ( AST_WRITE( FITSCHAN, WCSINFO, STATUS ) .EQ. 0 ) THEN * 2. CALL AST_SETC( FITSCHAN, 'Encoding', ENCODE, STATUS ) IF ( AST_WRITE( FITSCHAN, WCSINFO, STATUS ) .EQ. 0 ) THEN * 3. CALL AST_SET( FITSCHAN, 'Encoding=NATIVE', STATUS ) NOBJ = AST_WRITE( FITSCHAN, WCSINFO, STATUS ) END IF END IF \end{terminalv} \normalsize f- That is: \begin{enumerate} \item Start by trying the FITS-WCS encoding, on the grounds that FITS should provide a universal interchange standard in which all WCS information should be expressed if possible. \item If that fails, then try the original encoding used for the input WCS information, on the grounds that you are at least not making the information any harder for others to read than it originally was. \item If that also fails, then you are probably trying to store fairly complex information for which you need the native encoding. Only other AST programs will then be able to read this information, but these are probably the only programs that will be able to do anything sensible with it anyway. \end{enumerate} c+ An alternative approach might be to encode the WCS information in several ways, since this gives the maximum chance that other software will be able to read it. This approach is only possible if there is no significant conflict between the FITS keywords used by the different encodings\footnote{In practice, this means you should avoid mixing FITS-IRAF, FITS-WCS, FITS-AIPS, FITS-AIPS++ and FITS-PC encodings since they share many keywords.}. Adopting this approach would simply require multiple calls to astWrite, rewinding the FitsChan and changing its Encoding value before each one. c- f+ An alternative approach might be to encode the WCS information in several ways, since this gives the maximum chance that other software will be able to read it. This approach is only possible if there is no significant conflict between the FITS keywords used by the different encodings\footnote{In practice, this means you should avoid mixing FITS-IRAF, FITS-WCS, FITS-AIPS, FITS-AIPS++ and FITS-PC encodings since they share many keywords.}. Adopting this approach would simply require multiple calls to AST\_WRITE, rewinding the FitsChan and changing its Encoding value before each one. f- Unfortunately, however, there is a drawback to duplicating WCS information in the FITS header in this way, because any program which modifies one version of this information and simply copies the remainder of the header will risk producing two inconsistent sets of information. This could obviously be confusing to subsequent software. Whether you consider this a worthwhile risk probably depends on the use to which you expect your data to be put. \cleardoublepage \section{\label{ss:xmlchan}Storing AST Objects as XML (XmlChan)} \htmladdnormallinkfoot{XML}{http://www.w3.org/XML/} is fast becoming the standard format for passing structured data around the internet, and much general purpose software has been written for tasks such as the parsing, editing, display and transformation of XML data. The XmlChan class (a specialised form of Channel) provides facilities for storing AST objects externally in the form of XML documents, thus allowing such software to be used. The primary XML format used by the XmlChan class is a fairly close transliteration of the AST native format produced by the basic Channel class. Currently, there is no DTD or schema defining the structure of data produced in this format by an XmlChan. The following is a native AST representation of a simple 1-D Frame (including comments and with the Full attribute set to zero so that some default attribute values are included as extra comments): \small \begin{terminalv} Begin Frame # Coordinate system description # Title = "1-d coordinate system" # Title of coordinate system Naxes = 1 # Number of coordinate axes Domain = "SCREEN" # Coordinate system domain # Lbl1 = "Axis 1" # Label for axis 1 # Uni1 = "cm" # Units for axis 1 Ax1 = # Axis number 1 Begin Axis # Coordinate axis Unit = "cm" # Axis units End Axis End Frame \end{terminalv} \normalsize The corresponding XmlChan output would look like: \small \begin{terminalv} <_attribute name="Title" quoted="true" value="1-d coordinate system" desc="Title of coordinate system" default="true"/> <_attribute name="Naxes" value="1" desc="Number of coordinate axes"/> <_attribute name="Domain" quoted="true" value="SCREEN" desc="Coordinate system domain"/> <_attribute name="Lbl1" quoted="true" value="Axis 1" desc="Label for axis 1" default="true"/> <_attribute name="Uni1" quoted="true" value="cm" desc="Units for axis 1" default="true"/> <_attribute name="Unit" quoted="true" value="cm" desc="Axis units"/> \end{terminalv} \normalsize Notes: \begin{enumerate} \item The AST class name is used as the name for an XML element which contain a description of an AST object. \item AST attributes are described by XML elements with the name ``\_attribute''. Unfortunately, the word ``attribute'' is also used by XML to refer to a ``name=value'' pair within an element start tag. So for instance, the ``Title'' attribute of the AST Frame object is described within an XML element with name ``\_attribute'' in which the XML attribute ``name'' has the value ``Title'', and the XML attribute ``value'' has the value ``1-d coordinate system''. The moral is always to be clear clear about the context (AST or XML) in which the word \emph{attribute} is being used! \item The XML includes comments both as XML attributes with the name ``desc'', and as separate comment tags. \item Elements which describe default values are identified by the fact that they have an XML attribute called ``default'' set to the value ``true''. These elements are ignored when being read back into an XmlChan. \item The outer-most XML element of an AST object will set the default namespace to \verb+http://www.starlink.ac.uk/ast/xml/+ which will be inherited by all nested elements. \end{enumerate} The XmlChan class changes the default value for the Comment and Full attributes (inherited from the base Channel class) to zero and -1, resulting in terse output by default. With the default values for these attributes, the above XML is reduced to the following: \small \begin{terminalv} <_attribute name="Naxes" value="1"/> <_attribute name="Domain" quoted="true" value="SCREEN"/> <_attribute name="Unit" quoted="true" value="cm"/> \end{terminalv} \normalsize The XmlChan class uses the Skip attributes very similarly to the Channel class. If Skip is zero (the default) then an error will be reported if the text supplied by the source function does not begin with an AST Object. If Skip is non-zero, then initial text is skipped over without error until the start of an AST object is found. this allows an AST object to be located within a larger XML document. \subsection{Reading IVOA Space-Time-Coordinates XML (STC-X) Descriptions} The XmlChan class also provides support for reading (but not writing) XML documents which use a restricted subset of an early draft (V1.20) of the IVOA Space-Time-Coordinates XML (STC-X) system. The version of STC-X finally adopted by the IVOA differs in several significant respects from V1.20, and so the STC-X support currently provided by AST is mainly of historical interest. Note, AST also supports the alternative ``STC-S'' linear string description of the STC model (see \secref{ss:stcschans}). STC-X V1.20 is documented at \url{http://www.ivoa.net/Documents/WD/STC/STC-20050225.html}, and the current version is documented at \url{http://www.ivoa.net/Documents/latest/STC-X.html}. When an STC-X document is read using an XmlChan, the read operation produces an AST Object of the Stc class, which is itself a subclass of Region. Specifically, each such Object will be an instance of StcSearchLocation, StcResourceProfile, StcCatalogEntryLocation or StcObsDataLocation. See the description of the XmlChan class and the XmlFormat attribute for further details. \cleardoublepage \section{\label{ss:stcschans}Reading and writing STC-S descriptions (StcsChans)} The StcsChan class provides facilities for reading and writing IVOA ``STC-S'' descriptions. STC-S (see \url{http://www.ivoa.net/Documents/latest/STC-S.html}) is a linear string syntax that allows simple specification of the STC metadata describing a region in an astronomical coordinate system. AST supports a subset of the STC-S specification, allowing an STC-S description of a region within an AST-supported astronomical coordinate system to be converted into an equivalent AST Region object, and vice-versa. For further details, see the full description of the StcsChan class in \appref{ss:classdescriptions}. \cleardoublepage \section{\label{ss:intramaps}Creating Your Own Private Mappings (IntraMaps)} \subsection{The Need for Extensibility} However many Mapping classes are provided by AST, sooner or later you will want to transform coordinates in some way that has not been foreseen. You might want to plot a graph in some novel curvilinear coordinate system (perhaps you already have a WCS system in your software and just want to use AST for its graphical capabilities). Alternatively, you might need to calibrate a complex dataset (like an objective prism plate) where each position must be converted to world coordinates with reference to calibration data under the control of an elaborate algorithm. In such cases, it is clear that the basic pre-formed components provided by AST for building Mappings are just not enough. What you need is access to a programming language. However, if you write your own software to transform coordinate values, then it must be made available in the form of an AST class (from which you can create Objects) before it can be used in conjunction with other AST facilities. At this point you might consider writing your own AST class, but this is not recommended. Not only would the internal conventions used by AST take some time to master, but you might also find yourself having to change your software whenever a new version of AST was released. Fortunately, there is a much easier route provided by the IntraMap class. \subsection{The IntraMap Model} c+ To allow you to write your own Mappings, AST provides a special kind of Mapping called an IntraMap. An IntraMap is a sort of ``wrapper'' for a coordinate transformation function written in C. You write this function yourself and then register it with AST. This, in effect, creates a new class from which you can create Mappings (\emph{i.e.}\ IntraMaps) which will transform coordinates in whatever way your transformation function specifies. c- f+ To allow you to write your own Mappings, AST provides a special kind of Mapping called an IntraMap. An IntraMap is a sort of ``wrapper'' for a coordinate transformation routine written in Fortran. You write this routine yourself and then register it with AST. This, in effect, creates a new class from which you can create Mappings (\emph{i.e.}\ IntraMaps) which will transform coordinates in whatever way your transformation routine specifies. f- Because IntraMaps are Mappings, they may be used in the same way as any other Mapping. For instance, they may be combined in series or parallel with other Mappings using a CmpMap (\secref{ss:cmpmaps}), they may be inverted (\secref{ss:invertingmappings}), you may enquire about their attributes (\secref{ss:gettingattributes}), they may be inserted into FrameSets (\secref{ss:framesets}), \emph{etc.} They do, however, have some important limitations of which you should be aware before we go on to consider how to create them. \subsection{\label{ss:intramaplimitations}Limitations of IntraMaps} c+ By now, you might be wondering why any other kind of Mapping is required at all. After all, why not simply write your own coordinate transformation functions in C, wrap them up in IntraMaps and do away with all the other Mapping classes in AST? c- f+ By now, you might be wondering why any other kind of Mapping is required at all. After all, why not simply write your own coordinate transformation routines in Fortran, wrap them up in IntraMaps and do away with all the other Mapping classes in AST? f- c+ The reason is not too hard to find. Any transformation function you write is created solely by you, so it is a private extension which does not form a permanent part of AST. If you use it to calibrate some data and then pass that data to someone else, who has only the standard version of AST, then they will not be able to interpret it. c- f+ The reason is not too hard to find. Any transformation routine you write is created solely by you, so it is a private extension which does not form a permanent part of AST. If you use it to calibrate some data and then pass that data to someone else, who has only the standard version of AST, then they will not be able to interpret it. f- c+ Thus, while an IntraMap is fine for use by you and your collaborators (who we assume have access to the same transformation functions), it does not address the need for universal data exchange like other AST Mappings do. This is where the ``Intra'' in the class name ``IntraMap'' comes from, implying private or internal usage. c- f+ Thus, while an IntraMap is fine for use by you and your collaborators (who we assume have access to the same transformation routines), it does not address the need for universal data exchange like other AST Mappings do. This is where the ``Intra'' in the class name ``IntraMap'' comes from, implying private or internal usage. f- For this reason, it is unwise to store IntraMaps in datasets, unless they will be used solely for communication between collaborating items of software which share conventions about their use. A private database describing coordinate systems on a graphics device might be an example where IntraMaps would be suitable, because the data would probably never be accessed by anyone else's software. Restricting IntraMap usage to within a single program (\emph{i.e.} never writing it out) is, of course, completely safe. c+ If, by accident, an IntraMap should happen to escape as part of a dataset, then the unsuspecting recipient is likely to receive an error message when they attempt to read the data. However, AST will associate details of the IntraMap's transformation function and its author (if provided) with the data, so that the recipient can make an intelligent enquiry to obtain the necessary software if this proves essential. c- f+ If, by accident, an IntraMap should happen to escape as part of a dataset, then the unsuspecting recipient is likely to receive an error message when they attempt to read the data. However, AST will associate details of the IntraMap's transformation routine and its author (if provided) with the data, so that the recipient can make an intelligent enquiry to obtain the necessary software if this proves essential. f- c+ \subsection{\label{ss:transformationfunctions}Writing a Transformation Function} c- f+ \subsection{\label{ss:transformationfunctions}Writing a Transformation Routine} f- c+ The first stage in creating an IntraMap is to write the coordinate transformation function. This should have a calling interface like the astTranP function provided by AST (\emph{q.v.}). Here is a simple example of a suitable transformation function which transforms coordinates by squaring them: c- f+ The first stage in creating an IntraMap is to write the coordinate transformation routine. This should have a calling interface like the AST\_TRANN function provided by AST (\emph{q.v.}). Here is a simple example of a suitable transformation routine which transforms coordinates by squaring them: f- \xlabel{SqrTran} c+ \small \begin{terminalv} #include "ast.h" #include void SqrTran( AstMapping *this, int npoint, int ncoord_in, const double *ptr_in[], int forward, int ncoord_out, double *ptr_out[] ) { int point, coord; double x; /* Forward transformation. */ if ( forward ) { for ( point = 0; point < npoint; point++ ) { for ( coord = 0; coord < ncoord_in; coord++ ) { x = ptr_in[ coord ][ point ]; ptr_out[ coord ][ point ] = ( x == AST__BAD ) ? AST__BAD : x * x; } } /* Inverse transformation. */ } else { for ( point = 0; point < npoint; point++ ) { for ( coord = 0; coord < ncoord_in; coord++ ) { x = ptr_in[ coord ][ point ]; ptr_out[ coord ][ point ] = ( x < 0.0 || x == AST__BAD ) ? AST__BAD : sqrt( x ); } } } } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} SUBROUTINE SQRTRAN( THIS, NPOINT, NCOORD_IN, INDIM, IN, FORWARD, : NCOORD_OUT, OUTDIM, OUT, STATUS ) INTEGER THIS, NPOINT, NCOORD_IN, INDIM, NCOORD_OUT, OUTDIM, STATUS DOUBLE PRECISION IN( INDIM, NCOORD_IN ), OUT( OUTDIM, NCOORD_OUT ) LOGICAL FORWARD INCLUDE 'AST_PAR' DOUBLE PRECISION X INTEGER COORD, POINT * Forward transformation. IF ( FORWARD ) THEN DO 2 POINT = 1, NPOINT DO 1 COORD = 1, NCOORD_IN X = IN( POINT, COORD ) IF ( X .EQ. AST__BAD ) THEN OUT( POINT, COORD ) = AST__BAD ELSE OUT( POINT, COORD ) = X * X ENDIF 1 CONTINUE 2 CONTINUE * Inverse transformation. ELSE DO 4 POINT = 1, NPOINT DO 3 COORD = 1, NCOORD_IN X = IN( POINT, COORD ) IF ( X .LT. 0.0D0 .OR. X .EQ. AST__BAD ) THEN OUT( POINT, COORD ) = AST__BAD ELSE OUT( POINT, COORD ) = SQRT( X ) ENDIF 3 CONTINUE 4 CONTINUE ENDIF END \end{terminalv} \normalsize f- c+ As you can see, the function comes in two halves which implement the forward and inverse coordinate transformations. The number of points to be transformed (``npoint'') and the numbers of input and output coordinates per point (``ncoord\_in'' and ``ncoord\_out''---in this case both are assumed equal) are passed to the function. A pair of loops then accesses all the coordinate values. Note that it is legitimate to omit one or other of the forward/inverse transformations and simply not to implement it, if it will not be required. It is also permissible to require that the numbers of input and output coordinates be fixed (\emph{e.g.}\ at 2), or to write the function so that it can handle arbitrary dimensionality, as here. c- f+ As you can see, the routine comes in two halves which implement the forward and inverse coordinate transformations. The number of points to be transformed (NPOINT) and the numbers of input and output coordinates per point (NCOORD\_IN and NCOORD\_OUT---in this case both are assumed equal) are passed to the routine. A pair of loops then accesses all the coordinate values. Note that it is legitimate to omit one or other of the forward/inverse transformations and simply not to implement it, if it will not be required. It is also permissible to require that the numbers of input and output coordinates be fixed (\emph{e.g.}\ at 2), or to write the routine so that it can handle arbitrary dimensionality, as here. f- c+ Before using an incoming coordinate, the function must first check that it is not set to the value AST\_\_BAD, which indicates missing data (\secref{ss:badcoordinates}). If it is, the same value is also assigned to any affected output coordinates. The value AST\_\_BAD is also generated if any coordinates cannot be transformed. In this example, this can happen with the inverse transformation if negative values are encountered, so that the square root cannot be taken. c- f+ Before using an incoming coordinate, the routine must first check that it is not set to the value AST\_\_BAD, which indicates missing data (\secref{ss:badcoordinates}). If it is, the same value is also assigned to any affected output coordinates. The value AST\_\_BAD is also generated if any coordinates cannot be transformed. In this example, this can happen with the inverse transformation if negative values are encountered, so that the square root cannot be taken. f- c+ There are very few restrictions on what a coordinate transformation function may do. For example, it may freely perform I/O to access any external data needed, it may invoke other AST facilities (but beware of unwanted recursion), \emph{etc.} Typically, you may also want to pass information to it \emph{via}\ global variables. Remember, however, that whatever facilities the transformation function requires must be available in every program which uses it. c- f+ There are very few restrictions on what a coordinate transformation routine may do. For example, it may freely perform I/O to access any external data needed, it may invoke other AST facilities (but beware of unwanted recursion), \emph{etc.} Typically, you may also want to pass information to it \emph{via}\ global variables held in common blocks. Remember, however, that whatever facilities the transformation routine requires must be available in every program which uses it. f- c+ Generally, it is not a good idea to retain context information within a transformation function. That is, it should transform each set of coordinates as a single point and retain no memory of the points it has transformed before. This is in order to conform with the AST model of a Mapping. c- f+ Generally, it is not a good idea to retain context information within a transformation routine. That is, it should transform each set of coordinates as a single point and retain no memory of the points it has transformed before. This is in order to conform with the AST model of a Mapping. f- c+ If an error occurs within a transformation function, it should use the astSetStatus function (\secref{ss:errordetection}) to set the AST status to an error value before returning. This will alert AST to the error, causing it to abort the current operation. The error value AST\_\_ITFER is available for this purpose, but other values may also be used (\emph{e.g.}\ if you wish to distinguish different types of error). c- f+ If an error occurs within a transformation routine, it should set its STATUS argument to an error value before returning. This will alert AST to the error, causing it to abort the current operation. The error value AST\_\_ITFER is available for this purpose, but other values may also be used (\emph{e.g.}\ if you wish to distinguish different types of error). The AST\_\_ITFER error value is defined in the AST\_ERR include file. f- c+ \subsection{\label{ss:registeringintramaps}Registering a Transformation Function} c- f+ \subsection{\label{ss:registeringintramaps}Registering a Transformation Routine} f- c+ Having written your coordinate transformation function, the next step is to register it with AST. Registration is performed using astIntraReg, as follows: c- f+ Having written your coordinate transformation routine, the next step is to register it with AST. Registration is performed using AST\_INTRAREG, as follows: f- c+ \small \begin{terminalv} void SqrTran( AstMapping *, int, int, const double *[], int, int, double *[] ); const char *author, *contact, *purpose; ... purpose = "Square each coordinate value"; author = "R.F. Warren-Smith & D.S. Berry"; contact = "http://www.starlink.ac.uk/cgi-bin/htxserver/sun211.htx/?xref_SqrTran"; astIntraReg( "SqrTran", 2, 2, SqrTran, 0, purpose, author, contact ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} EXTERNAL SQRTRAN CHARACTER * ( 80 ) AUTHOR, CONTACT, PURPOSE ... PURPOSE = 'Square each coordinate value' AUTHOR = 'R.F. Warren-Smith & D.S. Berry' CONTACT = 'http://www.starlink.ac.uk/cgi-bin/htxserver/' // 'sun210.htx/?xref_SqrTran' CALL AST_INTRAREG( 'SqrTran', 2, 2, SQRTRAN, 0, : PURPOSE, AUTHOR, CONTACT, STATUS ) \end{terminalv} \normalsize f- c+ Note that you should also provide a function prototype to describe the transformation function (the implementation of the function itself would suffice, of course). c- f+ Note that the transformation routine must also appear in a Fortran EXTERNAL statement. f- c+ The first argument to astIntraReg is a name by which the transformation function will be known. This will be used when we come to create an IntraMap and is case sensitive. We recommend that you use the actual function name here and make this sufficiently unusual that it is unlikely to clash with any other functions in most people's software. c- f+ The first argument to AST\_INTRAREG is a name by which the transformation routine will be known. This will be used when we come to create an IntraMap and is case sensitive. We recommend that you base this on the actual routine name and make this sufficiently unusual that it is unlikely to clash with any other routines in most people's software. f- c+ The next two arguments specify the number of input and output coordinates which the transformation function will handle. These correspond with the Nin and Nout attributes of the IntraMap we will create. Here, we have set them both to 2, which means that we will only be able to create IntraMaps with 2 input and 2 output coordinates (despite the fact that the transformation function can actually handle other dimensionalities). We will see later (\secref{ss:variableintramapcoordinates}) how to remove this restriction. c- f+ The next two arguments specify the number of input and output coordinates which the transformation routine will handle. These correspond with the Nin and Nout attributes of the IntraMap we will create. Here, we have set them both to 2, which means that we will only be able to create IntraMaps with 2 input and 2 output coordinates (despite the fact that the transformation routine can actually handle other dimensionalities). We will see later (\secref{ss:variableintramapcoordinates}) how to remove this restriction. f- c+ The fourth argument should contain a set of flags which describe the transformation function in a little more detail. We will return to this shortly (\secref{ss:restrictedintramaps} \& \secref{ss:simplifyingintramaps}). For now, we supply a value of zero. c- f+ The fourth argument should contain a set of flags which describe the transformation routine in a little more detail. We will return to this shortly (\secref{ss:restrictedintramaps} \& \secref{ss:simplifyingintramaps}). For now, we supply a value of zero. f- c+ The remaining arguments are character strings which document the transformation function, mainly for the benefit of anyone who is unfortunate enough to encounter a reference to it in their data which they cannot interpret. As explained above (\secref{ss:intramaplimitations}), you should try and avoid this, but accidents will happen, so you should always provide strings containing the following: c- f+ The remaining arguments are character strings which document the transformation routine, mainly for the benefit of anyone who is unfortunate enough to encounter a reference to it in their data which they cannot interpret. As explained above (\secref{ss:intramaplimitations}), you should try and avoid this, but accidents will happen, so you should always provide strings containing the following: f- \begin{enumerate} c+ \item A short description of what the transformation function is for. c- f+ \item A short description of what the transformation routine is for. f- \item The name of the author. \item Contact details, such as an e-mail or WWW address. \end{enumerate} c+ The idea is that anyone finding an IntraMap in their data, but lacking the necessary transformation function, should be able to contact the author and make a sensible enquiry in order to obtain it. If you expect many enquiries, you may like to set up a World Wide Web page and use that instead (in the example above, we use the WWW address of the relevant part of this document). c- f+ The idea is that anyone finding an IntraMap in their data, but lacking the necessary transformation routine, should be able to contact the author and make a sensible enquiry in order to obtain it. If you expect many enquiries, you may like to set up a World Wide Web page and use that instead (in the example above, we use the WWW address of the relevant part of this document). f- \subsection{Creating an IntraMap} c+ Once a transformation function has been registered, creating an IntraMap from it is simple: c- f+ Once a transformation routine been registered, creating an IntraMap from it is simple: f- c+ \small \begin{terminalv} AstIntraMap *intramap; ... intramap = astIntraMap( "SqrTran", 2, 2, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER INTRAMAP ... INTRAMAP = AST_INTRAMAP( 'SqrTran', 2, 2, ' ', STATUS ); \end{terminalv} \normalsize f- c+ We simply use the astIntraMap constructor function and pass it the name of the transformation function to use. This name is the same (case sensitive) one that we associated with the function when we registered it using astIntraReg (\secref{ss:registeringintramaps}). c- f+ We simply use the AST\_INTRAMAP constructor function and pass it the name of the transformation routine to use. This name is the same (case sensitive) one that we associated with the routine when we registered it using AST\_INTRAREG (\secref{ss:registeringintramaps}). f- c+ You can, of course, register any number of transformation functions and select which one to use whenever you create an IntraMap. You can also create any number of independent IntraMaps using each transformation function. In this sense, each transformation function you register effectively creates a new ``sub-class'' of IntraMap, from which you can create Objects just like any other class. However, an error will occur if you attempt to use a transformation function that has not yet been registered. c- f+ You can, of course, register any number of transformation routines and select which one to use whenever you create an IntraMap. You can also create any number of independent IntraMaps using each transformation routine. In this sense, each transformation routine you register effectively creates a new ``sub-class'' of IntraMap, from which you can create Objects just like any other class. However, an error will occur if you attempt to use a transformation routine that has not yet been registered. f- c+ The second and third arguments to astIntraMap are the numbers of input and output coordinates. These define the Nin and Nout attributes for the IntraMap that is created and they must match the corresponding numbers given when the transformation function was registered. c- f+ The second and third arguments to AST\_INTRAMAP are the numbers of input and output coordinates. These define the Nin and Nout attributes for the IntraMap that is created and they must match the corresponding numbers given when the transformation routine was registered. f- c+ The final argument is the usual attribute initialisation string. You may set attribute values for an IntraMap in exactly the same way as for any other Mapping (\secref{ss:settingattributes}, and also see \secref{ss:intraflag}). c- f+ The penultimate argument is the usual attribute initialisation string. You may set attribute values for an IntraMap in exactly the same way as for any other Mapping (\secref{ss:settingattributes}, and also see \secref{ss:intraflag}). f- c+ \subsection{\label{ss:restrictedintramaps}Restricted Implementations of Transformation Functions} c- f+ \subsection{\label{ss:restrictedintramaps}Restricted Implementations of Transformation Routines} f- c+ You may not always want to use both the forward and inverse transformations when you create an IntraMap, so it is possible to omit either from the underlying coordinate transformation function. Consider the following, for example: c- f+ You may not always want to use both the forward and inverse transformations when you create an IntraMap, so it is possible to omit either from the underlying coordinate transformation routine. Consider the following, for example: f- c+ \small \begin{terminalv} void Poly3Tran( AstMapping *this, int npoint, int ncoord_in, const double *ptr_in[], int forward, int ncoord_out, double *ptr_out[] ) { double x; int point; /* Forward transformation. */ for ( point = 0; point < npoint; point++ ) { x = ptr_in[ 0 ][ point ]; ptr_out[ 0 ][ point ] = ( x == AST__BAD ) ? AST__BAD : 6.18 + x * ( 0.12 + x * ( -0.003 + x * 0.0000101 ) ); } } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} SUBROUTINE POLY3TRAN( THIS, NPOINT, NCOORD_IN, INDIM, IN, FORWARD, : NCOORD_OUT, OUTDIM, OUT, STATUS ) INTEGER THIS, NPOINT, NCOORD_IN, INDIM, NCOORD_OUT, OUTDIM, STATUS DOUBLE PRECISION IN( INDIM, NCOORD_IN ), OUT( OUTDIM, NCOORD_OUT ) LOGICAL FORWARD INCLUDE 'AST_PAR' DOUBLE PRECISION X INTEGER POINT * Forward transformation. DO 1 POINT = 1, NPOINT X = IN( POINT, 1 ) IF ( X .EQ. AST__BAD ) THEN OUT( POINT, 1 ) = AST__BAD ELSE OUT( POINT, 1 ) = : 6.18D0 + X * ( 0.12D0 + X * ( -0.003D0 + X * 0.0000101D0 ) ) END IF 1 CONTINUE END \end{terminalv} \normalsize f- c+ This implements a 1-dimensional cubic polynomial transformation. Since this is somewhat awkward to invert, however, we have only implemented the forward transformation. When registering the function, this is indicated via the ``flags'' argument to astIntraReg, as follows: c- f+ This implements a 1-dimensional cubic polynomial transformation. Since this is somewhat awkward to invert, however, we have only implemented the forward transformation. When registering the routine, this is indicated via the FLAGS argument to AST\_INTRAREG, as follows: f- c+ \small \begin{terminalv} void Poly3Tran( AstMapping *, int, int, const double *[], int, int, double *[] ); ... astIntraReg( "Poly3Tran", 1, 1, Poly3Tran, AST__NOINV, purpose, author, contact ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} EXTERNAL POLY3TRAN ... CALL AST_INTRAREG( 'Poly3Tran', 1, 1, POLY3TRAN, AST__NOINV, : PURPOSE, AUTHOR, CONTACT, STATUS ) \end{terminalv} \normalsize f- c+ Here, the fifth argument has been set to the flag value AST\_\_NOINV to indicate the lack of an inverse. If the forward transformation were absent, we would use AST\_\_NOFOR instead. Flag values for this argument may be combined using a bitwise OR if necessary. c- f+ Here, the fifth argument has been set to the flag value AST\_\_NOINV to indicate the lack of an inverse. If the forward transformation were absent, we would use AST\_\_NOFOR instead. Flag values for this argument may be combined by summing them if necessary. f- \subsection{\label{ss:variableintramapcoordinates}Variable Numbers of Coordinates} c+ In our earlier examples, we have used a fixed number of input and output coordinates when registering a coordinate transformation function. It is not necessary to impose this restriction, however, if the transformation function can cope with a variable number of coordinates (as with the example in \secref{ss:transformationfunctions}). We indicate the acceptability of a variable number when registering the transformation function by supplying the value AST\_\_ANY for the number of input and/or output coordinates, as follows: c- f+ In our earlier examples, we have used a fixed number of input and output coordinates when registering a coordinate transformation routine. It is not necessary to impose this restriction, however, if the transformation routine can cope with a variable number of coordinates (as with the example in \secref{ss:transformationfunctions}). We indicate the acceptability of a variable number when registering the transformation routine by supplying the value AST\_\_ANY for the number of input and/or output coordinates, as follows: f- c+ \small \begin{terminalv} astIntraReg( "SqrTran", AST__ANY, AST__ANY, SqrTran, 0, purpose, author, contact ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_INTRAREG( 'SqrTran', AST__ANY, AST__ANY, SQRTRAN, 0, : PURPOSE, AUTHOR, CONTACT, STATUS ) \end{terminalv} \normalsize f- The result is that an IntraMap may now be created with any number of input and output coordinates. For example: c+ \small \begin{terminalv} AstIntraMap *intramap1, *intramap2; ... intramap1 = astIntraMap( "SqrTran", 1, 1, "" ); intramap2 = astIntraMap( "SqrTran", 3, 3, "Invert=1" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER INTRAMAP1, INTRAMAP2 ... INTRAMAP1 = AST_INTRAMAP( 'SqrTran', 1, 1, ' ', STATUS ) INTRAMAP2 = AST_INTRAMAP( 'SqrTran', 3, 3, 'Invert=1', STATUS ) \end{terminalv} \normalsize f- c+ It is possible to fix either the number of input or output coordinates (by supplying an explicit number to astIntraReg), but more subtle restrictions on the number of coordinates, such as requiring that Nin and Nout be equal, are not supported. This means that: c- f+ It is possible to fix either the number of input or output coordinates (by supplying an explicit number to AST\_INTRAREG), but more subtle restrictions on the number of coordinates, such as requiring that Nin and Nout be equal, are not supported. This means that: f- c+ \small \begin{terminalv} intramap = astIntraMap( "SqrTran", 1, 2, "" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTRAMAP = AST_INTRAMAP( 'SqrTran', 1, 2, ' ', STATUS ) \end{terminalv} \normalsize f- c+ will be accepted without error, although the transformation function cannot actually handle such a combination sensibly. If this is important, it would be worth adding a check within the transformation function itself, so that the error would be detected when it came to be used. c- f+ will be accepted without error, although the transformation routine cannot actually handle such a combination sensibly. If this is important, it would be worth adding a check within the transformation routine itself, so that the error would be detected when it came to be used. f- c+ \subsection{\label{ss:intraflag}Adapting a Transformation Function to Individual IntraMaps} c- f+ \subsection{\label{ss:intraflag}Adapting a Transformation Routine to Individual IntraMaps} f- c+ In the examples given so far, our coordinate transformation functions have not made use of the ``this'' pointer passed to them (which identifies the IntraMap whose transformation we are implementing). In practice, this will often be the case. However, the presence of the ``this'' pointer allows the transformation function to invoke any other AST function on the IntraMap, and this permits enquiries about its attributes. The transformation function's behaviour can therefore be modified according to any attribute values which are set. This turns out to be a useful thing to do, so each IntraMap has a special IntraFlag attribute reserved for exactly this purpose. c- f+ In the examples given so far, our coordinate transformation routines have not made use of the THIS pointer passed to them (which identifies the IntraMap whose transformation we are implementing). In practice, this will often be the case. However, the presence of the THIS pointer allows the transformation routine to invoke any other AST routine on the IntraMap, and this permits enquiries about its attributes. The transformation routine's behaviour can therefore be modified according to any attribute values which are set. This turns out to be a useful thing to do, so each IntraMap has a special IntraFlag attribute reserved for exactly this purpose. f- c+ Consider, for instance, the case where the transformation function has access to several alternative sets of internally-stored data which it may apply to perform its transformation. Rather than implement many different versions of the transformation function, you may switch between them by setting a value for the IntraFlag attribute when you create an instance of an IntraMap, for example: c- f+ Consider, for instance, the case where the transformation routine has access to several alternative sets of internally-stored data which it may apply to perform its transformation. Rather than implement many different versions of the transformation routine, you may switch between them by setting a value for the IntraFlag attribute when you create an instance of an IntraMap, for example: f- c+ \small \begin{terminalv} intramap1 = astIntraMap( "MyTran", 2, 2, "IntraFlag=A" ); intramap2 = astIntraMap( "MyTran", 2, 2, "IntraFlag=B" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTRAMAP1 = AST_INTRAMAP( 'MyTran', 2, 2, 'IntraFlag=A', STATUS ) INTRAMAP2 = AST_INTRAMAP( 'MyTran', 2, 2, 'IntraFlag=B', STATUS ) \end{terminalv} \normalsize f- c+ The transformation function may then enquire the value of the IntraFlag attribute (\emph{e.g.}\ using astGetC and passing it the ``this'' pointer) and use whichever dataset is required for that particular IntraMap. c- f+ The transformation routine may then enquire the value of the IntraFlag attribute (\emph{e.g.}\ using AST\_GETC and passing it the THIS pointer) and use whichever dataset is required for that particular IntraMap. f- This approach is particularly useful when the number of possible transformations is unbounded or not known in advance, in which case the IntraFlag attribute may be used to hold numerical values encoded as part of a character string (effectively using them as data for the IntraMap). It is also superior to the use of a global switch for communication (\emph{e.g.}\ setting an index to select the ``current'' data before using the IntraMap), because it continues to work when several IntraMaps are embedded within a more complex compound Mapping, when you may have no control over the order in which they are used. \subsection{\xlabel{MaxTran}\label{ss:simplifyingintramaps}Simplifying IntraMaps} c+ A notable disadvantage of IntraMaps is that they are ``black boxes'' as far as AST is concerned. This means that they have limited ability to participate in the simplification of compound Mappings performed, \emph{e.g.}, by astSimplify (\secref{ss:simplifyingcmpmaps}), because AST cannot know how they interact with other Mappings. In reality, of course, they will often implement such specialised coordinate transformations that the simplification possibilities will be rather limited anyway. c- f+ A notable disadvantage of IntraMaps is that they are ``black boxes'' as far as AST is concerned. This means that they have limited ability to participate in the simplification of compound Mappings performed, \emph{e.g.}, by AST\_SIMPLIFY (\secref{ss:simplifyingcmpmaps}), because AST cannot know how they interact with other Mappings. In reality, of course, they will often implement such specialised coordinate transformations that the simplification possibilities will be rather limited anyway. f- One important simplification, however, is the ability of a Mapping to cancel with its own inverse to yield a unit Mapping (a UnitMap). This is important because Mappings are frequently used to relate a dataset to some external standard (a celestial coordinate system, for example). When inter-relating two similar datasets calibrated using the same standard, part of the Mapping often cancels, because it is applied first in one direction and then the other, effectively eliminating the reference to the standard. This is often a useful simplification and can lead to greater efficiency. c+ Many transformations have this property of cancelling with their own inverse, but not necessarily all. Consider the following transformation function, for example: c- f+ Many transformations have this property of cancelling with their own inverse, but not necessarily all. Consider the following transformation routine, for example: f- c+ \small \begin{terminalv} void MaxTran( AstMapping *this, int npoint, int ncoord_in, const double *ptr_in[], int forward, int ncoord_out, double *ptr_out[] ) { double hi, x; int coord, point; /* Forward transformation. */ if ( forward ) { for ( point = 0; point < npoint; point++ ) { hi = AST__BAD; for ( coord = 0; coord < ncoord_in; coord++ ) { x = ptr_in[ coord ][ point ]; if ( x != AST__BAD ) { if ( x > hi || hi == AST__BAD ) hi = x; } } ptr_out[ 0 ][ point ] = hi; } /* Inverse transformation. */ } else { for ( coord = 0; coord < ncoord_out; coord++ ) { for ( point = 0; point < npoint; point++ ) { ptr_out[ coord ][ point ] = ptr_in[ 0 ][ point ]; } } } } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} SUBROUTINE MAXTRAN( THIS, NPOINT, NCOORD_IN, INDIM, IN, FORWARD, : NCOORD_OUT, OUTDIM, OUT, STATUS ) INTEGER THIS, NPOINT, NCOORD_IN, INDIM, NCOORD_OUT, OUTDIM, STATUS DOUBLE PRECISION IN( INDIM, NCOORD_IN ), OUT( OUTDIM, NCOORD_OUT ) LOGICAL FORWARD INCLUDE 'AST_PAR' DOUBLE PRECISION HI, X INTEGER COORD, POINT * Forward transformation. IF ( FORWARD ) THEN DO 2 POINT = 1, NPOINT HI = AST__BAD DO 1 COORD = 1, NCOORD_IN X = IN( POINT, COORD ) IF ( X .NE. AST__BAD ) THEN IF ( X .GT. HI .OR. HI .EQ. AST__BAD ) HI = X END IF 1 CONTINUE 2 CONTINUE * Inverse transformation. ELSE DO 4 COORD = 1, NCOORD_OUT DO 3 POINT = 1, NPOINT OUT( POINT, COORD ) = IN( POINT, 1 ) 3 CONTINUE 4 CONTINUE END IF END \end{terminalv} \normalsize f- c+ This function takes any number of input coordinates and returns a single output coordinate which is the maximum value of the input coordinates. Its inverse (actually a ``pseudo-inverse'') sets all the input coordinates to the value of the output coordinate.\footnote{Remember that ``ptr\_in'' identifies the original ``output'' coordinates when applying the inverse transformation and ``ptr\_out'' identifies the original ``input'' coordinates.} c- f+ This routine takes any number of input coordinates and returns a single output coordinate which is the maximum value of the input coordinates. Its inverse (actually a ``pseudo-inverse'') sets all the input coordinates to the value of the output coordinate.\footnote{Remember that IN holds the original ``output'' coordinates when applying the inverse transformation and OUT holds the original ``input'' coordinates.} f- c+ If this function is applied in the forward direction and then in the inverse direction, it does \textbf{not} in general restore the original coordinate values. However, if applied in the inverse direction and then the forward direction, it does. Hence, replacing the sequence of operations with an equivalent UnitMap is possible in the latter case, but not in the former. c- f+ If this routine is applied in the forward direction and then in the inverse direction, it does \textbf{not} in general restore the original coordinate values. However, if applied in the inverse direction and then the forward direction, it does. Hence, replacing the sequence of operations with an equivalent UnitMap is possible in the latter case, but not in the former. f- c+ To distinguish these possibilities, two flag values are provided for use with astIntraReg to indicate what simplification (if any) is possible. For example, to register the above transformation function, we might use: c- f+ To distinguish these possibilities, two flag values are provided for use with AST\_INTRAREG to indicate what simplification (if any) is possible. For example, to register the above transformation routine, we might use: f- c+ \small \begin{terminalv} void MaxTran( AstMapping *, int, int, const double *[], int, int, double *[] ); ... astIntraReg( "MaxTran", AST__ANY, 1, MaxTran, AST__SIMPIF, purpose, author, contact ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} EXTERNAL MAXTRAN ... CALL AST_INTRAREG( 'MaxTran', AST__ANY, 1, MAXTRAN, AST__SIMPIF, : PURPOSE, AUTHOR, CONTACT, STATUS ) \end{terminalv} \normalsize f- c+ Here, the flag value AST\_\_SIMPIF supplied for the fifth argument indicates that simplification is possible if the transformation is applied in the inverse direction followed by the forward direction. To indicate the complementary case, the flag AST\_\_SIMPFI would be used instead. If both simplifications are possible (as with the SqrTran function in \secref{ss:transformationfunctions}), then we would use the bitwise OR of both values. c- f+ Here, the flag value AST\_\_SIMPIF supplied for the fifth argument indicates that simplification is possible if the transformation is applied in the inverse direction followed by the forward direction. To indicate the complementary case, the flag AST\_\_SIMPFI would be used instead. If both simplifications are possible (as with the SQRTRAN function in \secref{ss:transformationfunctions}), then we would use the sum of both values. f- c+ In practice, some judgement is usually necessary when deciding whether to allow simplification. For example, seen in one light our SqrTran function (\secref{ss:transformationfunctions}) does not cancel with its own inverse, because squaring a coordinate value and then taking its square root can change the original value, if this was negative. Therefore, replacing this combination with a UnitMap will change the behaviour of a compound Mapping and should not be allowed. Seen in another light, however, where the coordinates being processed are intrinsically all positive, it is a permissible and probably useful simplification. c- f+ In practice, some judgement is usually necessary when deciding whether to allow simplification. For example, seen in one light our SQRTRAN routine (\secref{ss:transformationfunctions}) does not cancel with its own inverse, because squaring a coordinate value and then taking its square root can change the original value, if this was negative. Therefore, replacing this combination with a UnitMap will change the behaviour of a compound Mapping and should not be allowed. Seen in another light, however, where the coordinates being processed are intrinsically all positive, it is a permissible and probably useful simplification. f- c+ If such distinctions are ever important in practice, it is simple to register the same transformation function twice with different flag values (use a separate name for each) and then use whichever is appropriate when creating an IntraMap. c- f+ If such distinctions are ever important in practice, it is simple to register the same transformation routine twice with different flag values (use a separate name for each) and then use whichever is appropriate when creating an IntraMap. f- \subsection{\label{ss:readingandwritingintramaps}Writing and Reading IntraMaps} c+ It is most important to realise that when you write an IntraMap to a Channel (\secref{ss:writingtoachannel}), the transformation function which it uses is not stored with it. To do so is impossible, because the function has been compiled and loaded into memory ready for execution before AST gets to see it. However, AST does store the name associated with the transformation function and various details about the IntraMap itself. c- f+ It is most important to realise that when you write an IntraMap to a Channel (\secref{ss:writingtoachannel}), the transformation routine which it uses is not stored with it. To do so is impossible, because the routine has been compiled and loaded into memory ready for execution before AST gets to see it. However, AST does store the name associated with the transformation routine and various details about the IntraMap itself. f- c+ This means that any program attempting to read the IntraMap (\secref{ss:readingfromachannel}) cannot make use of it unless it also has independent access to the original transformation function. If it does not have access to this function, an error will occur at the point where the IntraMap is read and the associated error message will direct the user to the author of the transformation function for more information. c- f+ This means that any program attempting to read the IntraMap (\secref{ss:readingfromachannel}) cannot make use of it unless it also has independent access to the original transformation routine. If it does not have access to this routine, an error will occur at the point where the IntraMap is read and the associated error message will direct the user to the author of the transformation routine for more information. f- c+ However, if the necessary transformation function is available, and has been registered before the read operation takes place, then AST is able to re-create the original IntraMap and will do so. Registration of the transformation function must, of course, use the same name (and, in fact, be identical in most particulars) as was used in the original program which wrote the data. c- f+ However, if the necessary transformation routine is available, and has been registered before the read operation takes place, then AST is able to re-create the original IntraMap and will do so. Registration of the transformation routine must, of course, use the same name (and, in fact, be identical in most particulars) as was used in the original program which wrote the data. f- c+ This means that a set of co-operating programs which all have access to the same set of transformation functions and register them in identical fashion (see \secref{ss:intramaplibrary} for how this can best be achieved) can freely exchange data that contain IntraMaps. The need to avoid exporting such data to unsuspecting third parties (\secref{ss:intramaplimitations}) must, however, be re-iterated. c- f+ This means that a set of co-operating programs which all have access to the same set of transformation routines and register them in identical fashion (see \secref{ss:intramaplibrary} for how this can best be achieved) can freely exchange data that contain IntraMaps. The need to avoid exporting such data to unsuspecting third parties (\secref{ss:intramaplimitations}) must, however, be re-iterated. f- c+ \subsection{\label{ss:intramaplibrary}Managing Transformation Functions in Libraries} c- f+ \subsection{\label{ss:intramaplibrary}Managing Transformation Routines in Libraries} f- c+ If you are developing a large suite of data reduction software, you may have a need to use IntraMaps at various points within it. Very probably this will occur in unrelated modules which are compiled separately and then stored in a library. Since the transformation functions required must be registered before they can be used, this makes it difficult to decide where to perform this registration, especially since any particular data reduction program may use an arbitrary subset of the modules in your library. c- f+ If you are developing a large suite of data reduction software, you may have a need to use IntraMaps at various points within it. Very probably this will occur in unrelated modules which are compiled separately and then stored in a library. Since the transformation routines required must be registered before they can be used, this makes it difficult to decide where to perform this registration, especially since any particular data reduction program may use an arbitrary subset of the modules in your library. f- c+ To assist with this problem, AST allows you to perform the same registration of a transformation function any number of times, so long as it is performed using an identical invocation of astIntraReg on each occasion (\emph{i.e.}\ all of its arguments must be identical). This means you do not have to keep track of whether a particular function has already been registered but could, in fact, register it on each occasion immediately before it is required (wherever that may be). In order that all registrations are identical, however, it is recommended that you group them all together into a single function, perhaps as follows: c- f+ To assist with this problem, AST allows you to perform the same registration of a transformation routine any number of times, so long as it is performed using an identical invocation of AST\_INTRAREG on each occasion (\emph{i.e.}\ all of its arguments must be identical). This means you do not have to keep track of whether a particular routine has already been registered but could, in fact, register it on each occasion immediately before it is required (wherever that may be). In order that all registrations are identical, however, it is recommended that you group them all together into a single routine, perhaps as follows: f- c+ \small \begin{terminalv} void MyTrans( void ) { ... astIntraReg( "MaxTran", AST__ANY, 1, MaxTran, AST__SIMPIF, purpose, author, contact ); ... astIntraReg( "Poly3Tran", 1, 1, Poly3Tran, AST__NOINV, purpose, author, contact ); ... astIntraReg( "SqrTran", 2, 2, SqrTran, 0, purpose, author, contact ); } \end{terminalv} \normalsize c- f+ \small \begin{terminalv} SUBROUTINE MYTRANS( STATUS ) INTEGER STATUS INCLUDE 'AST_PAR' EXTERNAL MAXTRAN, POLY3TRAN, SQRTRAN ... CALL AST_INTRAREG( 'MaxTran', AST__ANY, 1, MAXTRAN, AST__SIMPIF, : PURPOSE, AUTHOR, CONTACT, STATUS ) ... CALL AST_INTRAREG( 'Poly3Tran', 1, 1, POLY3TRAN, AST__NOINV, : PURPOSE, AUTHOR, CONTACT, STATUS ) ... CALL AST_INTRAREG( 'SqrTran, 2, 2, SQRTRAN, 0, : PURPOSE, AUTHOR, CONTACT, STATUS ) END \end{terminalv} \normalsize f- c+ You can then simply invoke this function wherever necessary. It is, in fact, particularly important to register all relevant transformation functions in this way before you attempt to read an Object that might be (or contain) an IntraMap (\secref{ss:readingandwritingintramaps}). This is because you may not know in advance which of these transformation functions the IntraMap will use, so they must all be available in order to avoid an error. c- f+ You can then simply invoke this routine wherever necessary. It is, in fact, particularly important to register all relevant transformation routines in this way before you attempt to read an Object that might be (or contain) an IntraMap (\secref{ss:readingandwritingintramaps}). This is because you may not know in advance which of these transformation routines the IntraMap will use, so they must all be available in order to avoid an error. f- \cleardoublepage \section{\label{ss:plots}Producing Graphical Output (Plots)} Graphical output from AST is performed though an Object called a Plot, which is a specialised form of FrameSet. A Plot does not represent the graphical content itself, but is a route through which plotting operations, such as drawing lines and curves, are conveyed on to a plotting surface to appear as visible graphics. \subsection{The Plot Model} When a Plot is created, it is initialised by providing a FrameSet whose base Frame (as specified by its Base attribute) is mapped linearly or logarithmically (as specified by the LogPlot attribues) on to a \emph{plotting area}. This is a rectangular region in the graphical coordinate space of the underlying graphics system and becomes the new base Frame of the Plot. In effect, the Plot becomes attached to the plotting surface, in rather the same way that a basic FrameSet might be attached to (say) an image. The current Frame of the Plot (derived from the current Frame of the FrameSet supplied) is used to represent a \emph{physical coordinate system}. This is the system in which plotting operations are performed by your program. Every plotting operation is then transformed through the Mapping which inter-relates the Plot's current and base Frames in order to appear on the plotting surface. An example may help here. Suppose we start with a FrameSet whose base Frame describes the pixel coordinates of an image and whose current Frame describes a celestial (equatorial) coordinate system. Let us assume that these two Frames are inter-related by a Mapping within the FrameSet which represents a particular sky projection. When a Plot is created from this FrameSet, we specify how the pixel coordinates (the base Frame) maps on to the plotting surface. This simply corresponds to telling the Plot where we have previously plotted the image data. If we now use the Plot to plot a line with latitude zero in our physical coordinate system, as given by the current Frame, this line would appear as a curve (the equator) on the plotting surface, correctly registered with the image. There are a number of plotting functions provided, which all work in a similar way. Plotting operations are transformed through the Mapping which the Plot represents before they appear on the plotting surface.\footnote{Like any FrameSet, a Plot can be used as a Mapping. In this case it is the inverse transformation which is used when plotting (\emph{i.e.}\ that which transforms between the current and base Frames).} It is possible to draw symbols, lines, axes, entire grids and more in this way. %\subsection{TBW---Creating a Plot} \subsection{Plotting Symbols} c+ The simplest form of plotting is to draw symbols (termed \emph{markers}) at a set of points. This is performed by astMark, which is supplied with a set of physical coordinates at which to place the markers: c- f+ The simplest form of plotting is to draw symbols (termed \emph{markers}) at a set of points. This is performed by AST\_MARK, which is supplied with a set of physical coordinates at which to place the markers: f- c+ \small \begin{terminalv} #include "ast.h" #define NCOORD 2 #define NMARK 10 double in[ NCOORD ][ NMARK ]; int type; ... astMark( plot, NMARK, NCOORD, NMARK, in, type ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INCLUDE 'AST_PAR' INTEGER NCOORD, NMARK, TYPE, STATUS DOUBLE PRECISION IN( NMARK, NCOORD ) STATUS = 0 ... CALL AST_MARK( PLOT, NMARK, NCOORD, NMARK, IN, TYPE, STATUS ) \end{terminalv} \normalsize f- c+ Here, NMARK specifies how many markers to plot and NCOORD specifies how many coordinates are being supplied for each point.\footnote{Remember, the physical coordinate space need not necessarily be 2-dimensional, even if the plotting surface is.} The array ``in'' supplies the coordinates and the integer ``type'' specifies which type of marker to plot. c- f+ Here, NMARK specifies how many markers to plot and NCOORD specifies how many coordinates are being supplied for each point.\footnote{Remember, the physical coordinate space need not necessarily be 2-dimensional, even if the plotting surface is.} The array IN supplies the coordinates and the integer TYPE specifies which type of marker to plot. f- \subsection{\label{ss:plottinggeodesics}Plotting Geodesic Curves} There is no Plot routine to draw a straight line, because any straight line in physical coordinates can potentially turn into a curve in graphical coordinates. We therefore start by considering how to draw geodesic curves. These are curves which trace the path of shortest distance between two points in physical coordinates and are the basic drawing element in a Plot. c+ In many instances, the geodesic will, in fact, be a straight line, but this depends on the Plot's current Frame. If this represents a celestial coordinate system, for instance, it will be a great circle (corresponding with the behaviour of the astDistance function which defines the metric of the physical coordinate space). The geodesic will, of course, be transformed into graphics coordinates before being plotted. A geodesic curve is plotted using astCurve as follows: c- f+ In many instances, the geodesic will, in fact, be a straight line, but this depends on the Plot's current Frame. If this represents a celestial coordinate system, for instance, it will be a great circle (corresponding with the behaviour of the AST\_DISTANCE function which defines the metric of the physical coordinate space). The geodesic will, of course, be transformed into graphics coordinates before being plotted. A geodesic curve is plotted using AST\_CURVE as follows: f- c+ \small \begin{terminalv} double start[ NCOORD ], finish[ NCOORD ]; ... astCurve( plot, start, finish ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} DOUBLE PRECISION START( NCOORD ), FINISH( NCOORD ) ... CALL AST_CURVE( PLOT, START, FINISH, STATUS ) \end{terminalv} \normalsize f- c+ Here, ``start'' and ``finish'' are arrays containing the starting and finishing coordinates of the curve. The astOffset and astDistance functions can often be useful for computing these (\secref{ss:distanceandoffset}). c- f+ Here, START and FINISH are arrays containing the starting and finishing coordinates of the curve. The AST\_OFFSET and AST\_DISTANCE routines can often be useful for computing these (\secref{ss:distanceandoffset}). f- c+ If you need to draw a series of curves end-to-end (when drawing a contour line, for example), then a more efficient alternative is to use astPolyCurve. This has the same effect as a sequence of invocations of astCurve, but allows you to supply a whole set of points at one time. astPolyCurve then joins them, in sequence, using geodesic curves: c- f+ If you need to draw a series of curves end-to-end (when drawing a contour line, for example), then a more efficient alternative is to use AST\_POLYCURVE. This has the same effect as a sequence of calls to AST\_CURVE, but allows you to supply a whole set of points at the same time. AST\_POLYLINE then joins them, in sequence, using geodesic curves: f- c+ \small \begin{terminalv} #define NPOINT 100 double coords[ NCOORD ][ NPOINT ]; ... astPolyCurve( plot, NPOINT, NCOORD, NPOINT, coords ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER NPOINT DOUBLE PRECISION COORDS( NPOINT, NCOORD ) ... CALL AST_POLYCURVE( PLOT, NPOINT, NCOORD, NPOINT, COORDS, STATUS ) \end{terminalv} \normalsize f- c+ Here, NPOINT specifies how many points are to be joined and NCOORD specifies how many coordinates are being supplied for each point. The array ``coords'' supplies the coordinates of the points in the Plot's physical coordinate system. c- f+ Here, NPOINT specifies how many points are to be joined and NCOORD specifies how many coordinates are being supplied for each point. The array COORDS supplies the coordinates of the points in the Plot's physical coordinate system. f- \subsection{Plotting Curves Parallel to Axes} c+ As there is no Plot function to draw a ``straight line'', drawing axes and grid lines to represent coordinate systems requires a slightly different approach. The problem is that for some coordinate systems, these grid lines will not be geodesics, so astCurve and astPolyCurve (\secref{ss:plottinggeodesics}) cannot easily be used (you would have to resort to approximating grid lines by many small elements). Lines of constant celestial latitude provide an example of this, with the exception of the equator which is a geodesic. c- f+ As there is no Plot routine to draw a ``straight line'', drawing axes and grid lines to represent coordinate systems requires a slightly different approach. The problem is that for some coordinate systems, these grid lines will not be geodesics, so AST\_CURVE and AST\_POLYCURVE (\secref{ss:plottinggeodesics}) cannot easily be used (you would have to resort to approximating grid lines by many small elements). Lines of constant celestial latitude provide an example of this, with the exception of the equator which is a geodesic. f- c+ The astGridLine function allows these curves to be drawn, as follows: c- f+ The AST\_GRIDLINE routine allows these curves to be drawn, as follows: f- c+ \small \begin{terminalv} int axis; double length; ... astGridLine( plot, axis, start, length ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER AXIS DOUBLE PRECISION LENGTH ... CALL AST_GRIDLINE( PLOT, AXIS, START, LENGTH, STATUS ) \end{terminalv} \normalsize f- c+ Here, ``axis'' specifies which physical coordinate axis we wish to draw parallel to. The ``start'' array contains the coordinates of the start of the curve and ``length'' specifies the distance to draw along the axis in physical coordinate space. c- f+ Here, AXIS specifies which physical coordinate axis we wish to draw parallel to. The START array contains the coordinates of the start of the curve and LENGTH specifies the distance to draw along the axis in physical coordinate space. f- \subsection{\label{ss:plottinggeneralizedcurves}Plotting Generalized Curves} We have seen how geodesic curves and grid lines can be drawn. The Plot class includes another method, c+ astGenCurve, c- f+ AST\_GENCURVE, f- which allows curves of \emph{any} form to be drawn. The caller supplies a Mapping which maps offset along the curve\footnote{normalized so that the start of the curve is at offset 0.0 and the end of the curve is at offset 1.0 - offset need not be linearly related to distance.} into the corresponding position in the current Frame of the Plot. c+ astGenCurve, c- f+ AST\_GENCURVE, f- then takes care of Mapping these positions into graphics coordinates. The choice of exactly which positions along the curve are to be used to define the curve is also made by c+ astGenCurve, c- f+ AST\_GENCURVE, f- using an adaptive algorithm which concentrates points around areas where the curve is bending sharply or is discontinuous in graphics coordinates. The IntraMap class may be of particular use in this context since it allows you to code your own Mappings to do any transformation you choose. \subsection{\label{ss:clipping}Clipping} Like many graphics systems, a Plot allows you to \emph{clip} the graphics you produce. This means that plotting is restricted to certain regions of the plotting surface so that anything drawn outside these regions will not appear. All Plots automatically clip at the edges of the plotting area specified when the Plot is created. This means that graphics are ultimately restricted to the rectangular region of plotting space to which you have attached the Plot. In addition to this, you may also specify lower and upper limits on each axis at which clipping should occur. This permits you to further restrict the plotting region. Moreover, you may attach these clipping limits to \emph{any} of the Frames in the Plot. This allows you to place restrictions on where plotting will take place in either the physical coordinate system, the graphical coordinate system, or in any other coordinate system which is described by a Frame within the Plot. For example, you could plot using equatorial coordinates and set up clipping limits in galactic coordinates. In general, you could set up arbitrary clipping regions by adding a new Frame to a Plot (in which clipping will be performed) and inter-relating this to the other Frames in a suitable way. c+ Clipping limits are defined using the astClip function, as follows: c- f+ Clipping limits are defined using the AST\_CLIP routine, as follows: f- c+ \small \begin{terminalv} #define NAXES 2 int iframe; double lbnd[ NAXES ], ubnd[ NAXES ]; ... astClip( plot, iframe, lbnd, ubnd); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} INTEGER IFRAME, NAXES DOUBLE PRECISION LBND( NAXES ), UBND( NAXES ) ... CALL AST_CLIP( PLOT, IFRAME, LBND, UBND, STATUS ) \end{terminalv} \normalsize f- c+ Here, the ``iframe'' value gives the index of the Frame within the Plot to which clipping is to be applied, while ``lbnd'' and ``ubnd'' give the limits on each axis of the selected Frame (NAXES is the number of axes in this Frame). c- f+ Here, the IFRAME value gives the index of the Frame within the Plot to which clipping is to be applied, while LBND and UBND give the limits on each axis of the selected Frame (NAXES is the number of axes in this Frame). f- c+ You can remove clipping by giving a value of AST\_\_NOFRAME for ``iframe''. c- f+ You can remove clipping by giving a value of AST\_\_NOFRAME for IFRAME. f- \subsection{Using a Plot as a Mapping} All Plots are also Mappings (just like the FrameSets from which they are derived), so can be used to transform coordinates. Like FrameSets, the forward transformation of a Plot will convert coordinates between the base and current Frames (\emph{i.e.}\ between graphical and physical coordinates). This would be useful if you were (say) reading a cursor position in graphical coordinates and needed to convert this into physical coordinates for display. c+ Conversely, a Plot's inverse transformation converts between its current and base Frames (\emph{i.e.}\ from physical coordinates to graphical coordinates). This transformation is applied automatically whenever plotting operations are carried out by AST functions. It may also be useful to apply it directly, however, if you wish to perform additional plotting operations (\emph{e.g.}\ those provided by the native graphics system) at positions specified in physical coordinates. c- f+ Conversely, a Plot's inverse transformation converts between its current and base Frames (\emph{i.e.}\ from physical coordinates to graphical coordinates). This transformation is applied automatically whenever plotting operations are carried out by AST routines. It may also be useful to apply it directly, however, if you wish to perform additional plotting operations (\emph{e.g.}\ those provided by the native graphics system) at positions specified in physical coordinates. f- c+ There is, however, one important difference between using a FrameSet and a Plot to transform coordinates, and this is that clipping may be applied by a Plot (if it has been enabled using astClip---\secref{ss:clipping}). Any point which lies within the clipped region of a Plot will, when transformed, yield coordinates with the value AST\_\_BAD. If you wish to avoid this clipping, you should extract the relevant Mapping from the Plot (using astGetMapping) and use this, instead of the Plot, to transform the coordinates. c- f+ There is, however. one important difference between using a FrameSet and a Plot to transform coordinates, and this is that clipping may be applied by a Plot (if it has been enabled using AST\_CLIP---\secref{ss:clipping}). Any point which lies within the clipped region of a Plot will, when transformed, yield coordinates with the value AST\_\_BAD. If you wish to avoid this clipping, you should extract the relevant Mapping from the Plot (using AST\_GETMAPPING) and use this, instead of the Plot, to transform the coordinates. f- \subsection{Using a Plot as a Frame} Every Plot is also a Frame, so can be used to obtain the values of Frame attributes such as a Title, axis Labels, axis Units, \emph{etc.}, which are typically used when displaying data and/or coordinates. These attributes are, as for any FrameSet, derived from the current Frame of the Plot (\secref{ss:framesetasframe}). They are also used automatically when using the Plot to plot coordinate axes and coordinate grids (\emph{e.g.}\ for labelling them---\secref{ss:plottingagrid}). c+ Because the current Frame of a Plot represents physical coordinates, any Frame operation applied to the Plot will effectively be working in this coordinate system. For example, the astDistance and astOffset functions will compute distances and offsets in physical coordinate space, while astFormat and astNorm will format physical coordinates in an appropriate way for display. c- f+ Because the current Frame of a Plot represents physical coordinates, any Frame operation applied to the Plot will effectively be working in this coordinate system. For example, the AST\_DISTANCE and AST\_OFFSET routines will compute distances and offsets in physical coordinate space, and AST\_FORMAT will format physical coordinates in an appropriate way for display. f- \subsection{\label{ss:validphysicalcoordinates}Regions of Valid Physical Coordinates} When points in physical coordinate space are transformed by a Plot into graphics coordinates for plotting, they may not always yield valid coordinates, irrespective of any clipping being applied (\secref{ss:clipping}). To indicate this, the resulting coordinate values will be set to the value AST\_\_BAD (\secref{ss:badcoordinates}). There are a number of reasons why this may occur, but typically it will be because physical coordinates only map on to a subset of the graphics coordinate space. This situation is commonly encountered with all-sky projections where, typically, the celestial sphere appears, when plotted, as a distorted shape (\emph{e.g.}\ an ellipse) which does not entirely fill the graphics space. In some cases, there may even be multiple regions of valid and invalid physical coordinates. When plotting is performed \emph{via} a Plot, graphical output will only appear in the regions of valid physical coordinates. Nothing will appear where invalid coordinates occur. Such output is effectively clipped. If you wish to plot in these areas, you must change coordinate system and use, say, graphical coordinates to address the plotting surface directly. \subsection{Plotting Borders} c+ The astBorder function is provided to draw a (line) border around your graphical output. With most graphics systems, this would simply be a rectangular box around the plotting area. With a Plot, however, this boundary follows the edge of each region containing valid, unclipped physical coordinates (\secref{ss:validphysicalcoordinates}). c- f+ The AST\_BORDER routine is provided to draw a (line) border around your graphical output. With most graphics systems, this would simply be a rectangular box around the plotting area. With a Plot, however, this boundary follows the edge of each region containing valid, unclipped physical coordinates (\secref{ss:validphysicalcoordinates}). f- c+ This means, for example, that if you were plotting an all-sky projection, this boundary would outline the perimeter of the celestial sphere when projected on to your plotting surface. Of course, if there is no clipping and all physical coordinates are valid, then you will get the traditional rectangular box. astBorder requires only a pointer to the Plot: c- f+ This means, for example, that if you were plotting an all-sky projection, this boundary would outline the perimeter of the celestial sphere when projected on to your plotting surface. Of course, if there is no clipping and all physical coordinates are valid, then you will get the traditional rectangular box. AST\_BORDER requires only a pointer to the Plot and the usual STATUS argument: f- c+ \small \begin{terminalv} int holes; ... holes = astBorder( plot ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} LOGICAL HOLES ... HOLES = AST_BORDER( PLOT, STATUS ) \end{terminalv} \normalsize f- c+ It returns a boolean (integer) value to indicate if any invalid or clipped physical coordinates were found within the plotting area. If they were, it will draw around the valid unclipped regions and return a value of one. Otherwise, it will draw a simple rectangular border and return zero. c- f+ It returns a logical value to indicate if any invalid or clipped physical coordinates were found within the plotting area. If they were, it will draw around the valid unclipped regions and return .TRUE.. Otherwise, it will draw a simple rectangular border and return .FALSE.. f- \subsection{Plotting Text} c+ Using a Plot to draw text involves supplying a string of text to be displayed and a position in physical coordinates where the text is to appear. The position is transformed into graphical coordinates to determine where the text should appear on the plotting surface. You must also provide a 2-element ``up'' vector which gives the upward direction of the text in graphical coordinates. This allows text to be drawn at any angle. c- f+ Using a Plot to draw text involves supplying a string of text to be displayed and a position in physical coordinates where the text is to appear. The position is transformed into graphical coordinates to determine where the text should appear on the plotting surface. You must also provide a 2-element UP vector which gives the upward direction of the text in graphical coordinates. This allows text to be drawn at any angle. f- c+ Plotting is performed by astText, for example: c- f+ Plotting is performed by AST\_TEXT, for example: f- c+ \small \begin{terminalv} char text[ 21 ]; double pos[ NCOORD ]; float up[ 2 ] = { 0.0f, 1.0f }; ... astText( plot, text, pos, up, "TL" ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CHARACTER * ( 20 ) TEXT DOUBLE PRECISION POS( NCOORD ) REAL UP( 2 ) DATA UP / 0.0, 1.0 / ... CALL AST_TEXT( PLOT, TEXT, POS, UP, 'TL', STATUS ) \end{terminalv} \normalsize f- c+ Here, ``text'' contains the string to be drawn, ``pos'' is an array of physical coordinates and ``up'' specifies the upward vector. In this case, the text will be drawn horizontally. The final argument specifies the text justification, here indicating that the top left corner of the text should appear at the position given. c- f+ Here, TEXT contains the string to be drawn, POS is an array of physical coordinates and UP specifies the upward vector. In this case, the text will be drawn horizontally. The penultimate argument specifies the text justification, here indicating that the top left corner of the text should appear at the position given. f- Further control over the appearance of the text is possible by setting values for various Plot attributes, for example Colour, Font and Size. Sub-strings within the displayed text can be given different appearances, or turned into super-scripts or sub-scripts, by the inclusion of escape sequences (see section~\secref{ss:escapes}) within the supplied text string. \subsection{\label{ss:plottingagrid}Plotting a Grid} c+ The most comprehensive plotting function available is astGrid, which can be used to draw labelled coordinate axes and, optionally, to overlay coordinate grids on the plotting area (Figure~\ref{fig:gridplot}). The routine is straightforward to use, simply requiring a pointer to the Plot: c- f+ The most comprehensive plotting routine available is AST\_GRID, which can be used to draw labelled coordinate axes and, optionally, to overlay coordinate grids on the plotting area (Figure~\ref{fig:gridplot}). The routine is straightforward to use, simply requiring a pointer to the Plot and a STATUS argument: f- c+ \small \begin{terminalv} astGrid( plot ); \end{terminalv} \normalsize c- f+ \small \begin{terminalv} CALL AST_GRID( PLOT, STATUS ) \end{terminalv} \normalsize f- It will draw both linear and curvilinear axes and grids, as required by the particular Plot. The appearance of the output can be modified in a wide variety of ways by setting various Plot attributes. The Label attributes of the current Frame are displayed as the axis labels in the grid, and the Title attribute as the plot title. Sub-strings within these strings can be given different appearances, or turned into super-scripts or sub-scripts, by the inclusion of escape sequences (see section~\secref{ss:escapes}) within the Label attributes. \subsection{\label{ss:escapes}Controlling the Appearance of Sub-strings} Normally, each string of characters displayed using a Plot will be plotted so that all characters in the string have the same font size, colour, \emph{etc.}, specified by the appropriate attributes of the Plot. However, it is possible to include \emph{escape sequences} within the text to modify the appearance of sub-strings. Escape sequences can be used to change, colour, font, size, width, to introduce extra horizontal space between characters, and to change the base line of characters (thus allowing super-scripts and sub-scripts to be created). See the entry for the Escape attribute in \appref{ss:attributedescriptions} for details. As an example, if the character string ``\verb+10\%^50+\%s70+0.5+'' is plotted, it will be displayed as ``$10^{0.5}$'' - that is, with a super-scripted exponent. The exponent text will be 70\% of the size of normal text (as determined by the Size attribute), and its baseline will be raised by 50\% of the height of a normal character. Such escape sequences can be used in the strings assigned to textual attributes of the Plot (such as the axis Labels), and may also be included in strings plotted using c+ astText. c- f+ AST\_TEXT. f- The Format attribute for the SkyAxis class includes the ``g'' option which will cause escape sequences to be included when formatting celestial positions so that super-script characters are used as delimiters for the various fields (a super-script ``h'' for hours, ``m'' for minutes, \emph{etc}). Note, the facility for interpreting escape sequences is only available if the graphics wrapper functions which provide the interface to the underlying graphics system support all the functions included in the \verb+grf.h+ file as of AST V3.2. Older grf interfaces may need to be extended by the addition of new functions before escape sequences can be interpretted. \subsection{\label{ss:logaxes}Producing Logarithmic Axes} In certain situations you may wish for one or both of the plotted axes to be displayed logarithmically rather than linearly. For instance, you may wish to do this when using a Plot to represent a spectrum of, say, flux against frequency. In this case, you can cause the frequency axis to be drawn logarithmically simply by setting the boolean LogPlot attribute for the frequency axis to a non-zero value. This causes several things to happen: \begin{enumerate} \item The Mapping between the base Frame of the Plot (which represents the underlying graphics world coordinate system) and the base Frame of the FrameSet supplied when the Plot was created, is modified. By default, this mapping is linear on both axes, but setting LogPlot non-zero for an axis causes the Mapping to be modified so that it is logarithmic on the specified axis. This is only possible if the displayed section of the axis does not include the value zero (otherwise the attempt to set a new value for LogPlot is ignored,and it retains its default value of zero). \item The major tick marks drawn as part of the annotated coordinate grid are spaced logarithmically rather than linearly. That is, major axis values are chosen so that there is a constant ratio between adjacent tick mark values. This ratio is constrained to be a power of ten. The minor tick marks are drawn at linearly distributed points between the adjoining major tick values. Thus if a pair of adjacent major tick values are drawn at axis values 10.0 and 100.0, minor ticks will be placed at 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0 and 90.0 (note only 8 minor tick marks are drawn). \item If possible, numerical axis labels are shown as powers of ten. This depends on the facilities implemented by the graphics wrapper functions (see the next section). Extra functions were introduced to this set of wrapper functions at AST V3.2 which enable super-scripts and sub-scripts to be produced. Some older wrappers may not yet have implemented these functiosn and this will result in axis labels being drawn in usual scientific or decimal notation. \end{enumerate} Whilst the LogPlot attribute can be used to control all three of the above facilities, it is possible to control them individually as well. The LogTicks and LogLabel attributes control the behaviour specified in items 2 and 3 above, but the default values for these attributes depend on the setting of the LogPlot attribute. This means that setting LogPlot non-zero will swicth all three facilites on, so long as zero values have not been assigned explicitly to LogTicks or LogLabel. \subsection{\label{ss:choosingagraphicspackage}Choosing a Graphics Package} The Plot class itself does not include any code for actually drawing on a graphics device. Instead, it requires a set of functions to be provided which it uses to draw the required graphics. These include functions to draw a straight line, draw a text string, \emph{etc}. You may choose to provide functions from your favorite graphics package, or you can even write your own! To accomodate variations in the calling interfaces of different graphics packages, AST defines a standard interface for these routines. If this interface differs from the interface provided by your graphics package (which in general it will), then you must write a set of \emph{wrapper functions}, which provide the interface expected by AST but which then call functions from your graphics package to provide the required functionality. AST comes with wrapper functions suitable for the PGPLOT graphics package (see \xref{SUN/15}{sun15}{}). There are two ways of indicating which wrapper functions are to be used by the Plot class: \begin{enumerate} \item A file containing C functions with pre-defined names can be written and linked with the application using options of the ast\_link command. (see \secref{ss:howtobuild} and \appref{ss:commanddescriptions}). AST is distributed with such a file (called \texttt{grf\_pgplot.c}) which calls PGPLOT functions to implement the required functionality. This file can be used as a template for writing your own. f+ Currently, it is not possible to write such ``grf modules'' in Fortran. If you want to use wrapper functions written in Fortran, then you must use the AST\_GRFSET method as described below. f- \item The c+ astGrfSet c- f+ AST\_GRFSET f- method of the Plot class can be used to ``register'' wrapper functions at run-time. This allows an application to switch between graphics systems if required. Graphics functions registered in this way do not need to have the pre-defined names used in the link-time method described above. \end{enumerate} For details of the interfaces of the wrapper routines, see c+ either the \texttt{grf\_pgplot.c} file included in the AST source distribution, or the reference documentation for the astGrfSet method. c- f+ the reference documentation for the AST\_GRFSET method. f- \cleardoublepage \section{Compiling and Linking Software that Uses AST} A small number of UNIX commands are provided by AST to assist with the process of building software. A description of these can be found in \appref{ss:commanddescriptions} and their use is discussed here. Note that in order to access these commands, the appropriate directory (normally ``/star/bin'') should be on your PATH.\footnote{If you have not installed AST in the usual location, then substitute the appropriate directory in place of ``/star'' wherever it occurs.} c+ \subsection{\label{ss:accessingheaderfile}Accessing the ``ast.h'' Header File} c- f+ \subsection{\label{ss:accessingheaderfile}Accessing AST Include Files} f- c+ The ``ast.h'' header file defines the external interface to the AST library, including all constants, function prototypes, macros, \emph{etc.}. This file should be located using the usual compiler options for finding C include files, for instance: \small \begin{terminalv} cc prog.c -I/star/include -o prog \end{terminalv} \normalsize This is preferable to specifying the file's absolute name within your software. c- f+ The include files provided for use with Fortran are: \begin{quote} \begin{description} \item[AST\_PAR]\mbox{}\\ Declares the types of all AST functions and defines parameter constants, except those that identify error values. \item[AST\_ERR]\mbox{}\\ Defines parameter constants to represent the various error values to which the AST error status may be set when an error occurs (\secref{ss:errordetection}). \end{description} \end{quote} References to AST include files should be in upper case. Most modern Fortran compilers allow the directory to be specified as a command line option: \small \begin{terminalv} f77 prog.f -I/star/include -o prog \end{terminalv} \normalsize If you are using such a compiler then your Fortran source code should, for instance, include: \small \begin{terminalv} INCLUDE 'AST_PAR' \end{terminalv} \normalsize (that is, there is no need to include the directory within the INCLUDE statement). If your compiler does not provide such an option then your source code must contain an absolute file name identifying the directory where the include files reside, for instance: \small \begin{terminalv} INCLUDE '/star/include/AST_PAR' \end{terminalv} \normalsize f- \subsection{\label{ss:linking}Linking with AST Facilities} c+ C programs which use AST facilities may be linked by including execution of the command ``ast\_link'' on the compiler command line. Thus, to compile and link a program called ``prog'', the following might be used: c- f+ Fortran programs may be linked with AST by including execution of the command ``ast\_link'' on the compiler command line. Thus, to compile and link a program called ``prog'', the following might be used: f- c+ \small \begin{terminalv} cc prog.c -L/star/lib `ast_link` -o prog \end{terminalv} \normalsize c- f+ \small \begin{terminalv} f77 prog.f -L/star/lib `ast_link` -o prog \end{terminalv} \normalsize On Linux systems you should usually use \verb+g77 -fno-second-underscore+ in place of \verb+f77+ - see \xref{``Software development on Linux''}{sun212} {software_development_on_linux} in \xref{SUN/212}{sun212}{}. f- Note the use of backward quote characters, which cause the ``ast\_link'' command to be executed and its result substituted into the compiler command. An alternative is to save the output from ``ast\_link'' in (say) a shell variable and use this instead. You may find this a little faster if you are building software repeatedly during development. Programs which use AST can also be linked in a number of other ways, depending on the facilities they require. In the example above, we have used the default method which assumes that the program will not be generating graphical output, so that no graphics libraries need be linked. If you need other facilities, then various switches can be applied to the ``ast\_link'' command in order to control the linking process. For example, if you were producing graphical output using the PGPLOT graphics package, you could link with the AST/PGPLOT interface by using the ``$-$pgplot'' switch with ``ast\_link'', as follows:\footnote{Use the ``$-$pgp'' option instead if you wish to use the Starlink version of PGPLOT which uses GKS to generate its output.} c+ \small \begin{terminalv} cc prog.c -L/star/lib `ast_link -pgplot` -o prog \end{terminalv} \normalsize c- f+ \begin{small} \begin{terminalv} f77 prog.f -L/star/lib `ast_link -pgplot` -o prog \end{terminalv} \end{small} again using \verb+g77 -fno-second-underscore+ in place of \verb+f77+ on Linux systems. f- See the ``ast\_link'' command description in \appref{ss:commanddescriptions} for details of the options available. \subsection{Building ADAM Applications that Use AST} Users of Starlink's \xref{ADAM}{sg4}{} programming environment \latex{(SG/4)} on UNIX should use the ``\xref{alink}{sun144}{ADAM_link_scripts}'' command (\xref{SUN/144}{sun144}{}) to compile and link applications and can access the AST library by including execution of the command ``ast\_link\_adam'' on the command line, as follows: c+ \begin{small} \begin{terminalv} alink adamprog.c `ast_link_adam` \end{terminalv} \end{small} c- f+ \begin{small} \begin{terminalv} alink adamprog.f `ast_link_adam` \end{terminalv} \end{small} f- Note the use of backward quote characters. By default, AST error messages produced by applications built in this way will be delivered \emph{via} the Starlink EMS Error Message Service (\xref{SSN/4}{ssn4}{}) so that error handling by AST is consistent with the \xref{\emph{inherited status}}{sun104}{inherited_status} error handling normally used in Starlink software. Switches may be given to the ``ast\_link\_adam'' command (in a similar way to ``ast\_link''---\secref{ss:linking}) in order to link with additional AST-related facilities, such as a graphics interface. See the ``ast\_link\_adam'' command description in \appref{ss:commanddescriptions} for details of the options available. \appendix \cleardoublepage \section{\label{ss:classhierarchy}The AST Class Hierarchy} The following table shows the hierarchy of classes in the AST library. For a description of each class, you should consult \appref{ss:classdescriptions}. \small \begin{terminalv} Object - Base class for all AST Objects Axis - Store axis information SkyAxis - Store celestial axis information Channel - Basic (textual) I/O channel FitsChan - I/O Channel using FITS header cards XmlChan - I/O Channel using XML StcsChan - I/O Channel using IVOA STC-S descriptions KeyMap - Store a set of key/value pairs Table - Store a 2-dimensional table of values Mapping - Inter-relate two coordinate systems CmpMap - Compound Mapping DssMap - Map points using Digitised Sky Survey plate solution Frame - Coordinate system description CmpFrame - Compound Frame SpecFluxFrame - Observed value versus spectral position FluxFrame - Observed value at a given fixed spectral position FrameSet - Set of inter-related coordinate systems Plot - Provide facilities for 2D graphical output Plot3D - Provide facilities for 3D graphical output Region - Specify areas within a coordinate system Box - A box region with sides parallel to the axes of a Frame Circle - A circular or spherical region within a Frame CmpRegion - A combination of two regions within a single Frame Ellipse - An elliptical region within a 2-dimensional Frame Interval - Intervals on one or more axes of a Frame. NullRegion - A boundless region within a Frame PointList - A collection of points in a Frame Polygon - A polygonal region within a 2-dimensional Frame Prism - An extrusion of a Region into orthogonal dimensions Stc - Represents an generic instance of an IVOA STC-X description StcResourceProfile - Represents an an IVOA STC-X ResourceProfile StcSearchLocation - Represents an an IVOA STC-X SearchLocation StcCatalogEntryLocation - Represents an an IVOA STC-X CatalogEntryLocation StcObsDataLocation - Represents an an IVOA STC-X ObsDataLocation SkyFrame - Celestial coordinate system description SpecFrame - Spectral coordinate system description DSBSpecFrame - Dual sideband spectral coordinate system description TimeFrame - Time coordinate system description GrismMap - Models the spectral dispersion produced by a grism IntraMap - Map points using a private transformation function LutMap - Transform 1-dimensional coordinates using a lookup table MathMap - Transform coordinates using mathematical expressions MatrixMap - Map positions by multiplying them by a matrix NormMap - Normalise coordinates using a supplied Frame PcdMap - Apply 2-dimensional pincushion/barrel distortion PermMap - Coordinate permutation Mapping PolyMap - General N-dimensional polynomial Mapping ChebyMap - N-dimensional Chebyshev polynomial Mapping RateMap - Calculates an element of a Mapping's Jacobian matrix SelectorMap - Locates positions within a set of Regions ShiftMap - Shifts each axis by a constant amount SlaMap - Sequence of celestial coordinate conversions SpecMap - Sequence of spectral coordinate conversions SphMap - Map 3-d Cartesian to 2-d spherical coordinates SwitchMap - Encapuslates a set of alternate Mappings TimeMap - Sequence of time coordinate conversions TranMap - Combine fwd. and inv. transformations from two Mappings UnitMap - Unit (null) Mapping UnitNormMap - Converts a vector to a unit vector plus length WcsMap - Implement a FITS-WCS sky projection WinMap - Match windows by scaling and shifting each axis ZoomMap - Zoom coordinates about the origin \end{terminalv} \normalsize \cleardoublepage c+ \section{\label{ss:functiondescriptions}AST Function Descriptions} \small \include{c_routines} \normalsize c- f+ \section{\label{ss:functiondescriptions}AST Routine Descriptions} \small \include{f_routines} \normalsize f- \cleardoublepage \section{\label{ss:attributedescriptions}AST Attribute Descriptions} \small c+ \include{c_attribs} c- f+ \include{f_attribs} f- \normalsize \cleardoublepage \section{\label{ss:classdescriptions}AST Class Descriptions} \small c+ \include{c_classes} c- f+ \include{f_classes} f- \normalsize \cleardoublepage \section{\label{ss:commanddescriptions}UNIX Command Descriptions} The commands described here are provided for use from the UNIX shell to assist with developing software which uses AST. To use these commands, you should ensure that the directory ``/star/bin''\footnote{Or the equivalent directory if AST is installed in a non-standard location.} is on your PATH. \small c+ \include{c_commands} c- f+ \include{f_commands} f- \normalsize c+ \cleardoublepage \section{\label{ss:memoryfunctions}AST Memory Management and Utility Functions} AST provides a memory management layer that can be used in place of system functions such as \texttt{malloc}, \texttt{free}, \texttt{realloc}, \emph{etc.} The AST replacements for these functions ( \texttt{astMalloc}, \texttt{astFree} and \texttt{astRealloc}) add extra information to each allocated memory block that allows AST to check the validity of supplied pointers. For example, this extra information allows \texttt{astFree} to detect if the supplied pointer has already been freed, and if so to issue an appropriate error message. The existence of this extra information is invisible to outside callers, and stored in a header block located just before the returned memory block. In addition to the standard functions, AST provides other memory management functions, such as: \begin{description} \item [\texttt{astStore}] - stores data in dynamically allocated memory, allocating the memory (or adjusting the size of previously allocated memory) to match the amount of data to be stored. \item [\texttt{astGrow}] - allocates and expands memory to hold an adjustable-sized array. \item [\texttt{astAppendString}] - allocates and expands memory to hold a concatenated string. \end{description} Theses are just a few of the available utilities functions in the AST memory management layer. Prototypes for all AST memory management functions are included in the header file ``\texttt{ast.h}''. An important restriction on these functions is that pointers created by other memory management functions, such as the system version of \texttt{malloc} \emph{etc.}, should never supplied to an AST memory management function. Only pointers created by AST should be used by these functions. In addition to memory management functions, AST provides various other utility functions, such as a basic regular expression facility, and other string manipulation functions. These are also documented in this appendix. The AST memory management layer is implemented on top of the usual \texttt{malloc}, {tt free} and \texttt{realloc} functions. By default these will be the standard functions provided by . However, the facilities of the STARMEM package (included in the Starlink Software Collection) can be used to specify alternative functions to use. This requires that AST be configured using the ``--with-starmem'' option when it is built. The STARMEM package provides a wrapper for the standard malloc implementation that enables the user to switch malloc schemes at runtime by setting the STARMEM\_MALLOC environment variable. Currently allowed values for this variable are: \begin{description} \item [SYSTEM] - standard system malloc/free - the default \item [DL] - Doug Lea's malloc/free \item [GC] - Hans-Boehm Garbage Collection \end{description} \small \include{memory_routines} \normalsize c- \newpage \section{\xlabel{FitsWcsCoverage}\label{ss:fitswcscoverage}FITS-WCS Coverage} This appendix gives details of the FitsChan class implementation of the conventions described in the FITS-WCS papers available at \url{http://fits.gsfc.nasa.gov/fits_wcs.html}. These conventions are used only if the Encoding attribute of the FitsChan has the value ``FITS-WCS'' (whether set explicitly or defaulted). It should always be possible for a FrameSet to be read (using the c+ astRead c- f+ AST\_READ f- function) from a FitsChan containing a header which conforms to these conventions. However, only those FrameSets which are compatible with the FITS-WCS model can be \emph{written} to a FitsChan using the c+ astWrite c- f+ AST\_WRITE f- function. For instance, if the current Frame of a FrameSet is re-mapped using, say, an arbitrary MathMap then the FrameSet will no longer be compatible with the FITS-WCS model, and so will not be written out successfully to a FitsChan. The following sub-sections describe the details of the implementation of each of the first four FITS-WCS papers. Here, the term ``pixel axes'' is used to refer to the FITS pixel coordinates (i.e. the centre of the first image pixel has a value 1.0 on each pixel axis); the term ``IWC axes'' is used to refer to the axes of the Intermediate World Coordinate system; and the term ``WCS axes'' is used to refer to the axes of the final physical coordinate system described by the CTYPE\emph{i} keywords. \subsection{Paper I - General Linear Coordinates} When reading a FrameSet from a FitsChan, these conventions are used if the CTYPE\emph{i} keyword values within the FitsChan do not conform to the conventions described in later papers, in which case the axes are assumed to be linear. When writing a FrameSet to a FitsChan, these conventions are used for axes which are described by a simple Frame (\emph{i.e.} not a SkyFrame, SpecFrame, \emph{etc.}). Table \ref{tab:fitspaper1} describes the use made by AST of each keyword defined by FITS-WCS paper I. \begin{table}[htbp] \begin{tabular}{|l|p{2.5in}|p{2.5in}|} \hline \multicolumn{1}{|c|}{\textbf{Keyword}} & \multicolumn{1}{c|}{\textbf{Read}} & \multicolumn{1}{c|}{\textbf{Write}} \\ \hline \fitskey{WCSAXES\emph{a}}{Ignored.}{Set to the number of axes in the WCS Frame - only written if different to NAXIS.} \fitskey{CRVAL\emph{ia}}{Used to create the pixel to WCS Mapping.}{Always written (see ``Choice of Reference Point'' below).} \fitskey{CRPIX\emph{ja}}{Used to create the pixel to WCS Mapping.}{Always written (see ``Choice of Reference Point'' below).} \fitskey{CDELT\emph{ia}}{Used to create the pixel to WCS Mapping.}{Only written if the CDMatrix attribute of the FitsChan is set to zero.} \fitskey{CROTA\emph{i}}{Used to create the pixel to WCS Mapping.}{Only written in FITS-AIPS and FITS-AIPS++ encodings.} \fitskey{CTYPE\emph{ia}}{Used to choose the class and attributes of the WCS Frame, and to create the pixel to WCS Mapping (note, ``STOKES'' and ``COMPLEX'' axes are treated as unknown linear axes).}{Always written (see ``Use and Choice of CTYPE keywords'' below).} \fitskey{CUNIT\emph{ia}}{Used to set the Units attributes of the WCS Frame.}{Only written if the Units attribute of the WCS Frame has been set explicitly. If so, the Units value for each axis is used as the CUNIT value.} \fitskey{PC\emph{i\_j}\emph{a}}{Used to create the pixel to WCS Mapping.}{Only written if the CDMatrix attribute of the FitsChan is set to zero.} \fitskey{CD\emph{i\_j}\emph{a}}{Used to create the pixel to WCS Mapping.}{Only written if the CDMatrix attribute of the FitsChan is set to a non-zero value.} \fitskey{PV\emph{i\_ma}}{Ignored for linear axes.}{Not written if the axes are linear.} \fitskey{PS\emph{i\_ma}}{Ignored.}{Not used.} \fitskey{WCSNAME\emph{a}}{Used to set the Domain attribute of the WCS Frame.}{Only written if the Domain attribute of the WCS Frame has been set explicitly. If so, the Domain value is used as the WCSNAME value.} \fitskey{CRDER\emph{ia}}{Ignored.}{Not used.} \fitskey{CSYER\emph{ia}}{Ignored.}{Not used.} \hline \end{tabular} \vspace{3.mm} \caption{Use of FITS-WCS Paper I keywords} \label{tab:fitspaper1} \end{table} \subsubsection{Requirements for a Successful Write Operation} When writing a FrameSet in which the WCS Frame is a simple Frame to a FitsChan, success depends on the Mapping from pixel coordinates (the base Frame in the FrameSet) to the WCS Frame being linear. The write operation will fail if this is not the case. \subsubsection{Use and Choice of CTYPE\emph{i} keywords} When reading a FrameSet from a FitsChan the CTYPE\emph{i} values in the FitsChan are used to set the Symbol attributes of the corresponding WCS Frame. The Label attributes of the WCS Frame are set from the CNAME\emph{i} keywords, if present in the header. Otherwise they are set from the CTYPE\emph{i} comments strings in the header, so long as each axis has a unique non-blank comment. Otherwise, the Label attributes are set to the CTYPE\emph{i} values. The above procedure is over-ridden if the axis types conform to the conventions described in paper II or III, as described below. When writing a FrameSet to a FitsChan, each CTYPE\emph{i} value is set to the value of the Symbol attribute of the corresponding axis in the Frame being written. If a value has been set explicitly for the axis Label attribute, it is used as the axis comment (except that any existing comments in the FitsChan take precedence if the keyword value has not changed). The above procedure is over-ridden if the Frame is a SkyFrame or a SpecFrame, in which case the CTYPE\emph{i} value is derived from the System attribute of the Frame and the nature of the pixel to WCS Mapping according to the conventions of papers II and III, as described below. \subsubsection{Choice of Reference Point} When writing a FrameSet to a FitsChan, the pixel coordinates of the reference point for linear axes (i.e. the CRPIX\emph{j} values) are chosen as follows: \begin{itemize} \item If the FrameSet is being written to a FitsChan which previously contained a set of axis descriptions with the same identifying letter, then the previous CRVAL\emph{j}values are converted into the coordinate system of the Frame being written (if possible). These values are then transformed into the pixel Frame, and the closest integer pixel values are used as the CRPIX keywords. \item If the above step could not be performed for any reason, the central pixel is used as the reference point. This requires the image dimensions to be present in the FitsChan in the form of a set of NAXIS\emph{j} keyword values. \item If both the above two steps failed for any axis, then the pixel reference position is set to a value of 1.0 on the pixel axis. \end{itemize} The pixel to WCS Mapping is then used to find the corresponding CRVAL\emph{j}values. Again, the above procedure is over-ridden if the Frame is a SkyFrame or a SpecFrame, in which case the conventions of papers II and III are used as described below. \subsubsection{Choice of Axis Ordering} When reading a FrameSet from a FitsChan, WCS axis $i$ in the current Frame of the resulting FrameSet corresponds to axis $i$ in the FITS header. When writing a FrameSet to a FitsChan, the axis ordering for the FITS header is chosen to make the CD\emph{i\_j} or PC\emph{i\_j} matrix predominately diagonal. This means that the axis numbering in the FITS header will not necessarily be the same as that in the AST Frame. \subsubsection{Alternate Axis Descriptions} When reading a FrameSet from a FitsChan which contains alternate axis descriptions, each complete set of axis descriptions results in a single Frame being added to the final FrameSet, connected via an appropriate Mapping to the base pixel Frame. The Ident attribute of the Frame is set to hold the single alphabetical character which is used to identify the set of axis descriptions within the FITS header (a single space is used for the primary axis descriptions). When writing a FrameSet to a FitsChan, it is assumed that the base Frame represents pixel coordinates, and the current Frame represents the primary axis descriptions. If there are any other Frames present in the FrameSet, an attempt is made to create a complete set of ``alternate'' set of keywords describing each additional Frame. The first character in the Ident attribute of the Frame is used as the single character descriptor to be appended to the keyword, with the proviso that a given character can only be used once. If a second Frame is found with an Ident attribute which has already been used, its Ident attribute is ignored and the next free character is used instead. Note, failure to write a set of alternate axis descriptions does not result in failure of the entire write operation: the primary axis descriptions are still written, together with any other alternate axis descriptions which can be produced successfully. \subsection{Paper II - Celestial Coordinates} These conventions are used when reading a FrameSet from a FitsChan containing appropriate CTYPE\emph{i} values, and when writing a FrameSet in which the WCS Frame is a SkyFrame. Table \ref{tab:fitspaper2} describes the use made by AST of each keyword whose meaning is defined or extended by FITS-WCS paper II. \begin{table}[htbp] \begin{tabular}{|l|p{2.5in}|p{2.5in}|} \hline \multicolumn{1}{|c|}{\textbf{Keyword}} & \multicolumn{1}{c|}{\textbf{Read}} & \multicolumn{1}{c|}{\textbf{Write}} \\ \hline \fitskey{CTYPE\emph{ia}}{All coordinate systems and projection types listed in paper II are supported (note, ``CUBEFACE'' axes are treated as unknown linear axes). In addition, "-HPX" (HEALPix) and "-XPH" (polar HEALPix) are supported.}{Determined by the System attribute of the SkyFrame and the WcsType attribute of the WcsMap within the FrameSet.} \fitskey{CUNIT\emph{ia}}{Ignored (assumed to be 'degrees').}{Not written.} \fitskey{PV\emph{i\_ma}}{Used to create the pixel to WCS Mapping (values are stored as attributes of a WcsMap within this Mapping).}{Values are obtained from the WcsMap in the pixel to WCS Mapping.} \fitskey{LONPOLE\emph{a}}{Used to create the pixel to WCS Mapping. Also stored as a PVi\_m attribute for the longitude axis of the WcsMap.}{Only written if not equal to the default value defined in paper II (see ``Choice of LONPOLE/LATPOLE'' below).} \fitskey{LATPOLE\emph{a}}{Used to create the pixel to WCS Mapping. Also stored as a PV attribute for the longitude axis of the WcsMap.}{Only written if not equal to the default value defined in paper II (see ``Choice of LONPOLE/LATPOLE'' below).} \fitskey{RADESYS\emph{a}}{Used to set the attributes of the SkyFrame. All values supported except that ecliptic coordinates are currently always assumed to be FK5.}{Always written. Determined by the System attribute of the SkyFrame.} \fitskey{EQUINOX\emph{a}}{Used to set the Equinox attribute of the SkyFrame.}{Written if relevant. Determined by the Equinox attribute of the SkyFrame.} \fitskey{EPOCH}{Used to set the Equinox attribute of the SkyFrame.}{Only written if using FITS-AIPS and FITS-AIPS++ encodings. Determined by the Equinox attribute of the SkyFrame.} \fitskey{MJD-OBS}{Used to set the Epoch attribute of the SkyFrame. DATE-OBS is used if MJD-OBS is not present. A default value based on RADESYS and EQUINOX is used if used if DATE-OBS is not present either.}{Determined by the Epoch attribute of the SkyFrame. Only written if this attribute has been set to an explicit value (in which case DATE-OBS is also written).} \hline \end{tabular} \vspace{3.mm} \caption{Use of FITS-WCS Paper II keywords} \label{tab:fitspaper2} \end{table} \subsubsection{Requirements for a Successful Write Operation} When writing a FrameSet in which the WCS Frame is a SkyFrame to a FitsChan, success depends on the following conditions being met: \begin{enumerate} \item The Mapping from pixel coordinates (the base Frame in the FrameSet) to the WCS SkyFrame includes a WcsMap. \item The Mapping prior to the WcsMap (\emph{i.e.} from pixel to IWC) is linear. \item The Mapping after the WcsMap (\emph{i.e.} from native spherical to celestial coordinates) is a spherical rotation for the celestial axes, and linear for any other axes. \item The TabOK attribute is set to a non-zero positive value in the FitsChan, and the longitude and latitude axes are separable. In this case the Mapping will be described by a pair of 1-dimensional look-up tables, using the ``-TAB'' algorithm described in FITS-WCS paper III. \end{enumerate} If none of the above conditions hold, the write operation will be unsuccessful. \subsubsection{Choice of LONPOLE/LATPOLE} When writing a FrameSet to a FitsChan, the choice of LONPOLE and LATPOLE values is determined as follows: \begin{enumerate} \item If the projection represented by the WcsMap is azimuthal, then any values set for attributes ``PV\emph{i}\_3'' and ``PV\emph{i}\_4'' (where ``\emph{i}'' is the index of the longitude axis) within the WcsMap are used as the LONPOLE and LATPOLE values. Reading a FrameSet from a FITS-WCS header results in the original LONPOLE and LATPOLE values being stored within a WcsMap within the FrameSet. Consequently, if a FrameSet is read from a FITS-WCS header and it is subsequently written out to a new FITS-WCS header, the original LONPOLE and LATPOLE values will usually be used in the new header (the exception being if the WcsMap has been explicitly modified before being written out again). Any extra rotation of the sky is absorbed into the CD\emph{i\_j} or PC\emph{i\_j} matrix (this is possible only if the projection is azimuthal). \item If the projection represented by the WcsMap is azimuthal but no values have been set for the ``PV\emph{i}\_3'' and ``PV\emph{i}\_4'' attributes within the WcsMap, then the default LONPOLE and LATPOLE values are used. This results in no LONPOLE or LATPOLE keywords being stored in the header since default values are never stored. Any extra rotation of the sky is absorbed into the CD\emph{i\_j} or PC\emph{i\_j} matrix (this is possible only if the projection is azimuthal). \item If the projection represented by the WcsMap is not azimuthal, then the values of LONPOLE and LATPOLE are found by transforming the coordinates of the celestial north pole (\emph{i.e} longitude zero, latitude $+\pi/2$) into native spherical coordinates using the inverse of the Mapping which follows the WcsMap. \end{enumerate} \subsubsection{User Defined Fiducial Points} When reading a FrameSet from a FitsChan, projection parameters PV\emph{i}\_0, PV\emph{i}\_1 and PV\emph{i}\_2 (for longitude axis ``\emph{i}'') are used to indicate a user-defined fiducial point as described in section 2.5 of paper II. This results in a shift of IWC origin being applied \emph{before} the WcsMap which converts IWC into native spherical coordinates. The values of these projection parameters, if supplied, are stored as the corresponding PVi\_m attributes of the WcsMap. When writing a FrameSet to a FitsChan, the PV attributes of the WcsMap determine the native coordinates of the fiducial point (the fixed defaults for each projection described in paper II are used if the PV attributes of the WcsMap have not been assigned a value). The corresponding celestial coordinates are used as the CRVAL\emph{i} keywords and the corresponding pixel coordinates as the CRPIX\emph{j} keywords. \subsubsection{Common Non-Standard Features} A collection of common non-standard features are supported when reading a FrameSet from a FitsChan, in addition to those embodied within the available encodings of the FitsChan class. These are translated into the equivalent standard features before being used to create a FrameSet. Note, the reverse operation is never performed: it is not possible to produce non-standard features when writing a FrameSet to a FitsChan (other than those embodied in the available encodings of the FitsChan class). The supported non-standard features include: \begin{itemize} \item EQUINOX keywords with string values equal to a date preceded by the letter B or J (\emph{e.g.} ``B1995.0''). \item EQUINOX or EPOCH keywords with value zero (these are converted to B1950). \item The IRAF ``ZPX'' projection is represented by a WcsMap with type of AST\_\_ZPN. Projection parameter values are read from any WAT\emph{i\_nnn} keywords, and corresponding PVi\_m attributes are set in the WcsMap. The WAT\emph{i\_nnn} keywords may specify corrections to the basic ZPN projection by including ``lngcor'' or ``latcor'' terms. These are supported if they use half cross-terms, in either simple or Chebyshev representation. \item The IRAF ``TNX'' projection is represented by a WcsMap with type of AST\_\_TPN (a distorted TAN projection retained within the WcsMap class from an early draft of the FITS-WCS paper II). Projection parameter values are read from any WAT\emph{i\_nnn} keywords, and corresponding PV attributes are set in the WcsMap. If the TNX projection cannot be converted exactly into an AST\_\_TPN projection, ASTWARN keywords are added to the FitsChan containing a warning message (but only if the Warnings attribute of the FitsChan is set appropriately). Currently, TNX projections that use half cross-terms, in either simple or Chebyshev representation, are supported. \item ``QV'' parameters for TAN projections (as produced by \xref{AUTOASTROM}{sun242}{} \footnote{\url{http://www.astro.gla.ac.uk/users/norman/star/autoastrom/}} are renamed to the equivalent ``PV'' parameters. \item TAN projections that have associated ``PV'' parameters on the latitude axis are converted to the corresponding TPN (distorted TAN) projections. This conversion can be controlled using the PolyTan attribute of the FitsChan class. \end{itemize} \subsection{Paper III - Spectral Coordinates} These conventions are used when reading a FrameSet from a FitsChan which includes appropriate CTYPE\emph{i} values, and when writing a FrameSet in which the WCS Frame is a SpecFrame. Table \ref{tab:fitspaper3} describes the use made by AST of each keyword whose meaning is defined or extended by FITS-WCS paper III. \begin{table}[htbp] \begin{footnotesize} \begin{tabular}{|l|p{2.5in}|p{2.5in}|} \hline \multicolumn{1}{|c|}{\textbf{Keyword}} & \multicolumn{1}{c|}{\textbf{Read}} & \multicolumn{1}{c|}{\textbf{Write}} \\ \hline \fitskey{CTYPE\emph{ia}}{All coordinate systems and projection types listed in paper III are supported algorithm (the ``-LOG'' algorithm may also be applied to non-spectral linear axes; the ``-TAB'' algorithm requires the TabOK attribute to be set in the FitsChan).}{Determined by the System attribute of the SpecFrame and the nature of the pixel to SpecFrame Mapping.} \fitskey{CUNIT\emph{ia}}{Used to set the Units attribute of the SpecFrame (note, SpecFrames always have an ``active'' Units attribute (see astSetActiveUnit).}{Always written.} \fitskey{PV\emph{i\_ma}}{Used to create the pixel to WCS Mapping (values are stored as attributes of a GrismMap).} {Set from the attributes of the GrismMap, if present, and if set explicitly.} \fitskey{SPECSYS\emph{a}}{Used to set the StdOfRest attribute of the SpecFrame (all systems are supported except CMBDIPOL).} {Set from the StdOfRest attribute of the SpecFrame, but only if it has been set explicitly.} \fitskey{SSYSOBS\emph{a}}{Ignored.}{Never written.} \fitskey{OBSGEO-X/Y/Z}{Used to set the ObsLon and ObsLat attributes of the Frame (the observers height above sea level is ignored).}{Set from the ObsLon and ObsLat attributes of the Frame, if they have been set explicitly (it is assumed that the observer is at sea level).} \fitskey{MJD-AVG}{Used to set the Epoch attributes of the SpecFrame.}{Set from the Epoch attribute of the SpecFrame, if it has been set explicitly.} \fitskey{SSYSSRC\emph{a}}{Used to set the SourceVRF attribute of the SpecFrame (all systems are supported except CMBDIPOL).} {Set from the SourceVRF attribute of the SpecFrame.} \fitskey{ZSOURCE\emph{a}}{Used to set the SourceVel attribute of the SpecFrame (the SourceVRF attribute is first set to the system indicated by the SSYSSRC keyword, and the ZSOURCE value is then converted to an apparent radial velocity and stored as the SourceVel attribute).} {Set from the SourceVel attribute of the SpecFrame, if it has been set explicitly (the SourceVel value is first converted from apparent radial velocity to redshift).} \fitskey{VELOSYS\emph{a}}{Ignored.}{Set from the attributes of the SpecFrame that define the standard of rest and the observers position.} \fitskey{RESTFRQ\emph{a}}{Used to set the RestFreq attribute of the SpecFrame.}{Set from the RestFreq attribute of the SpecFrame, but only if the System attribute is not set to ``WAVE'', ``VOPT'', ``ZOPT'' or ``AWAV'', and only if RestFreq has been set explicitly.} \fitskey{RESTWAV\emph{a}}{Used to set the RestFreq attribute of the SpecFrame (after conversion from wavelength to frequency).} {Set from the RestFreq attribute of the SpecFrame (after conversion), but only if the System attribute is set to ``WAVE'', ``VOPT'', ``ZOPT'' or ``AWAV'', and only if RestFreq has been set explicitly.} \fitskey{CNAME\emph{ia}}{Used to set the Label attributes of the WCS Frame keywords.}{Set from the Label attributes of the WCS Frame, if they have been set explicitly.} \hline \end{tabular} \end{footnotesize} \vspace{3.mm} \caption{Use of FITS-WCS Paper III keywords} \label{tab:fitspaper3} \end{table} \subsubsection{Requirements for a Successful Write Operation} When writing a FrameSet in which the WCS Frame is a SpecFrame to a FitsChan, the write operation is successful only if the Mapping from pixel coordinates (the base Frame in the FrameSet) to the SpecFrame satisfies one of the following conditions: \begin{enumerate} \item It is linear. \item It is logarithmic. \item It is linear if the SpecFrame were to be re-mapped into one of the other spectral systems supported by FITS-WCS paper III. \item It contains a GrismMap, and the Mapping before the GrismMap (from pixel coordinates to grism parameter) is linear, and the Mapping after the GrismMap is either null or represents a change of spectral system from wavelength (air or vacuum) to one of the supported spectral systems. \item The TabOK attribute is set to a non-zero positive value in the FitsChan. \end{enumerate} If none of the above conditions hold, the write operation will be unsuccessful. Note, if the FitsChan's TabOK attribute is set to a positive non-zero value then any Mapping that does not meet any of the earlier conditions will be written out as a look-up table, using the ``-TAB'' algorithm described in FITS-WCS paper III. If the TabOK attribute is to zero (the default) or negative in the FitsChan, then the write operation will be unsuccessful unless one of the eaerlier conditions is met.\footnote{If the -TAB algorithm is used, the positive value of the TabOK attribute is used as the table version number (the EXTVER header) in the associated FITS binary table.} \subsubsection{Common Non-Standard Features} The following non-standard features are supported when reading spectral axes from a FitsChan: \begin{itemize} \item Conversion of ``-WAV'', ``-FRQ'' and ``-VEL'' algorithm codes (specified in early drafts of paper III) to the corresponding ``-X2P'' form. \item Conversion of ``RESTFREQ'' to ``RESTFRQ'' \end{itemize} \subsection{Paper IV - Coordinate Distortions} This paper proposes that an additional 4 character code be appended to the end of the CTYPE\emph{i} keyword to specify the nature of any distortion away from the basic algorithm described by the first 8 characters of the CTYPE\emph{i} value. Currently AST ignores all such codes when reading a FrameSet from a FitsChan (except for the ``-SIP'' code defined by the Spitzer Space Telescope project - see below). This means that a FrameSet can still be read from such headers, but the Mapping which gives the WCS position associated with a given pixel position will reflect only the basic algorithm and will not include the effects of the distortion. If such a FrameSet is then written out to a FitsChan, the resulting CTYPE\emph{i} keywords will include no distortion code. \subsubsection{The ``-SIP'' distortion code} The Spitzer Space Telescope project (\url{http://www.spitzer.caltech.edu/}) has developed its own system for encoding 2-dimensional image distortion within a FITS header, based on the proposals of paper IV. A description of this system is available in \url{http://ssc.spitzer.caltech.edu/postbcd/doc/shupeADASS.pdf}. In this system, the presence of distortion is indicated by appending the distortion code ``-SIP'' to the CTYPE\emph{i} keyword values for the celestial axes. The distortion takes the form of a polynomial function which is applied to the pixel coordinates, after subtraction of the CRPIX\emph{j} values. This system is a strictly 2 dimensional system. When reading a FrameSet from a FitsChan which includes the ``-SIP'' distortion code, AST assumes that it is only applied to the first 2 WCS axes in a FITS header (i.e. CTYPE1 and CTYPE2). If the ``-SIP'' distortion code is attached to other axes, it will be ignored. The distortion itself is represented by a PolyMap within the resulting FrameSet. If a FrameSet is read from a FitsChan which includes ``-SIP'' distortion, and an attempt is then made to write this FrameSet out to a FitsChan, the write operation will fail unless the distortion is insignificant (\emph{i.e.} is so small that the tests for linearity built into AST are passed). In this case, no distortion code will be appended to the resulting CTYPE\emph{i} keyword values. \newpage \section{\xlabel{changes_and_new_features}\label{ss:changes}Release Notes} \subsection{Changes Introduced in V1.1} The following describes the most significant changes which occurred in the AST library between versions V1.0 and V1.1 (not the most recent version): \begin{enumerate} \item A new ``How To\ldots'' section (\secref{ss:howto}) has been added to this document. It contains simple recipies for performing commonly-required operations using AST. c+ \item A new astUnformat function has been provided to read formatted coordinate values for the axes of a Frame (\secref{ss:unformattingaxisvalues}). In essence, this function is the inverse of astFormat. It may be used to decode user-supplied formatted values representing coordinates, turning them into numerical values for processing. Celestial coordinates may also be read using this function (\secref{ss:unformattingskyaxisvalues}) and free-format input is supported. c- f+ \item A new AST\_UNFORMAT function has been provided to read formatted coordinate values for the axes of a Frame (\secref{ss:unformattingaxisvalues}). In essence, this function is the inverse of AST\_FORMAT. It may be used to decode user-supplied formatted values representing coordinates, turning them into numerical values for processing. Celestial coordinates may also be read using this function (\secref{ss:unformattingskyaxisvalues}) and free-format input is supported. f- \item The Format attribute string used by a SkyFrame when formatting celestial coordinate values now allows the degrees/hours field to be omitted, so that celestial coordinates may be given in (\emph{e.g.}) arc-minutes and/or arc-seconds (\secref{ss:formattingskyaxisvalues}). As a result, the degrees/hours field is no longer included by default. A new ``t'' format specifier has been introduced (see the Format attribute) to allow minutes and/or seconds of time to be specified if required. c+ \item A new function astMapBox has been introduced. This allows you to find the extent of a ``bounding box'' which just encloses another box after it has been transformed by a Mapping. A typical use might be to calculate the size which an image would have if it were transformed by the Mapping. c- f+ \item A new routine AST\_MAPBOX has been introduced. This allows you to find the extent of a ``bounding box'' which just encloses another box after it has been transformed by a Mapping. A typical use might be to calculate the size which an image would have if it were transformed by the Mapping. f- c+ \item A new class of Object, the IntraMap, has been introduced (\secref{ss:intramaps}). This is a specialised form of Mapping which encapsulates a privately-defined coordinate transformation function (\emph{e.g.}\ written in C) so that it may be used like any other AST Mapping. This allows you to create Mappings that perform any conceivable coordinate transformation. c- f+ \item A new class of Object, the IntraMap, has been introduced (\secref{ss:intramaps}). This is a specialised form of Mapping which encapsulates a privately-defined coordinate transformation routine (\emph{e.g.}\ written in Fortran) so that it may be used like any other AST Mapping. This allows you to create Mappings that perform any conceivable coordinate transformation. f- \item The internal integrity of a FrameSet is now automatically preserved whenever changes are made to any attributes which affect the current Frame (either by setting or clearing their values). This is accomplished by appropriately re-mapping the current Frame to account for any change to the coordinate system which it represents (\secref{ss:framesetintegrity}). \item The internal structure of a FrameSet is now automatically tidied to eliminate redundant nodes whenever any of its Frames is removed or re-mapped. Automatic simplification of any compound Mappings which result may also occur. The effect of this change is to prevent the accumulation of unnecessary structure in FrameSets which are repeatedly modified. c+ \item Some improvements have been made to the algorithms for simplifying compound Mappings, as used by astSimplify. c- f+ \item Some improvements have been made to the algorithms for simplifying compound Mappings, as used by AST\_SIMPLIFY. f- \item The textual representation used for some Objects (\emph{i.e.}\ when they are written to a Channel) has changed slightly, but remains compatible with earlier versions of AST. c+ \item Interfaces to the internal functions and macros used by AST for handling memory and error conditions are now provided \emph{via} the ``ast.h'' header file. This is for the benefit of those writing (\emph{e.g.}) new graphics interfaces for AST. c- c+ \item A problem has been fixed which could result when using astRead to read FITS headers in which the CDELT value is zero. Previously, this could produce a Mapping whose inverse transformation was not defined and this could unnecessarily restrict the use to which it could be put. The problem has been overcome by supplying a suitable small CDELT value for FITS axes which have only a single pixel. c- f+ \item A problem has been fixed which could result when using AST\_READ to read FITS headers in which the CDELT value is zero. Previously, this could produce a Mapping whose inverse transformation was not defined and this could unnecessarily restrict the use to which it could be put. The problem has been overcome by supplying a suitable small CDELT value for FITS axes which have only a single pixel. f- \item A bug has been fixed which could occasionally cause a MatrixMap to be used with the wrong Invert attribute value when it forms part of c+ a compound Mapping which is being simplified using astSimplify. c- f+ a compound Mapping which is being simplified using AST\_SIMPLIFY. f- f+ \item A bug has been fixed which could cause the AST\_\_BAD parameter to have an incorrect value on some platforms. f- \item A problem has been fixed which could prevent tick marks being drawn on a coordinate axis close to a singularity in the coordinate system. \end{enumerate} \subsection{Changes Introduced in V1.2} The following describes the most significant changes which occurred in the AST library between versions V1.1 and V1.2 (not the most recent version): \begin{enumerate} c+ \item A new function, astPolyCurve, has been introduced to allow more efficient plotting of multiple geodesic curves (\secref{ss:plottinggeodesics}). c- f+ \item A new routine, AST\_POLYCURVE, has been introduced to allow more efficient plotting of multiple geodesic curves (\secref{ss:plottinggeodesics}). f- c+ \item A new set of functions, astResample$<$X$>$, has been introduced to perform resampling of gridded data such as images (\emph{i.e.}\ re-gridding) under the control of a geometrical transformation specified by a Mapping. c- f+ \item A new set of functions, AST\_RESAMPLE$<$X$>$, has been introduced to perform resampling of gridded data such as images (\emph{i.e.}\ re-gridding) under the control of a geometrical transformation specified by a Mapping. f- \item The command-line options ``$-$pgp'' and ``$-$pgplot'', which were previously synonymous when used with the ``ast\_link'' and ``ast\_link\_adam'' commands, are no longer synonymous. The option ``$-$pgp'' now causes linking with the Starlink version of PGPLOT (which uses GKS to generate its output), while ``$-$pgplot'' links with the standard (or ``native'') version of PGPLOT. c+ \item The function astMapBox has been changed to execute more quickly, although this has been achieved at the cost of some loss of robustness when used with difficult Mappings. c- f+ \item The routine AST\_MAPBOX has been changed to execute more quickly, although this has been achieved at the cost of some loss of robustness when used with difficult Mappings. f- \item A new value of ``FITS-IRAF'' has been introduced for the Encoding attribute of a FitsChan. This new encoding provides an interim solution to the problem of storing coordinate system information in FITS headers, until the proposed new FITS-WCS standard becomes stable. \item When a FrameSet is created from a set of FITS header cards (by reading from a FitsChan using a ``foreign'' encoding), the base Frame of the resulting FrameSet now has its Domain attribute set to ``GRID''. This reflects the fact that this Frame represents FITS data grid coordinates (equivalent to FITS pixel coordinates---see \secref{ss:domainconventions}). Previously, this Domain value was not set. c+ \item astFindFits now ignores trailing spaces in its keyword template. c- f+ \item AST\_FINDFITS now ignores trailing spaces in its keyword template. f- c+ \item astPutFits now recognises ``D'' and ``d'' as valid exponent characters in floating point numbers. c- f+ \item AST\_PUTFITS now recognises ``D'' and ``d'' as valid exponent characters in floating point numbers. f- \item The FitsChan class is now more tolerant of common minor violations of the FITS standard. \item The FitsChan class now incorporates an improved test for the linearity of Mappings, allowing more reliable conversion of AST data into FITS (using ``foreign'' FITS encodings). c+ \item Some further improvements have been made to the algorithms for simplifying compound Mappings, as used by astSimplify. c- f+ \item Some further improvements have been made to the algorithms for simplifying compound Mappings, as used by AST\_SIMPLIFY. f- \item A new UnitRadius attribute has been added to the SphMap class. This allows improved simplification of compound Mappings (CmpMaps) involving SphMaps and typically improves performance when handling FITS world coordinate information. \item A MatrixMap no longer propagates input coordinate values of AST\_\_BAD automatically to all output coordinates. If certain output coordinates do not depend on the affected input coordinate(s) because the relevant matrix elements are zero, then they may now remain valid. \item A minor bug has been corrected which could cause certain projections which involve half the celestial sphere to produce valid coordinates for the other (unprojected) half of the sphere as well. c+ \item A bug has been fixed which could occasionally cause astConvert to think that conversion between a CmpFrame and another Frame was possible when, in fact, it wasn't. c- f+ \item A bug has been fixed which could occasionally cause AST\_CONVERT to think that conversion between a CmpFrame and another Frame was possible when, in fact, it wasn't. f- \end{enumerate} \subsection{Changes Introduced in V1.3} The following describes the most significant changes which occurred in the AST library between versions V1.2 and V1.3 (not the most recent version): \begin{enumerate} c+ \item A new set of functions, astResample$<$X$>$, has been introduced to provide efficient resampling of gridded data, such as spectra and images, under the control of a geometrical transformation specified by a Mapping. A variety of sub-pixel interpolation schemes are supported. c- f+ \item A new set of functions, AST\_RESAMPLE$<$X$>$, has been introduced to provide efficient resampling of gridded data, such as spectra and images, under the control of a geometrical transformation specified by a Mapping. A variety of sub-pixel interpolation schemes are supported. f- \item A new class, PcdMap, has been introduced. This is a specialised form of Mapping which implements 2-dimensional pincushion or barrel distortion. \item A bug has been fixed which could cause a FitsChan to produce too many digits when formatting floating point values for inclusion in a FITS header if the numerical value was in the range -0.00099999\ldots to -0.0001. \item A bug has been fixed which could cause a FitsChan to lose the comment associated with a string value in a FITS header. \item A FitsChan now reports an error if it reads a FITS header which identifies a non-standard sky projection (previously, this was accepted without error and a Cartesian projection used instead). \item A bug has been fixed which could prevent conversion between the coordinate systems represented by two CmpFrames. This could only occur if the CmpFrames contained a relatively large number of nested Frames. %\item A bug has been fixed which could cause a program to crash if %FrameSets were nested inside each other (for example, if one FrameSet %had another FrameSet added to it for use as a Frame or Mapping). The %problem could only occur if the nested structure was loaded from a data %c+ %file (using astRead). %c- %f+ %file (using AST\_READ). %f- % \item Further improvements have been made to the simplification of compound Mappings, including fixes for several bugs which could cause indefinite looping or unwanted error messages. \item Some memory leaks have been fixed. \item A small number of documentation errors have been corrected. \end{enumerate} \subsection{Changes Introduced in V1.4} The following describes the most significant changes which have occurred in the AST library between versions V1.3 and V1.4 (not the most recent version): \begin{enumerate} c+ \item A new MathMap class has been introduced. This is a form of Mapping that allows you to define coordinate transformations in a flexible and transportable way using arithmetic operations and mathematical functions similar to those available in C. c- f+ \item A new MathMap class has been introduced. This is a form of Mapping that allows you to define coordinate transformations in a flexible and transportable way using arithmetic operations and mathematical functions similar to those available in Fortran. f- c+ \item {\bf{WARNING---INCOMPATIBLE CHANGE.}} Transformation functions used with the IntraMap class (see, for example, astIntraReg) now require a ``this'' pointer as their first parameter. \textbf{Existing implementations will not continue to work correctly with this version of AST unless this parameter is added.} There is no need for existing software to make use of this pointer, but it must be present. c- f+ \item {\bf{WARNING---INCOMPATIBLE CHANGE.}} Transformation routines used with the IntraMap class (see, for example, AST\_INTRAREG) now require a THIS pointer as their first argument. \textbf{Existing implementations will not continue to work correctly with this version of AST unless this argument is added.} There is no need for existing software to make use of this pointer, but it must be present. f- This change has been introduced so that transformation functions can gain access to IntraMap attributes. c+ \item A new IntraFlag attribute has been added to the IntraMap class. This allows the transformation functions used by IntraMaps to adapt to produce the required transformation on a per-IntraMap basis (\secref{ss:intraflag}). c- f+ \item A new IntraFlag attribute has been added to the IntraMap class. This allows the transformation routines used by IntraMaps to adapt to produce the required transformation on a per-IntraMap basis (\secref{ss:intraflag}). f- \item The Plot attributes MajTickLen and MinTickLen, which control the length of major and minor tick marks on coordinate axes, may now be subscripted using an axis number. This allows tick marks of different lengths to be used on each axis. It also allows tick marks to be suppressed on one axis only by setting the length to zero. \item The value of the Plot attribute NumLab, which controls the plotting of numerical labels on coordinate axes, no longer has any effect on whether labelling of a coordinate grid is interior or exterior (as controlled by the Labelling attribute). \item The FitsChan class now provides some support for the IRAF-specific ``ZPX'' sky projection, which is converted transparently into the equivalent FITS ``ZPN'' projection (see the description of the Encoding attribute for details). \item The FitsChan class now recognises the coordinate system ``ICRS'' (International Celestial Reference System) as equivalent to ``FK5''. This is an interim measure and full support for the (exceedingly small) difference between ICRS and FK5 will be added at a future release. Note that ``ICRS'' is not yet recognised as a coordinate system by other classes such as SkyFrame, so this change only facilitates the importation of foreign data. \item A bug in the FitsChan class has been fixed which could result in longitude values being incorrect by 180 degrees when using cylindrical sky projections, such as the FITS ``CAR'' projection. \item A bug in the FitsChan class has been fixed which could result in the FITS sky projection parameters ProjP(0) to ProjP(9) being incorrectly named PROJP1 to PROJP10 when written out as FITS cards. \item A bug in the FitsChan class has been fixed which could cause confusion between the FITS-IRAF and FITS-WCS encoding schemes if both a CD matrix and a PC matrix are erroneously present in a FITS header. \item Some minor memory leaks have been fixed. \item A small number of documentation errors have been corrected. \end{enumerate} \subsection{Changes Introduced in V1.5} The following describes the most significant changes which have occurred in the AST library between versions V1.4 and V1.5 (not the most recent version): \begin{enumerate} \item The FitsChan class has been modified to support the latest draft FITS WCS standard, described in the two papers ``Representation of world coordinates in FITS'' (E.W.\,Greisen and M.\,Calabretta, dated 30th November, 1999), and ``Representation of celestial coordinates in FITS'' (M.\,Calabretta and E.W.\,Greisen, dated 24th September, 1999). These are available at \url{http://www.cv.nrao.edu/fits/documents/wcs/wcs.html}. The FITS-WCS encoding now uses these updated conventions. The main changes are: \begin{itemize} \item Rotation and scaling of pixel axes is now represented by a matrix of \texttt{CDj\_i} keywords instead of a combination of \texttt{PCjjjiii} and \texttt{CDELTj} keywords. \item Projection parameters are now associated with particular axes and are represented by \texttt{PVi\_m} keywords instead of the \texttt{PROJPm} keywords. \item The tangent plane projection (``TAN'') can now include optional polynomial correction terms. \item An entire set of keywords must be supplied for each set of secondary axis descriptions, and each such keyword must finish with a single character indicating which set it belongs to. This means that keywords which previously occupied eight characters have been shorten to seven to leave room for this extra character. Thus \texttt{LONGPOLE} has become \texttt{LONPOLE} and \texttt{RADECSYS} has become \texttt{RADESYS}. \end{itemize} \item Two new encodings have been added to the FitsChan class: \begin{description} \item [FITS-PC] This encoding uses the conventions of the now superseded FITS WCS paper by E.W.\,Greisen and M.\,Calabretta which used keywords \texttt{CDELTj} and \texttt{PCjjjiii} to describe axis scaling and rotation. These are the conventions which were used by the FITS-WCS encoding prior to version 1.5 of AST. This encoding is provided to allow existing data which use these conventions to be read. It should not in general be used to create new data. \item [FITS-AIPS] This encoding is based on the conventions described in the document ``Non-linear Coordinate Systems in AIPS'' by Eric W. Greisen (revised 9th September, 1994 and available by ftp from fits.cv.nrao.edu /fits/documents/wcs/aips27.ps.Z). This encoding uses \texttt{CROTAi} and \texttt{CDELTi} keywords to describe axis rotation and scaling. \end{description} \item The FitsChan class now provides some support for the IRAF-specific ``TNX'' sky projection, which is converted transparently into the equivalent FITS ``TAN'' projection (see the description of the Encoding attribute for details). \item FrameSets originally read from a DSS encoded FITS header can now be written out using the FITS-WCS encoding (a TAN projection with correction terms will be used) in addition to the DSS encoding. The reverse is also possible: FrameSets originally read from a FITS-WCS encoded FITS header and which use a TAN projection can now be written out using the DSS encoding. \item The algorithm used by the FitsChan class to verify that a FrameSet conforms to the FITS-WCS model has been improved so that FrameSets including more complex mixtures of parallel and serial Mappings can be written out using the FITS-WCS encoding. \item The FitsChan class has been changed so that long strings included in the description of an Object can be saved and restored without truncation when using the NATIVE encoding. Previously, very long Frame titles, mathematical expressions, \emph{etc.} were truncated if they exceeded the capacity of a single FITS header card. They are now split over several header cards so that they can be restored without truncation. Note, this facility is only available when using NATIVE encoding. \item The FitsChan class has a new attribute called Warnings which can be used to select potentially dangerous conditions under which warnings should be issued. These conditions include (for instance) unsupported features within non-standard projections, missing keywords for which default values will be used, \emph{etc}. \item The WcsMap class has been changed to support the changes made to the FITS-WCS encoding in the FitsChan class: \begin{itemize} \item Projection parameters are now associated with a particular axis and are specified using a new set of attributes called PVj\_m. Here, ``j'' is the index of an axis of WcsMap, and ``m'' is the index of the projection parameter. \item The old attributes ProjP(0) to ProjP(9) are still available but are now deprecated in favour of the new PVj\_m attributes. They are interpreted as aliases for PV(axlat)\_0 to PV(axlat)\_9, where ``axlat'' is the index of the latitude axis. \item The GLS projection projection has been renamed as SFL, but the AST\_\_GLS type has been retained as an alias for AST\_\_SFL. \end{itemize} \end{enumerate} \subsection{Changes Introduced in V1.6} The following describes the most significant changes which have occurred in the AST library between versions V1.5 and V1.6: \begin{enumerate} c+ \item The C interface to several methods (astTranN, astMark and astPolyCurve) have been changed to make them easier to call from C++. Parameters which previously had type ``double (*)[]'' have been changed to the simpler ``double *''. Using the old types may result in non-fatal compiler warnings, but should not change the behaviour of the methods. c- \item A bug has been fixed in the Plot class which could cause groups of tick marks to be skipped when using very small gaps. \item A bug has been fixed in the Plot class which could cause axes to be labeled outside the visible window, resulting in no axes being visible. \item The FITS-WCS encoding used by the FitsChan class now includes the WCSNAME keyword. When creating a FrameSet from FITS headers, the values of the WCSNAME keywords are now used as the Domain names for the corresponding Frames in the returned FrameSet. When writing a FrameSet to a FITS header the Domain names of each Frame are stored in WCSNAME keywords in the header. \item The FITS-WCS encoding used by the FitsChan class now attempts to retain the identification letter associated with multiple axis descriptions. When reading a FrameSet from a FITS header, the identification letter is stored in the Ident attribute for each Frame. When writing a FrameSet to a FITS header, the identification letter is read from the Ident attribute of each Frame. The letter to associate with each Frame can be changed by assigning a new value to the Frame's Ident attribute. \item The FITS-WCS, FITS-PC, FITS-IRAF and FITS-AIPS encodings used by the FitsChan class now create a SkyFrame with the System attribute set to ``Unknown'' if the CTYPE keywords in the supplied header refers to an unknown celestial coordinate system. Previously, a Frame was used instead of a SkyFrame. \item The FITS-WCS, FITS-PC, FITS-IRAF and FITS-AIPS encodings used by the FitsChan class no longer report an error if the FITS header contains no CTYPE keywords. It is assumed that a missing CTYPE keyword implies that the world coordinate system is linear and identically equal to ``intermediate world coordinates''. \item The new value ``noctype'' is now recognized by the Warnings attribute of the FitsChan class. This value causes warnings to be issued if CTYPE keywords are missing from foreign encodings. \item A new attribute called AllWarnings has been added to the FitsChan class. This is a read-only, space separated list of all the known condition names which can be specified in the Warnings attribute. \item The FitsChan class now attempts to assigns a Title to each Frame in a FrameSet read using a foreign encoding. The Title is based on the Domain name of the Frame. If the Frame has no Domain name, the default Title supplied by the Frame class is retained. \item The FitsChan class uses the comments associated with CTYPE keywords as axis labels when reading a foreign encoding. This behaviour has been modified so that the default labels provided by the Frame class are retained (instead of using the CTYPE comments) if any of the CTYPE comments are identical. \item A new ``interpolation'' scheme identified by the symbolic constant AST\_\_BLOCKAVE has been added to the AST\_RESAMPLE$<$X$>$ set of functions. The new scheme calculates each output pixel value by finding the mean of the input pixels in a box centred on the output pixel. \item The SkyFrame class can now be used to represent an arbitrary spherical coordinate system by setting its System attribute to ``Unknown''. \item The indices of the latitude and longitude axes of a SkyFrame can now be found using new read-only attributes LatAxis and LonAxis. The effects of any axis permutation is taken into account. \item A new attribute called Ident has been added to the Object class. This serves the same purpose as the existing ID attribute, but (unlike ID) its value is transferred to the new Object when a copy is made. \item A bug has been fixed which could prevent complex CmpFrames behaving correctly (for instance, resulting in the failure of attempts to find a Mapping between a CmpFrame and itself). \end{enumerate} \subsection{Changes Introduced in V1.7} The following describes the most significant changes which have occurred in the AST library between versions V1.6 and V1.7: \begin{enumerate} \item The Frame class has a new method called f+ AST\_ANGLE f- c+ astAngle c- which returns the angle subtended by two points at a third point within a 2 or 3 dimensional Frame. \item The Frame class has a new method called f+ AST\_OFFSET2 f- c+ astOffset2 c- which calculates a position which is offset away from a given starting point by a specified distance along a geodesic curve which passes through the starting point at a given position angle. It can only be used with 2-dimensional Frames. \item The Frame class has a new method called f+ AST\_AXDISTANCE f- c+ astAxDistance c- which returns the increment between two supplied axis values. For axes belonging to SkyFrames, the returned value is normalized into the range $\pm\pi$. \item The Frame class has a new method called f+ AST\_AXOFFSET f- c+ astAxOffset c- which returns an axis value a given increment away from a specified axis value. For axes belonging to SkyFrames, the returned value is normalized into the range $\pm\pi$ (for latitude axes) or zero to $2\pi$ (for longitude axes). \item The Plot class has a new method called f+ AST\_GENCURVE f- c+ astGenCurve c- which allows generalised user-defined curves to be drawn. The curve is defined by a user-supplied Mapping which maps distance along the curve into the corresponding position in the current Frame of the Plot. The new method then maps these current Frame position into graphics coordinates, taking care of any non-linearities or discontinuities in the mapping. \item The Plot class has a new method called f+ AST\_GRFSET f- c+ astGrfSet c- which allows the underlying primitive graphics functions to be selected at run-time. Previously, the functions used by the Plot class to produce graphics could only be selected at link-time, using the options of the ast\_link command. The new Plot method allows an application to over-ride the functions established at link-time, by specifying alternative primitive graphics routines. In addition, the two new Plot methods f+ AST\_GRFPUSH and AST\_GRFPOP f- c+ astGrfPush and astGrfPop c- allow the current graphics routines to be saved and restore on a first-in-last-out stack, allowing temporary changes to be made to the set of registered graphics routines. \item The DrawAxes attribute of the Plot class can now be specified independantly for each axis, by appending the axis index to the end of the attribute name. \item A bug has been fixed in the Plot class which could result in axis labels being drawn on inappropriate edges of the plotting box when using ``interior'' labelling. \item A bug has been fixed in the IntraMap class which could cause IntraMaps to be corrupted after transforming any points. \item Bugs have been fixed in the FitsChan class which could cause inappropriate ordering of headers within a FitsChan when writing or reading objects using NATIVE encodings. \item A bug has been fixed in the FitsChan class which could cause the celestial longitude of a pixel to be estimated incorrectly by 180 degrees if the reference point is at either the north or the south pole. \end{enumerate} \subsection{Changes Introduced in V1.8-2} The following describes the most significant changes which have occurred in the AST library between versions V1.7 and V1.8-2: \begin{enumerate} \item The SkyFrame class has a new attribute called NegLon which allows longitude values to be displayed in the range $-\pi$ to $+\pi$, instead of the usual range zero to $2.\pi$. \item Some new c+ functions (astAngle, astAxAngle, astResolve, astOffset2, astAxOffset, astAxDistance) c- f+ routines (AST\_ANGLE, AST\_AXANGLE, AST\_RESOLVE, AST\_OFFSET2, AST\_AXOFFSET, AST\_AXDISTANCE) f- have been added to the Frame class to allow navigation of the coordinate space to be performed without needing to know the underlying geometry of the co-ordinate system (for instance, whether it is Cartesian or spherical). Note, version 1.8-1 contained many of these facilities, but some have been changed in version 1.8-2. Particularly, positions angles are now referred to the second Frame axis for \emph{all} classes of Frames (including SkyFrames), and the c+ astBear function has been replaced by astAxAngle. c- f+ AST\_BEAR routine has been replaced by AST\_AXANGLE. f- \end{enumerate} \subsection{Changes Introduced in V1.8-3} The following describes the most significant changes which occurred in the AST library between versions V1.8-2 and V1.8-3: \begin{enumerate} \item A new method called astDecompose has been added to the Mapping class which enables pointers to be obtained to the component parts of CmpMap and CmpFrame objects. \item Functions within proj.c and wcstrig.c have been renamed to avoid name clashes with functions in more recent versions of Mark Calabretta's wcslib library. \end{enumerate} \subsection{Changes Introduced in V1.8-4} The following describes the most significant changes which occurred in the AST library between versions V1.8-3 and V1.8-4: \begin{enumerate} \item The FitsChan class has a new attribute called DefB1950 which can be used to select the default reference frame and equinox to be used if a FitsChan with foreign encoding contains no indication of the reference frame or equinox. \item A bug has been fixed in the FitsChan class which could prevent astWrite from creating a set of FITS headers from an otherwise valid FrameSet, when when using FITS-AIPS encoding. \item A bug has been fixed in the FitsChan class which could cause astRead to mis-interpret the FITS CROTA keyword when using FITS-AIPS encoding. \end{enumerate} \subsection{Changes Introduced in V1.8-5} The following describes the most significant changes which occurred in the AST library between versions V1.8-4 and V1.8-5: \begin{enumerate} \item The Plot class defines new graphical elements Axis1, Axis2, Grid1, Grid2, NumLabs1, NumLabs2, TextLab1, TextLab2, Ticks1 and Ticks2. These allow graphical attributes (colour, width, etc) to be set for each axis individually. Previously, graphical attributes could only be set for both axes together, using graphical elements Axes, Grid, NumLabs, TextLabs and Ticks. \end{enumerate} \subsection{Changes Introduced in V1.8-7} The following describes the most significant changes which occurred in the AST library between versions V1.8-5 and V1.8-7: \begin{enumerate} \item A new attribute called CarLin has been added to the FitsChan class which controls the way CAR projections are handled when reading a FrameSet from a non-native FITS header. Some FITS writers use a CAR projection to represent a simple linear transformation between pixel coordinates and celestial sky coordinates. This is not consistent with the definition of the CAR projection in the draft FITS-WCS standard, which requires the resultant Mapping to include a 3D rotation from native spherical coordinates to celestial spherical coordinates, thus making the Mapping non-linear. Setting CarLin to 1 forces c+ astRead c- f+ AST\_READ f- to ignore the FITS-WCS standard and treat any CAR projections as simple linear Mappings from pixel coordinates to celestial coordinates. \item A bug has been fixed which could result in axis Format attributes set by the user being ignored under certain circumstances. \item A bug in the way tick marks positions are selected in the Plot class has been fixed. This bug could result in extra ticks marks being displayed at inappropriate positions. This bug manifested itself, for instance, if the Mapping represented by the Plot was a simple Cartesian to Polar Mapping. In this example, the bug caused tick marks to be drawn at negative radius values. \item A bug has been fixed which could prevent attribute settings from being read correctly by c+ astSet, c- f+ AST\_SET, f- etc., on certain platforms (MacOS, for instance). \end{enumerate} \subsection{Changes Introduced in V1.8-8} The following describes the most significant changes which occurred in the AST library between versions V1.8-7 and V1.8-8: \begin{enumerate} \item A bug has been fixed in the FitsChan class which could cause problems when creating a FrameSet from a FITS header containing WCS information stored in the form of Digitised Digitised Sky Survey (DSS) keywords. These problems only occurred for DSS fields in the southern hemisphere, and resulted in pixel positions being mapped to sky positions close to the corresponding \emph{northern} hemispshere field. \item A new method called c+ astBoundingBox c- f+ AST\_BOUNDINGBOX f- has been added to the Plot class. This method returns the bounding box of the previous graphical output produced by a Plot method. \item A new attribute called Invisible has been added to the Plot class which suppresses the graphical output normally produced by Plot methods. All the calculations needed to produce the normal output are still performed however, and so the bounding box returned by the new c+ astBoundingBox c- f+ AST\_BOUNDINGBOX f- method is still usable. \item Bugs have been fixed related to the appearance of graphical output produced by the Plot class. These bugs were to do with the way in which graphical elements relating to a specific axis (e.g. \texttt{Colour(axis1)}, etc.) interacted with the corresponding generic element (e.g. \texttt{Colour(axes)}, etc.). \end{enumerate} \subsection{Changes Introduced in V1.8-13} The following describes the most significant changes which occurred in the AST library between versions V1.8-8 and V1.8-13: \begin{enumerate} \item The FitsChan class has been modified so that LONPOLE keywords c+ are only produced by astWrite when necessary. For zenithal projections such as TAN, the LONPOLE keyword can always take its default value and so is not included in the FITS header produced by astWrite. c- f+ are only produced by AST\_WRITE when necessary. For zenithal projections such as TAN, the LONPOLE keyword can always take its default value and so is not included in the FITS header produced by AST\_WRITE f- Previously, the unnecessary production of a LONPOLE keyword could prevent FrameSets being written out using encodings which do not support the LONPOLE keyword (such as FITS-IRAF). \item The FitsChan class has been modified to retain leading and trailing spaces within COMMENT cards. \item The FitsChan class has been modified to only use CTYPE comments as axis labels if all non-celestial axes have unique non-blank comments (otherwise the CTYPE keyword values are used as labels). \item The FitsChan class has been modified so that it does not append a trailing ``Z'' character to the end of DATE-OBS keyword values. \item The FitsChan class has been modified to use latest list of FITS-WCS projections, as described in the FITS-WCS paper II, ``Representations of celestial coordinates in FITS'' (Calabretta \& Greisen, draft dated 23 April 2002). Support has been retained for the polynomial correction terms which previous drafts have allowed to be associated with TAN projections. \item The WcsMap class has additional projection types of AST\_\_TPN (which implements a distorted TAN projection) and AST\_\_SZP. The AST\_\_TAN projection type now represents a simple TAN projection and has no associated projection parameters. In addition, the usage of projection parameters has been brought into line with the the FITS-WCS paper II. \item The WcsMap class has been modified so that a ``get'' operation on a projection parameter attribute will return the default value defined in the FITS-WCS paper II if no value has been set for the attribute. Previously, a value of AST\_\_BAD was returned in such a situation. \item The Frame class has new attributes Top(axis) and Bottom(axis) which allow a ``plottable range'' to be specified for each Frame axis. The grid c+ produced by the astGrid method will not extend beyond these limits. c- f+ produced by the AST\_GRID routine will not extend beyond these limits. f- \end{enumerate} \subsection{Changes Introduced in V2.0} Note, Frame descriptions created using AST V2.0 will not be readable by applications linked with earlier versions of AST. This applies to Frame descriptions created using: \begin{itemize} \item the Channel class \item the FitsChan class if the NATIVE Encoding is used c+ \item the astShow function c- f+ \item the AST\_SHOW routine. f- \end{itemize} Applications must be re-linked with AST V2.0 in order to be able to read Frame descriptions created by AST v2.0. The following describes the most significant changes which have occurred in the AST library between versions V1.8-13 and V2.0 (the current version): \begin{enumerate} \item The default value for the Domain attribute provided by the CmpFrame class has been changed from ``CMP'' to a string formed by concatenating the Domain attributes of the two component Frames, separated by a minus sign. If both component Domains are blank, then the old default of ``CMP'' is retained for the CmpFrame Domain. \item The implementation of the c+ astWrite function c- f+ AST\_WRITE routine f- within the FitsChan class has been modified. It will now attempt to produce a set of FITS header cards to describe a FrameSet even if the number of axes in the Current Frames is greater than the number in the Base Frame (that is, if there are more WCS axes than pixel axes). This has always been possible with NATIVE encoding, but has not previously been possible for foreign encodings. The WCSAXES keyword is used to store the number of WCS axes in the FITS header. \item Another change to the c+ astWrite function c- f+ AST\_WRITE routine f- within the FitsChan class is that the ordering of ``foreign'' axes (\emph{i.e.} CTYPE keywords) is now chosen to make the CD (or PC) matrix as diagonal as possible - any element of axis transposition is removed by this re-ordering as recommended in FITS-WCS paper I. Previously the ordering was determined by the order of the axes in the Current Frame of the supplied FrameSet. This change does not affect NATIVE encoding. \item Support for spectral coordinate systems has been introduced throught the addition of two new classes, SpecFrame and SpecMap. The SpecFrame is a 1-dimensional Frame which can be used to describe positions within an electromagnetic spectrum in various systems (wavelength, frequency, various forms of velocity,~\emph{etc.}) and referred to various standards of rest (topocentric, geocentric, heliocentric LSRK,~\emph{etc.}). The SpecMap is a Mapping which can transform spectral axis values between these various systems and standards of rest. Note, FitsChans which have a foreign encoding (\emph{i.e.} any encoding other than NATIVE) are not yet able to read or write these new classes. \item Facilities have been added to the Frame class which allow differences in axis units to be taken into account when finding a Mapping between two Frames. In previous versions of AST, the Unit attribute was a purely descriptive item intended only for human readers - changing the value of Unit made no difference to the behaviour of the Frame. As of version 2.0, the Unit attribute can influence the nature of the Mappings between Frames. For instance, if the c+ astFindrame or astConvert c- f+ AST\_FINDRAME or AST\_CONVERT f- method is used to find the Mapping between an Axis with Unit set to ``m'' and another Axis with Unit set to ``km'', then the method will return a ZoomMap which introduces a scaling factor of 0.001 between the two axes. These facilities assume that units are specified following the rules included in FITS-WCS paper I (\emph{Representation of World Coordinates in FITS}, Greisen \& Calabretta). In order to minimise the risk of breaking existing software, the default behaviour for simple Frames is to ignore the Unit attribute (\emph{i.e.} to retain the previous behaviour). However, the new Frame method c+ astSetActiveUnit c- f+ AST\_SETACTIVEUNIT f- may be used to ``activate'' (or deactivate) the new facilities within a specific Frame. Note, the new SpecFrame class is different to the simple Frame class in that the new facilities for handling units are always active within a SpecFrame. \item The System and Epoch attributes fo the SkyFrame class have been moved to the parent Frame class. This enables all sub-classes of Frame (such as the new SpecFrame class) to share these attributes, and to provide suitable options for each class. \item The Frame class has a new attribute called AlignSystem, which allows control over the alignment process performed by the methods c+ astFindFrame and astConvert. c- f+ AST\_FINDFRAME and AST\_CONVERT. f- \item The CmpFrame class has been modified so that attributes of a component Frame can be accessed without needing to extract the Frame first. To do this, append an axis index to the end of the attribute name. For instance, if a CmpFrame contains a SpecFrame and a SkyFrame (in that order), then the StdOfRest attribute of the SpecFrame can be referred to as the ``StdOfRest(1)'' attribute of the CmpFrame. Likewise, the Equinox attribute of the SkyFrame can be accessed as the ``Equinox(2)'' (or equivalently ``Equinox(3)'') attribute of the CmpFrame. The ``System(1)'' attribute of the CmpFrame will refer to the System attribute of the SpecFrame, whereas the ``System(2)'' and ``System(3)'' attributes of the CmpFrame will refer to the System attribute of the SkyFrame (the ``System'' attribute without an axis specifier will refer to the System attribute of the CmpFrame as a whole, since System is an attribute of all Frames, and a CmpFrame is a Frame and so has its own System value which is independant of the System attributes of its component Frames). \item The algorithms used by the Plot class for determining when to omit overlapping axis labels, and the abbreviation of redundant leading fields within sexagesimal axis labels, have been improved to avoid some anomolous behaviour in previous versions. \item The curve drawing algorithm used by the Plot class has been modified to reduce the chance of it ``missing'' small curve sections, such as may be produced if a grid line cuts across the plot very close to a corner. Previously, these missed sections could sometimes result in axis labels being omitted. \item A new function c+ (astVersion) c- f+ (AST\_VERSION) f- has been added to return the version of the AST library in use. \item Bugs have been fixed in the Plot class which caused serious problems when plotting high precision data. These problems could range from the omission of some tick marks to complete failure to produce a plot. \end{enumerate} Programs which are statically linked will need to be re-linked in order to take advantage of these new facilities. \subsection{Changes Introduced in V3.0} The following describes the most significant changes which occurred in the AST library between versions V2.0 and V3.0: \begin{enumerate} \item Many changes have been made in the FitsChan class in order to bring the FITS-WCS encoding into line with the current versions of the FITS-WCS papers (see \url{http://www.atnf.csiro.au/people/mcalabre/WCS/}): \begin{itemize} \item The rotation and scaling of the pixel axes may now be specified using either CD\emph{i\_j} keywords, or PC\emph{i\_j} and CDELTj keywords. A new attribute called CDMatrix has been added to the FitsChan class to indicate which set of keywords should be used when writing a FrameSet to a FITS-WCS header. \item The FITS-WCS encoding now supports most of the conventions described in FITS-WCS paper III for the description of spectral coordinates. The exceptions are that the SSYSOBS keyword is not supported, and WCS stored in tabular form (as indicated by the ``-TAB'' algorithm code) is not supported. \item User-specified fiducial points for WCS projections are now supported by FitsChans which use FITS-WCS encoding. This use keywords PVi\_0, PVi\_1 and PVi\_2 for the longitude axis. \item When reading a FITS-WCS header, a FitsChan will now use keywords PVi\_3 and PVi\_4 for the longitude axis (if present) in preference to any LONPOLE and LATPOLE keywords which may be present. When writing a FITS-WCS header, both forms are written out. \item The number of WCS axes is stored in the WCSAXES keyword if its value would be different to that of the NAXIS keyword. \item Helio-ecliptic coordinates are now supported by FitsChans which use FITS-WCS encoding. This uses CTYPE codes ``HLON'' and ``HLAT''. The resulting SkyFrame will have a System value of ``HELIOECLIPTIC'', and all the usual facilities, such as conversion to other celestial systems, are available. \item The FITS-WCS encoding now supports most of the conventions described in FITS-WCS paper III for the description of spectral coordinates. The exceptions are that the SSYSOBS keyword is not supported, and WCS stored in tabular form (as indicated by the ``-TAB'' algorithm code) is not supported. \item When reading a FITS-WCS header, a FitsChan will now ignore any distortion codes which are present in CTYPE keywords. Here, a ``distortion code'' is the final group of four characters in a CTYPE value of the form ``xxxx-yyy-zzz'', as described in FITS-WCS paper IV. The exception to this is that the ``-SIP'' distortion code (as used by the Spitzer Space Telescope project - see \url{http://ssc.spitzer.caltech.edu/postbcd/doc/shupeADASS.pdf}) is interpreted correctly and results in a PolyMap being used to represent the distortion in the resulting FrameSet. Note, ``-SIP'' distortion codes can only be read, not written. A FrameSet which uses a PolyMap will not in general be able to be written out to a FitsChan using any foreign encoding (although NATIVE encoding can of course be used). \item The Warnings attribute of the FitsChan class now accepts values ``BadVal'' (which gives warnings about conversion errors when reading FITS keyword values), ``Distortion'' (which gives warnings about unsupported distortion codes within CTYPE values), and ``BadMat'' (which gives a warning if the rotation/scaling matrix cannot be inverted). \item When writing a FrameSet to a FitsChan which uses a non-Native encoding, the comment associated with any card already in the FitsChan will be retained if the keyword value being written is the same as the keyword value already in the FitsChan. \item A FrameSet which uses the non-FITS projection type AST\_\_TPN (a TAN projection with polynomial distortion terms) can now be written to a FitsChan if the Encoding attribute is set to FITS-WCS. The standard ``-TAN'' code is used within the CTYPE values, and the distortion coefficients are encoded in keywords of the form `` QVi\_ma'', which are directly analogous to the standard ``PVi\_ma'' projection parameter keywords. Thus a FITS reader which does not recognise the QV keywords will still be able to read the header, but the distortion will be ignored. \item The default value for DefB1950 attribute now depends on the value of the Encoding attribute. \item A new appendix has been added to SUN/210 and SUN/211 giving details of the implementation provided by the FitsChan class of the conventions contained in the first four FITS-WCS papers. \end{itemize} \item The SkyFrame class now supports two new coordinate systems ``ICRS'' and ``HELIOECLIPTIC''. The default for the System attribute for SkyFrames has been changed from ``FK5'' to ``ICRS''. \item The c+ astRate c- f+ AST\_RATE f- function has been added which allows an estimate to be made of the rate of change of a Mapping output with respect to one of the Mapping inputs. \item All attribute names for Frames of any class may now include an optional axis specifier. This includes those attributes which describe a property of the whole Frame. For instance, the Domain attribute may now be specified as ``Domain(1)'' in addition to the simpler ``Domain''. In cases such as this, where the attribute describes a property of the whole Frame, axis specifiers will usually be ignored. The exception is that a CmpFrame will use the presence of an axis specifier to indicate that the attribute name relates to the primary Frame containing the specified axis, rather than to the CmpFrame as a whole. \item A new subclass of Mapping, the PolyMap, has been added which performs a general N-dimensional polynomial mapping. \item A new subclass of Mapping, the GrismMap, has been added which models the spectral dispersion produced by a grating, prism or grism. \item A new subclass of Mapping, the ShiftMap, has been added which adds constant values onto all coordinates (this is equivalent to a WinMap with unit scaling on all axes). \item Minor bugs have been fixed within the Plot class to do with the choice and placement of numerical axis labels. \item The SphMap class has a new attribute called PolarLong which gives the longitude value to be returned when a Cartesian position corresponding to either the north or south pole is transformed into spherical coordinates. \item The WcsMap class now assigns a longitude of zero to output celestial coordinates which have a latitude of plus or minus 90 degrees. \item The NatLat and NatLon attributes of the WcsMap class have been changed so that they now return the fixed native coordinates of the projection reference point, rather than the native coordinates of the user-defined fiducial point. \item Notation has been changed in both the WcsMap and FitsChan classes to reflect the convention used in the FITS-WCS papers that index ``i'' refers to a world coordinate axis, and index ``j'' refers to a pixel axis. \item Changes have been made to several Mapping classes in order to allow the c+ astSimplify c- f+ AST\_SIMPLIFY f- function to make simplifications in a CmpMap which previously were not possible. \item The SlaMap class has been extended by the addition of conversions between FK5 and ICRS coordinates, and between FK5 and helio-ecliptic coordinates. \item The SpecMap class has been changed to use the equation for the refractive index of air as given in the current version of FITS-WCS paper III. Also, the forward and inverse transformations between frequency and air-wavelength have been made more compatible by using an iterative procedure to calculate the inverse. \end{enumerate} \subsection{Changes Introduced in V3.1} The following describes the most significant changes which have occurred in the AST library between versions V3.0 and V3.1 (the current version): \begin{enumerate} \item Addition of a new class called XmlChan - a Channel which reads and writes AST objects in the form of XML. \item A bug has been fixed in the Plot class which could cause incorrect graphical attributes to be used for various parts of the plot if either axis has no tick marks (i.e. if both major and minor tick marks have zero length). \end{enumerate} Programs which are statically linked will need to be re-linked in order to take advantage of these new facilities. \subsection{Changes Introduced in V3.2} The following describes the most significant changes which have occurred in the AST library between versions V3.1 and V3.2: \begin{enumerate} \item A new c+ function astPutCards c- f+ routine AST\_PUTCARDS f- has been added to the FitsChan class. This allows multiple concatenated header cards to be stored in a FitsChan in a single call, providing an alternative to the existing c+ astPutCards function. c- f+ AST\_PUTCARDS routine. f- \item Some signficant changes have been made to the simplification of Mappings which should resultin a greater degree of simplication taking place.Some bugs have also been fixed which could result in an infinite loop being entered when attempting to simplify certain Mappings. \item The FitsChan class now translates the spectral algorithm codes ``-WAV'', ``-FRQ'' and ``-VEL'' (specified in early drafts of paper III) to the corresponding ``-X2P'' form when reading a spectral axis description from a set of FITS header cards. \item A bug has been fixed in the FitsChan class which could cause keywords associated with alternate axis descriptions to be mis-interpreted. \item The Plot class now provides facilities for modifying the appearance of sub-strings within text strings such as axis labels, titles, \emph{etc}, by producing super-scripts, sub-scripts, changing the font colour, size, \emph{etc}. See attribute Escape. \item The default value of the Tol attribute of the Plot class has been changed from 0.001 to 0.01. This should not usually cause any significant visible change to the plot, but should make the plotting faster. You may need to set a lower value for Tol if you are producing a particularly large plot. \item The algorithm for finding the default value for the Gap attribute has been changed. This attribute specifies the gap between major axis values in an annotated grid drawn by the Plot class. The change in algorithm may cause the default value to be different to previous versions in cirtain circumstances. \item Some bugs have been fixed in the Plot class which could cause the system to hang for a long time while drawing certain all-sky grids (notable some of the FITS Quad-cube projections). \item The SkyAxis class has extended the Format attribute by the addition of the ``g'' option. this option is similar to the older ``l'' option in that it results in characters (``h'', ``m'', ``s'', \emph{etc}) being used as delimiters between the sexagesimal fields of the celestial position. The difference is that the ``g'' option includes graphics escape sequences in the returned formatted string which result in the field delimiter characters being drawn as super-scripts when plotted as numerical axis values by a Plot. \item The Plot class has been extended to include facilities for producing logarithmic axes. See attributes LogPlot, LogTicks, LogGap and LogLabel. \item New functions astGCap and astGScales have been added to the interface defined by file \verb+grf.h+. The ast\_link command has been modified so that the \verb+-mygrf+ switch loads dummy versions of the new grf functions. This means that applications should continue to build without any change. However, the facilities for interpreting escape sequences within strings drawn by the Plot class will not be available unless the new grf functions are implemented. If you choose to implement them, you should modify your linking procedure to use the \verb+-grf+ switch in place of the older \verb+-mygrf+ switch. See the description of the ast\_link command for details of the new switches. Also note that the astGQch function, whilst included in verb+grf.h+ in pervious versions of AST, was not actually called. As of this version of AST, calls are made to the astGQch function, and so any bugs in the implementation of astGQch may cause spurious behaviour when plotting text strings. \item A new 'static' method called astEscapes has been added which is used to control and enquire whether astGetC and astFormat will strip any graphical escape sequences which may be present out of the returned value. \item New attribute XmlPrefix has been added to the XmlChan class. It allows XML written by the XmlChan class to include an explicit namespace prefix on each element. \item New attribute XmlFormat has been added to the XmlChan class. It specifies the format in which AST objects should be written. \item A new class of Mapping, the TranMap, has been introduced. A TranMap takes its forward transformation from an existing Mapping, and its inverse transformation from another existing Mapping. \item A bug has been fixed in WcsMap which caused error reports to include erroneous axis numbers when referring to missing parameter values. \end{enumerate} \subsection{Changes Introduced in V3.3} The following describes the most significant changes which have occurred in the AST library between versions V3.2 and V3.3: \begin{enumerate} \item Options have been added to the SkyFrame class which allows the origin of celestial coordinates to be moved to any specified point. See the new attributes SkyRef, SkyRefIs, SkyRefP and AlignOffset. \item An option has been added to the FitsChan class which allows extra Frames representing cartesian projection plane coordinates (``intermediate world coordinates'' in the parlance of FITS-WCS) to be created when reading WCS information from a foreign FITS header. This option is controlled by a new attribute called Iwc. \item The FitsChan class which been modified to interpret FITS-WCS CAR projection headers correctly if the longitude reference pixel (CRPIX) is very large. \item The FITS-AIPS++ encoding in the FitsChan class now recognised spectral axes if they conform to the AIPS convention in which the spectral axis is descirbed by a CTYPE keyword od the form "AAAA-BBB" where ``AAAA'' is one of FREQ, VELO or FELO, and ``BBB'' is one of LSR, LSD, HEL or OBS. Such spectral axes can be both read and written. \item The FitsChan class now has a FITS-AIPS++ encoding which represents WCS information using FITS header cards recognised by the AIPS++ project. Support for spectral axes is identical to the FITS-AIPS encoding. \item The organisation of the AST distribution and the commands for building it have been changed. Whereas AST used to be built and installed with \verb+./mk build; ./mk install+, it now builds using the more standard idiom \verb+./configure; make; make install+. The installation location is controlled by the \verb+--prefix+ argument to ./configure (as is usual for other packages which use this scheme). Note that the INSTALL environment variable now has a \emph{different} meaning to that which it had before, and it should generally be \emph{unset}. Also, there is no need to set the SYSTEM variable. \item Shared libraries are now installed in the same directory as the static libraries. In addition, links to sharable libraries are installed with names which include version information, and ``libtool libraries'' are also installed (see \url{http://www.gnu.org/software/libtool/manual.html}). \item The \verb+ast_dev+ script has been removed. Instead, the location of the AST include files should be specified using the -I option when compiling. f+ \item The names of the installed AST include files have been changed to upper case. f- \end{enumerate} \subsection{Changes Introduced in V3.4} The following describes the most significant changes which have occurred in the AST library between versions V3.3 and V3.4: \begin{enumerate} \item The Mapping class has a new method c+ (astLinearApprox) c- f+ (AST\_LINEARAPPROX) f- which calculates the co-efficients of a linear approximation to a Mapping. \item The Format attribute for simple Frames and SkyFrames has been extended. It has always been possible, in both classes, to specify a precision by including a dot in the Format value followed by an integer (\emph{e.g.} ``\verb+dms.1+'' for a SkyFrame, or ``\verb+%.10g+'' for a simple Frame). The precision can now also be specified using an asterisk in place of the integer (\emph{e.g.} ``\verb+dms.*+'' or ``\verb+%.*g+''). This causes the precision to be derived on the basis of the Digits attribute value. \item The Plot class has been changed so that the default value used for the Digits attribute is chosen to be the smallest value which results in no pair of adjacent labels being identical. For instance, if an annotated grid is being drawn describing a SkyFrame, and the Format(1) value is set to ``\verb+hms.*g+'' (the ``g'' causes field delimiters to be drawn as superscripts), and the Digits(1) value is unset, then the seconds field will have a number of decimal places which results in no pair of labels being identical. \item Addition of a new class classed DSBSpecFrame. This is a sub-class of SpecFrame which can be used to describe spectral axes associated with dual sideband spectral data. \item The FitsChan class will now read headers which use the old ``-GLS'' projection code, converting them to the corresponding modern ``-SFL'' code, provided that the celestial axes are not rotated. \item The FitsChan class has a new Encoding, ``FITS-CLASS'', which allows the reading and writing of FITS headers using the conventions of the CLASS package - see \url{http://www.iram.fr/IRAMFR/GILDAS/doc/html/class-html/class.html}). \end{enumerate} \subsection{Changes Introduced in V3.5} The following describes the most significant changes which have occurred in the AST library between versions V3.4 and V3.5: \begin{enumerate} \item AST now provides facilities for representing regions of various shapes within a coordinate system. The Region class provides general facilities which are independent of the specific shape of region being used. Various sub-classes of Region are also now available which provide means of creating Regions of specific shape. Facilities provided by the Region class include testing points to see if they are inside the Region, testing two Regions for overlap, transforming Regions from one coordinate system to another \emph{etc}. \item A new class of 1-dimensional Frame called FluxFrame has been added which can be used to describe various systems for describing ovserved value at a single fixed spectral position. \item A new class of 2-dimensional Frame called SpecFluxFrame has been added which can be used to describe a 2-d frame spanned by a spectral position axis and and an observed value axis. \item A new class of Mapping called RateMap has been added. A RateMap encapsulates a previously created Mapping. The inputs of the RateMap correspond to the inputs of the encapsulated Mapping. All RateMaps have just a single output which correspond to the rate of change of a specified output of the encapsulated Mapping with respect to a specified input. \item The SkyFrame class now supports a value of ``J2000'' for System. This system is an equatorial system based on the mean dynamical equator and equinox at J2000, and differs slightly from an FK5(J2000) system. \item A new class called KeyMap has been added. A KeyMap can be used to store a collection of vector or scalar values or Objects, indexed by a character string rather than an integer. \item The parameter list for the c+ astRate c- f+ AST\_RATE f- method of the Mapping class has been modified. It no longer returns a second derivative estimate. Existing code which uses this method will need to be changed. \item Methods c+ (astSetFits) c- f+ (AST\_SETFITS) f- have been added to the FitsChan class to allow values for named keywords to be changed or added. \end{enumerate} \subsection{Changes Introduced in V3.6} The following describes the most significant changes which occurred in the AST library between versions V3.5 and V3.6: \begin{enumerate} \item If the Format attribute associated with an axis of a SkyFrame starts with a percent character (``\verb+%+''), then axis values are now formatted and unformatted as a decimal radians value, using the Format syntax of a simple Frame. \item The Plot class has a new attribute called Clip which controls the clipping performed by AST at the plot boundary. \item The keys used to label components of the PolyMap structure when a PolyMap is written out through a Channel have been changed. The new keys are shorter than the old keys and so can written succesfully to a FitsChan. The new PolyMap class always writes new styles keys but can read either old or new style keys. Consequently, PolyMap dumps written by this version of AST cannot be read by older versions of AST. \item A mimimal cut down subset of the C version of SLALIB is now included with the AST distribution and built as part of building AST. This means that it is no longer necessary to have SLALIB installed separately at your site. The SLALIB code included with AST is distrubuted under the GPL. The default behaviour of the ast\_link script is now to link with this internal slalib subset. However, the ``-csla'' option can still be used to force linking with an external full C SLALIB library. A new option ``-fsla'' has been introduced which forces linking with the external full Fortran SLALIB library. \end{enumerate} \subsection{Changes Introduced in V3.7} The following describes the most significant changes which occurred in the AST library between versions V3.6 and V3.7: \begin{enumerate} \item Support for time coordinate systems has been introduced throught the addition of two new classes, TimeFrame and TimeMap. The TimeFrame is a 1-dimensional Frame which can be used to describe moments in time (either absolute or relative) in various systems (MJD, Julian Epoch, \emph{etc.}) and referred to various time scales (TAI, UTC, UT1, GMST, \emph{etc}). The TimeMap is a Mapping which can transform time values between these various systems and time scales. Note, FitsChans which have a foreign encoding (\emph{i.e.} any encoding other than NATIVE) are not able to read or write these new classes. \end{enumerate} \subsection{Changes Introduced in V4.0} The following describes the most significant changes which occurred in the AST library between versions V3.7 and V4.0: \begin{enumerate} \item Experimental support for reading IVOA Space-Time-Coordinates (STC-X) descriptions using the XmlChan class has been added. Support is included for a subset of V1.20 of the draft STC specification. \item A new set of methods (AST\_REBIN/astRebin) has been added to the Mapping class. These are flux-conserving alternatives to the existing AST\_RESAMPLE/astResample methods. \end{enumerate} \subsection{Changes Introduced in V4.1} The following describes the most significant changes which occurred in the AST library between versions V4.0 and V4.1: \begin{enumerate} \item A new control flag has been added to the AST\_RESAMPLE/astResample functions which produces approximate flux conservation. \item New constants AST\_\_SOMB and AST\_\_SOMBCOS have been added to c+ ast.h. These specify kernels for astResample and astRebin c- f+ AST\_PAR. These specify kernels for AST\_RESAMPLE and AST\_REBIN f- based on the ``Sombrero'' function ( $2*J1(x)/x$ where $J1(x)$ is the first order Bessel function of the first kind). \item The SkyFrame class now supports a System value of AZEL corresponding to horizon (azimuth/elevation) coordinates. \item The FitsChan class allows the non-standard strings ``AZ--'' and ``EL--'' to be used as axis types in FITS-WCS CTYPE keyword values. \item The Frame class now has attributes ObsLon and ObsLat to specify the geodetic longitude and latitude of the observer. \item The ClockLon and ClockLat attributes have been removed from the TimeFrame class. Likewise, the GeoLon and GeoLat attributes have been removed from the SpecFrame class. Both classes now use the ObsLon and ObsLat attributes of the parent Frame class instead. However, the old attribute names can be used as synonyms for ObsLat and ObsLon. Also, dumps created using the old scheme can be read succesfully by AST V4.1 and converted to the new form. \item A new c+ function astMapSplit c- f+ routine AST\_MAPSPLIT f- has been added to the Mapping class. This splits a Mapping into two component Mappings which, when combined in parallel, are equivalent to the original Mapping. \item The default value for the SkyRefIs attribute has been changed from ``Origin'' to ``Ignored''. This means that if you want to use a SkyFrame to represent offsets from some origin position, you must now set the SkyRefIs attribute explicitly to either ``Pole'' or ``Origin'', in addition to assigning the required origin position to the SkyRef attribute. \end{enumerate} \subsection{Changes Introduced in V4.2} The following describes the most significant changes which occurred in the AST library between versions V4.1 and V4.2: \begin{enumerate} \item The SideBand attribute of the DSBSpecFrame class can now take the option ``LO'' in addition to ``USB'' and ``LSB''. The new option causes the DSBSpecFrame to represent the offset from the local oscillator frequency, rather than either of the two sidebands. \item The FitsChan class has been changed so that it writes out a VELOSYS keyword when creating a FITS-WCS encoding (VELOSYS indicates the topocentric apparent velocity of the standard of rest). FitsChan also strips out VELOSYS keywords when reading a FrameSet from a FITS-WCS encoding. \item The FitsChan class has a new method called c+ astRetainFits c- f+ AST\_RETAINFITS f- that indicates that the current card in the FitsChan should not be stripped out of the FitsChan when an AST Object is read from the FitsChan. Unless this method is used, all cards that were involved in the creation of the AST Object will be stripped from the FitsChan afte a read operation. \item A problem with unaligned memory access that could cause bus errors on Solaris has been fixed. \item A new read-only attribute called ObjSize has been added to the base Object Class. This gives the number of bytes of memory occupied by the Object. Note, this is the size of the internal in-memory representation of the Object, not the size of the textual representation produced by writing the Object out through a Channel. \item A new function c+ astTune c- f+ AST\_TUNE f- has been added which can be used to get and set global AST tuning parameters. At the moment there are only two such parameter, both of which are concerned with memory management within AST. \item A new method called c+ astTranGrid c- f+ AST\_TRANGRID f- has been added to the Mapping class. This method creates a regular grid of points covering a rectangular region within the input space of a Mapping, and then transforms this set of points into the output space of the Mapping, using a piecewise-continuous linear approximation to the Mapping if appropriate in order to achive higher speed. \item A new subclass of Mapping has been added called SwitchMap. A SwitchMap represents several alternate Mappings, each of which is used to transforms input positions within a different region of the input coordinate space. \item A new subclass of Mapping has been added called SelectorMap. A SelectorMap tests each input position to see if it falls within one of several Regions. If it does, the index of the Region containing the input position is returned as the Mapping output. \item The behaviour of the c+ astConvert c- f+ AST\_CONVERT f- method when trying to align a CmpFrame with another Frame has been modified. If no conversion between positions in the Frame and CmpFrame can be found, an attempt is now made to find a conversion between the Frame and one of two component Frames contained within the CmpFrame. Thus is should now be possible to align a SkyFrame with a CmpFrame containing a SkyFrame and a SpecFrame (for instance). The returned Mapping produces bad values for the extra axes (i.e. for the SpecFrame axis in the above example). \item The ``ast\_link\_adam'' and ``ast\_link'' scripts now ignore the \verb+-fsla+ and \verb+-csla+ options, and always link against the minimal cut-down version of SLALIB distributed as part of AST. \end{enumerate} \subsection{Changes Introduced in V4.3} The following describes the most significant changes which occurred in the AST library between versions V4.2 and V4.3: \begin{enumerate} \item The c+ astGetFitsS c- f+ AST\_GETFITSS f- function now strips trailing white space from the returned string, if the original string contains 8 or fewer characters \item The SpecFrame class has a new attribute called SourceSys that specified whether the SourceVel attribute (which specifies the rest frame of the source) should be accessed as an apparent radial velocity or a redshift. Note, any existing software that assumes that SourceVel always represents a velocity in km/s should be changed to allow for the possibility of SourceVel representing a redshift value. \end{enumerate} \subsection{Changes Introduced in V4.4} The following describes the most significant changes which occurred in the AST library between versions V4.3 and V4.4: \begin{enumerate} \item The c+ astFindFrame c- f+ AST\_FINDFRAME f- function can now be used to search a CmpFrame for an instance of a more specialised class of Frame (SkyFrame, TimeFrame, SpecFrame, DSBSpecFrame or FluxFrame). That is, if an instance of one of these classes is used as the ``template'' when calling c+ astFindFrame, c- f+ AST\_FINDFRAME, f- and the ``target'' being searched is a CmpFrame (or a FrameSet in which the current Frame is a CmpFrame), then the component Frames within the CmpFrame will be searched for an instance of the supplied template Frame, and, if found, a suitable Mapping (which will include a PermMap to select the required axes from the CmpFrame) will be returned by c+ astFindFrame. c- f+ AST\_FINDFRAME. f- Note, for this to work, the MaxAxes and MinAxes attributes of the template Frame must be set so that they cover a range that includes the number of axes in the target CmpFrame. \item The SkyFrame, SpecFrame, DSBSpecFrame, TimeFrame and FluxFrame classes now allow the MaxAxes and MinAxes attributes to be set freely to any value. In previous versions of AST, any attempt to change the value of MinAxes or MaxAxes was ignored, resulting in them always taking the default values. \item The DSBSpecFrame class has a new attribute called AlignSB that specifies whether or not to take account of the SideBand attributes when aligning two DSBSpecFrames using c+ astConvert. c- f+ AST\_CONVERT. f- \item The Frame class has a new attribute called Dut1 that can be used to store a value for the difference between the UT1 and UTC timescales at the epoch referred to by the Frame. \item The number of digits used to format the Frame attributes ObsLat and ObsLon has been increased. \item The use of the SkyFrame attribute AlignOffset has been changed. This attribute is used to control how two SkyFrames are aligned by c+ astConvert. c- f+ AST\_CONVERT. f- If the template and target SkyFrames both have a non-zero value for AlignOffset, then alignment occurs between the offset coordinate systems (that is, a UnitMap will always be used to align the two SkyFrames). \item The Plot class has a new attribute called ForceExterior that can be used to force exterior (rather than interior) tick marks to be produced. By default, exterior ticks are only produced if this would result in more than 3 tick marks being drawn. \item The TimeFrame class now supports conversion between angle based timescales such as UT1 and atomic based timescales such as UTC. \end{enumerate} \subsection{Changes Introduced in V4.5} The following describes the most significant changes that occurred in the AST library between versions V4.4 and V4.5: \begin{enumerate} \item All FITS-CLASS headers are now created with a frequency axis. If the FrameSet supplied to c+ astWrite c- f+ AST\_WRITE f- contains a velocity axis (or any other form of spectral axis) it will be converted to an equivalent frequency axis before being used to create the FITS-CLASS header. \item The value stored in the FITS-CLASS keyword ``VELO-LSR'' has been changed from the velocity of the source to the velocity of the reference channel. \item Addition of a new method call c+ astPurgeWCS c- f+ AST\_PURGEWCS f- to the FitsChan class. This method removes all WCS-related header cards from a FitsChan. \item The Plot class has a new attribute called GrfContext that can be used to comminicate context information between an application and any graphics functions registered with the Plot class via the c+ astGrfSet function. c- f+ AST\_GRFSET routine. f- \item Functions registered with the Plot class using c+ astGrfSet c- f+ AST\_GRFSET f- now take a new additional integer parameter, ``grfcon''. The Plot class sets this parameter to the value of the Plot's GrfContext attribute before calling the graphics function. NOTE, THIS CHANGE WILL REQUIRE EXISTING CODE THAT USES c+ astGrfSet c- f+ AST\_GRFSET f- TO BE MODIFIED TO INCLUDE THE NEW PARAMETER. \item The c+ astRebinSeq functions c- f+ AST\_REBINSEQ routines f- now have an extra parameter that is used to record the total number of input data values added into the output array. This is necessary to correct a flaw in the calculation of output variances based on the spread of input values. NOTE, THIS CHANGE WILL REQUIRE EXISTING CODE TO BE MODIFIED TO INCLUDE THE NEW PARAMETER (CALLED "NUSED"). \item Support has been added for the FITS-WCS ``HPX'' (HEALPix) projection. \item A new flag ``AST\_\_VARWGT'' can be supplied to c+ astRebinSeq. c- f+ AST\_REBINSEQ. f- This causes the input data values to be weighted using the reciprocals of the input variances (if supplied). \item The Frame class has a new read-only attribute called NormUnit that returns the normalised value of the Unit attribute for an axis. Here, ``normalisation'' means cancelling redundant units, etc. So for instance, a Unit value of ``s*(m/s)'' would result in a NormUnit value of ``m''. \item A new c+ function astShowMesh c- f+ routine AST\_SHOWMESH f- has been added to the Region class. It displays a mesh of points covering the surface of a Region by writing out a table of axis values to standard output. \item The Plot class now honours the value of the LabelUp attribute even if numerical labels are placed around the edge of the Plot. Previously LabelUp was only used if the labels were drawn within the interior of the plot. The LabelUp attribute controls whether numerical labels are drawn horizontally or parallel to the axis they describe. \item A bug has been fixed that could segmentation violations when setting attribute values. \end{enumerate} \subsection{Changes Introduced in V4.6} The following describes the most significant changes which have occurred in the AST library between versions V4.5 and V4.6: \begin{enumerate} \item The TimeFrame class now support Local Time as a time scale. The offset from UTC to Local Time is specified by a new TimeFrame attribute called LTOffset. \item A new class called Plot3D has been added. The Plot3D class allows the creation of 3-dimensional annotated coordinate grids. \item A correction for diurnal aberration is now included when converting between AZEL and other celestial coordinate systems. The correction is based on the value of the ObsLat Frame attribute (the geodetic latitude of the observer). \item A bug has been fixed which caused the DUT1 attribute to be ignored by the SkyFrame class when finding conversions between AZEL and other celestial coordinate systems. \end{enumerate} \subsection{Changes Introduced in V5.0} The following describes the most significant changes which occurred in the AST library between versions V4.6 and V5.0: \begin{enumerate} \item The AST library is now thread-safe (assuming that the POSIX pthreads library is available when AST is built). Many of the macros defined in the ast.h header file have changed. It is therefore necessary to re-compile all source code that includes ast.h. \item New methods astLock and astUnlock allow an AST Object to be locked for exclusive use by a thread. \item The TimeFrame class now support Local Time as a time scale. The offset from UTC to Local Time is specified by a new TimeFrame attribute called LTOffset. \item The Channel class has a new attribute called Strict which controls whether or not to report an error if unexpected data items are found within an AST Object description read from an external data source. Note, the default behaviour is now not to report such errors. This differs from previous versions of AST which always reported an error is unexpected input items were encountered. \end{enumerate} \subsection{Changes Introduced in V5.1} The following describes the most significant changes which occurred in the AST library between versions V5.0 and V5.1: \begin{enumerate} c+ \item The astUnlock function now has an extra parameter that controls whether or not an error is reported if the Object is currently locked by another thread. c- \item The Prism class has been modified so that any class of Region can be used to define the extrusion axes. Previously, only a Box or Interval could be used for this purpose. c+ \item The values of the AST\_\_THREADSAFE macro (defined in ast.h) have been changed from ``yes'' and ``no'' to ``1'' and ``0''. c- \item Improvements have been made to the way that Prisms are simplified when c+ astSimplify c- f+ AST\_SIMPLIFY f- is called. The changes mean that more types of Prism will now simplify into a simpler class of Region. \item The PointList class has a new method, c+ astPoints, c- f+ AST\_POINTS, f- that copies the axis values from the PointList into a supplied array. \item The PointList class has a new (read-only) attribute, ListSize, that gives the number of points stored in the PointList. \item The handling of warnings within different classes of Channel has been rationalised. The XmlStrict attribute and c+ astXmlWarnings c- f+ AST\_XMLWARNINGS f- function have been removed. The same functionality is now available via the existing Strict attribute (which has had its remit widened), a new attribute called ReportLevel, and the new c+ astWarnings c- f+ AST\_WARNINGS f- function. This new function can be used on any class of Channel. Teh FitsChan class retains its long standing ability to store warnings as header cards within the FitsChan, but it also now stores warnings in the parent Channel structure, from where they can be retrieved using the c+ astWarnings c- f+ AST\_WARNINGS f- function. \item A new function called c+ astIntercept c- f+ AST\_INTERCEPT f- has been added to the Frame class. This function finds the point of intersection beteeen two geodesic curves. \item A bug in the type-checking of Objects passed as arguments to constructor functions has been fixed. This bug could lead to applications crashing or showing strange behaviour if an inappropriate class of Object was supplied as an argument to a constructor. \item The c+ astPickAxes c- f+ AST\_PICKAXES f- function will now return a Region, if possible, when applied to a Region. If this is not possible, a Frame will be returned as before. \item The choice of default tick-mark for time axes has been improved, to avoid previous issues which could result in no suitable gap being found, or inappropriate tick marks when using formatted dates. \item A new function called c+ astTestFits c- f+ AST\_TESTFITS f- has been added to the FitsChan class. This function tests a FitsChan to see if it contains a defined value for specified FITS keyword. \item The AST\_\_UNDEF parameters used to flag undefined FITS keyword values have been removed. Use the new c+ astTestFits c- f+ AST\_TESTFITS f- function instead. c+ \item The astIsUndef functions used to test FITS keyword values have been removed. Use the new astTestFits function instead. c- \end{enumerate} \subsection{Changes Introduced in V5.2} The following describes the most significant changes which occurred in the AST library between versions V5.1 and V5.2: \begin{enumerate} \item A new method called c+ astSetFitsCM c- f+ AST\_SETFITSCM f- has been added to the FitsChan class. It stores a pure comment card in a FitsChan (that is, a card with no keyword name or equals sign). \item A new attribute called ObsAlt has been added to the Frame class. It records the geodetic altitude of the observer, in metres. It defaults to zero. It is used when converting times to or from the TDB timescale, or converting spectral positions to or from the topocentric rest frame, or converting sky positions to or from horizon coordinates. The FitsChan class will include its effect when creating a set of values for the OBSGEO-X/Y/Z keywords, and will also assign a value to it when reading a set of OBSGEO-X/Y/Z keyword values from a FITS header. \item The TimeMap conversions ``TTTOTDB'' and ``TDBTOTT'', and the SpecMap conversions ``TPF2HL'' and ``HLF2TP'', now have an additional argument - the observer's geodetic altitude. \item The Polygon class has been modified to make it consistent with the IVOA STC definition of a Polygon. Specifically, the inside of a polygon is now the area to the left of each edge as the vertices are traversed in an anti-clockwise manner, as seen from the inside of the celestial sphere. Previously, AST used the anti-clockwise convention, but viewed from the outside of the celestial sphere instead of the inside. Any Polygon saved using previous versions of AST will be identified and negated automatically when read by AST V5.2. \item A new class of Channel, called StcsChan, has been added that allows conversion of suitable AST Objects to and from IVOA STC-S format. \item A new method called c+ astRemoveRegions c- f+ AST\_REMOVEREGIONS f- has been added to the Mapping class. It searches a (possibly compound) Mapping (or Frame) for any instances of the AST Region class, and either removes them, or replaces them with UnitMaps (or equivalent Frames). It can be used to remove the masking effects of Regions from a compound Mapping or Frame. \item A new method called c+ astDownsize c- f+ AST\_DOWNSIZE f- has been added to the Polygon class. It produces a new Polygon that contains a subset of the vertices in the supplied Polygon. The subset is chosen to retain the main features of the supplied Polygion, in so far as that is possible, within specified constraints. \item A new constructor called c+ astOutline c- f+ AST\_OUTLINE f- has been added to the Polygon class. Given a 2D data array, it identifies the boundary of a region within the array that holds pixels with specified values. It then creates a new Polygon to describe this boundary to a specified accuracy. \item A new set of methods, called c+ astMapGetElem c- f+ AST\_MAPGETELEM f- has been added to the KeyMap class. They allow a single element of a vector valued entry to be returned. \item A new attribute called KeyError has been added to the KeyMap Class. It controls whether the c+ astMapGet... c- f+ AST\_MAPGET... f- family of functions report an error if an entry with the requested key does not exist in the KeyMap. \end{enumerate} \subsection{Changes Introduced in V5.3} The following describes the most significant changes which occurred in the AST library between versions V5.2 and V5.3: \begin{enumerate} \item The details of how a Frame is aligned with another Frame by the c+ astFindFrame and astConvert c- f+ AST\_FINDFRAME and AST\_CONVERT f- functions have been changed. The changes mean that a Frame can now be aligned with an instance of a sub-class of Frame, so long as the number of axes and the Domain values are consistent. For instance, a basic 2-dimensional Frame with Domain ``SKY'' will now align succesfully with a SkyFrame, conversion between the two Frames being achieved using a UnitMap. c+ \item The arrays that supply input values for astMapPut1 are now declared ``const''. c- \item Added method c+ astMatchAxes c- f+ AST\_MATCHAXES f- to the Frame class. This method allows corresponding axes within two Frames to be identified. \item The c+ astAddFrame c- f+ AST\_ADDFRAME f- method can now be used to append one or more axes to all Frames in a FrameSet. \end{enumerate} \subsection{Changes Introduced in V5.3-1} The following describes the most significant changes which have occurred in the AST library between versions V5.3 and V5.3-1: \begin{enumerate} c+ \item The utility functions provided by the AST memory management layer are now documented in an appendix. c- \item The KeyMap class now supports entries that have undefined values. A new method called c+ astMapPutU c- f+ AST\_MAPPUTU f- will store an entry with undefined value in a keymap. Methods that retrieve values from a KeyMap c+ (astMapGet0, etc.) c- f+ (AST\_MAPGET0, etc.) f- ignore entries with undefined values when searching for an entry with a given key. \item The KeyMap class has a new method called c+ astMapCopy c- f+ AST\_MAPCOPY f- that copies entries from one KeyMap to another KeyMap. \item The KeyMap class has a new boolean attribute called MapLocked. If c+ non-zero, c- f+ .TRUE., f- an error is reported if an attempt is made to add any new entries to a KeyMap (the value associated with any old entry may still be changed without error). The default is c+ zero. c- f+ .FALSE. f- \item The Object class has a new method called astHasAttribute/AST\_HASATTRIBUTE that returns a boolean value indicating if a specified Object has a named attribute. \item The SkyFrame class has two new read-only boolean attributes called IsLatAxis and IsLonAxis that can be used to determine the nature of a specified SkyFrame axis. \item A bug has been fixed in the c+ astRebin(Seq) c- f+ AST\_REBIN(SEQ) f- methods that could cause flux to be lost from the edges of the supplied array. \item A bug has been fixed in the c+ astRebin(Seq) c- f+ AST\_REBIN(SEQ) f- methods that caused the first user supplied parameter to be interpreted as the full width of the spreading kernel, rather than the half-width. \item The StcsChan class now ignores case when reading STC-S phrases (except that units strings are still case sensitive). \item A new Mapping method, c+ astQuadApprox, c- f+ AST\_QUADAPPROX, f- produces a quadratic least-squares fit to a 2D Mapping. \item A new Mapping method, c+ astSkyOffsetMap, c- f+ AST\_SKYOFFSETMAP, f- produces a Mapping from absolute SkyFrame coordinates to offset SkyFrame coordinates. \item The Channel class now has an Indent attribute that controls indentation in the text created by c+ astWrite. c- f+ AST\_WRITE. f- The StcsIndent and XmlIndent attributes have been removed. \item All classes of Channel now use the string ``'' to represent the floating point value AST\_\_BAD, rather than the literal formatted value (typically ``-1.79769313486232e+308'' ). \item The KeyMap class now uses the string ``'' to represent the floating point value AST\_\_BAD, rather than the literal formatted value (typically ``-1.79769313486232e+308'' ). \item The KeyMap class has a new method called c+ astMapPutElem c- f+ AST\_MAPPUTELEM f- that allows a value to be put into a single element of a vector entry in a KeyMap. The vector entry is extended automatically to hold the new element if required. \item The DSBSpecFrame class now reports an error if the local oscillator frequency is less than the absoliute value of the intermediate frequency. \end{enumerate} \subsection{Changes Introduced in V5.3-2} The following describes the most significant changes which occurred in the AST library between versions V5.3-1 and V5.3-2: \begin{enumerate} \item A bug has been fixed in the FitsChan class that could cause wavelength axes to be assigned the units ``m/s'' when reading WCS information from a FITS header. \item The c+ astSet function c- f+ AST\_SET routine f- now allows literal commas to be included in string attribute values. String attribute values that include a literal comma should be enclosed in quotation marks. \item A bug in FitsChan has been fixed that caused ``-SIN'' projection codes within FITS-WCS headers to be mis-interpreted, resulting in no FrameSet being read by astRead. \item The KeyMap class has a new attribute called ``SortBy''. It controls the order in which keys are returned by the c+ astMapKey c- f+ AST\_MAPKEY f- function. Keys can be sorted alphabetically or by age, or left unsorted. \item Access to KeyMaps holding thousands of entries is now significantly faster. \item KeyMaps can now hold word (i.e. c+ short integer) c- f+ INTEGER*2) f- values. \end{enumerate} \subsection{Changes Introduced in V5.4-0} The following describes the most significant changes which occurred in the AST library between versions V5.3-2 and V5.4-0: \begin{enumerate} \item the FitsChan class now has an option to support reading and writing of FITS-WCS headers that use the -TAB algorithm described in FITS-WCS paper III. This option is controlled by a new FitsChan attribute called TabOK. See the documentation for TabOK for more information. \item A new class called ``Table'' has been added. A Table is a KeyMap in which each entry represents a cell in a two-dimensional table. \item A new class called ``FitsTable'' has been added. A FitsTable is a Table that has an associated FitsChan holding headers appropriate to a FITS binary table. \item KeyMaps can now hold byte values. These are held in variables of type c+ "unsigned char". c- f+ BYTE. f- \item KeyMaps have a new attribute called KeyCase that can be set to zero to make the handling of keys case insensitive. \item a memory leak associated with the use of the c+ astMapPutElem c- f+ AST\_MAPPUTELEM f- functions has been fixed. \item A new method called c+ astMapRename c- f+ AST\_MAPRENAME f- has been added to rename existing entry in a KeyMap. \end{enumerate} \subsection{Changes Introduced in V5.5-0} The following describes the most significant changes which occurred in the AST library between versions V5.4-0 and V5.5-0: \begin{enumerate} \item The FitsChan ``TabOK'' attribute is now an integer value rather than a boolean value. If TabOK is set to a non-zero positive integer before invoking the c+ astWrite c- f+ AST\_WRITE f- method, its value is used as the version number for any table that is created as a consequence of the write operation. This is the value stored in the PVi\_1a keyword in the IMAGE header, and the EXTVER keyword in the binary table header. In previous versions of AST, the value used for these headers could not be controlled and was fixed at 1. If TabOK is set to a negative or zero value, the -TAB algorithm will not be supported by either the c+ astWrite or astRead c- f+ AST\_WRITE or AST\_READ f- methods. \end{enumerate} \subsection{Changes Introduced in V5.6-0} The following describes the most significant changes which occurred in the AST library between versions V5.5-0 and V5.6-0: \begin{enumerate} \item c+ New functions astBBuf and astEBuf c- f+ New routines AST\_BBUF and AST\_EBUF f- have been added to the Plot class. These control the buffering of graphical output produced by other Plot methods. \item New functions astGBBuf and astGEBuf have been added to the interface defined by file \verb+grf.h+. The ast\_link command has been modified so that the \verb+-grf_v3.2+ switch loads dummy versions of the new grf functions. This means that applications that use the \verb+-grf_v3.2+ switch should continue to build without any change. However, the new public c+ functions astBBuf and astEBuf c- f+ routines AST\_BBUF and AST\_EBUF f- will report an error unless the new grf functions are implemented. If you choose to implement them, you should modify your linking procedure to use the \verb+-grf+ (or \verb+-grf_v5.6+ ) switch in place of the older \verb+-grf_v3.2+ switch. See the description of the ast\_link command for details of these switches. \item New method c+ astGetRegionMesh c- f+ AST\_GETREGIONMESH f- returns a set of positions covering the boundary, or volume, of a supplied Region. \end{enumerate} \subsection{ChangesIntroduced in V5.6-1} The following describes the most significant changes which occurred in the AST library between versions V5.6-0 and V5.6-1: \begin{enumerate} \item Tables can now have any number of parameters describing the global properties of the Table. \item Frames now interpret the unit string ``A'' as meaning ``Ampere'' rather than ``Angstrom'', as specified by FITS-WCS paper I. \item A bug has been fixed in the c+ astFindFrame c- f+ AST\_FINDFRAME f- method that allowed a template Frame of a more specialised class to match a target frame of a less specialised class. For example, this bug would allow a template SkyFrame to match a target Frame. This no longer happens. \end{enumerate} \subsection{Changes Introduced in V5.7-0} The following describes the most significant changes which occurred in the AST library between versions V5.6-1 and V5.7-0: \begin{enumerate} \item The FitsChan class support for the IRAF-specific ``TNX'' projection has been extended to include reading TNX headers that use a Chebyshev representation for the distortion polynomial. \item The FitsChan class support for the IRAF-specific ``ZPX'' projection has been extended to include reading ZPX headers that use simple or Chebyshev representation for the distortion polynomial. \item A bug has been fixed in the FitsChan class that caused headers including the Spitzer ``-SIP'' distortion code to be read incorrectly if no inverse polynomial was specified in the header. \item A new attribute called PolyTan has been added to the FitsChan class. It can be used to indicate that FITS headers that specify a TAN projection should be interpreted according to the ``distorted TAN'' convention included in an early draft of FITS-WCS paper II. Such headers are created by (for instance) the SCAMP tool (\url{http://www.astromatic.net/software/scamp}). \item The PolyMap class now provides a method called c+ astPolyTran c- f+ AST\_POLYTRAN f- that adds an inverse transformation to a PolyMap by sampling the forward transformation on a regular grid, and then fitting a polynomial function from the resulting output values to the grid of input values. \end{enumerate} \subsection{Changes Introduced in V5.7-1} The following describes the most significant changes which occurred in the AST library between versions V5.7-0 and V5.7-1: \begin{enumerate} \item - All classes of Channel can now read to and write from specified text files, without the need to provide source and sink functions when the Channel is created. The files to use are specified by the new attributes SourceFile and SinkFile. \item - The FitsChan class now ignores trailing spaces in character-valued WCS keywords when reading a FrameSet from a FITS header. \item - If the FitsChan astRead method reads a FITS header that uses the -SIP (Spitzer) distortion code within the CTYPE values, but which does not provide an inverse polynomial correction, the FitsChan class will now use the PolyTran method of the PolyMap class to create an estimate of the inverse polynomial correction. \end{enumerate} \subsection{Changes Introduced in V5.7-2} The following describes the most significant changes which occurred in the AST library between versions V5.7-1 and V5.7-2: \begin{enumerate} c+ \item The Object class has a new function astToString (C only), which creates an in-memory textual serialisation of a given AST Object. A corresponding new function called astFromString re-creates the Object from its serialisation. c- \item The PolyMap class can now use an iterative Newton-Raphson method to evaluate the inverse the inverse transformation if no inverse transformation is defined when the PolyMap is created. \item The FitsChan class has a new method c+ astWriteFits c- f+ AST\_WRITEFITS f- which writes out all cards currently in the FitsChan to the associated external data sink (specified either by the SinkFile attribute or the sink function supplied when the FitsChan was created), and then empties the FitsChan. \item The FitsChan class has a new read-only attribute called ``Nkey'', which holds the number of keywords for which values are held in a FitsChan. \item The FitsChan c+ astGetFits c- f+ AST\_GETFITS f- methods can now be used to returned the value of the current card. \item The FitsChan class has a new read-only attribute called ``CardType'', which holds the data type of the keyword value for the current card. \item The FitsChan class has a new method c+ astReadFits c- f+ AST\_READFITS f- which forces the FitsChan to reads cards from the associated external source and appends them to the end of the FitsChan. \item - If the FitsChan astRead method reads a FITS header that uses the -SIP (Spitzer) distortion code within the CTYPE values, but which does not provide an inverse polynomial correction, and for which the PolyTran method of the PolyMap class fails to create an accurate estimate of the inverse polynomial correction, then an iterative method will be used to evaluate the inverse correction for each point transformed. \end{enumerate} \subsection{Changes Introduced in V6.0} The following describes the most significant changes which occurred in the AST library between versions V5.7-2 and V6.0: \begin{enumerate} \item This version of AST is the first that can be used with the Python AST wrapper module, starlink.Ast, available at \url{http://github.com/timj/starlink-pyast}. \item When reading a FITS-WCS header, the FitsChan class now recognises the non-standard ``TPV'' projection code within a CTYPE keyword value. This code is used by SCAMP (see www.astromatic.net/software/scamp) to represent a distorted TAN projection. \item The Plot class has been changed to remove visual anomalies (such as incorrectly rotated numerical axis labels) if the graphics coordinates have unequal scales on the X and Y axes. - The graphics escape sequences used to produce graphical sky axis labels can now be changed using the new c+ function astTuneC. c- f+ routine AST\_TUNEC. f- \end{enumerate} \subsection{Changes Introduced in V6.0-1} The following describes the most significant changes which occurred in the AST library between versions V6.0 and V6.0-1: \begin{enumerate} \item The FitsChan class now recognises the Spitzer ``-SIP'' distortion code within FITS headers that describe non-celestial axes, as well as celestial axes. \item A bug has been fixed that could cause inappropriate equinox values to be used when aligning SkyFrames if the AlignSystem attribute is set. \item The versioning string for AST has changed from ``$.-$'' to ``$..$''. \end{enumerate} \subsection{Changes Introduced in V7.0.0} The following describes the most significant changes which occurred in the AST library between versions V6.0-1 and V7.0.0: \begin{enumerate} \item Fundamental positional astronomy calculations are now performed using the IAU SOFA library where possible, and the Starlink PAL library \xref{SUN/268}{sun268}{} otherwise (the PAL library contains a subset of the Fortran Starlink SLALIB library re-written in C). Copies of these libraries are bundled with AST and so do not need to be obtained or built separately, although external copies of SOFA and PAL can be used if necessary by including the ``\texttt{--with-external\_pal}'' option when configuring AST. \end{enumerate} \subsection{Changes Introduced in V7.0.1} The following describes the most significant changes which occurred in the AST library between versions V7.0.0 and V7.0.1: \begin{enumerate} \item The levmar and wcslib code distributed within AST is now stored in the main AST library (libast.so) rather than in separate libraries. \end{enumerate} \subsection{Changes Introduced in V7.0.2} The following describes the most significant changes which occurred in the AST library between versions V7.0.1 and V7.0.2: \begin{enumerate} \item The libast\_pal library is no longer built if the ``--with-external\_pal'' option is used when AST is configured. \end{enumerate} \subsection{Changes Introduced in V7.0.3} The following describes the most significant changes which occurred in the AST library between versions V7.0.2 and V7.0.3: \begin{enumerate} \item A bug has been fixed which could cause an incorrect axis to be used when accessing axis attributes within CmpFrames. This could happen if axes within the CmpFrame have been permuted. \item A bug has been fixed in the SkyFrame class that could cause the two values of the SkyRef and/or SkyRefP attributes to be reversed. \item Bugs have been fixed in the CmpRegion class that should allow the border around a compound Region to be plotted more quickly, and more accurately. Previously, component Regions nested deeply inside a CmpRegion may have been completely or partially ignored. \item A bug has been fixed in the Plot3D class that caused a segmentation violation if the MinTick attribute was set to zero. \item The astResampleX set of methods now includes astResampleK and astResampleUK that handles 64 bit integer data. \end{enumerate} \subsection{Changes Introduced in V7.0.4} The following describes the most significant changes which occurred in the AST library between versions V7.0.3 and V7.0.4: \begin{enumerate} \item The previously private grf3d.h header file is now installed into prefix/include. \end{enumerate} \subsection{Changes Introduced in V7.0.5} The following describes the most significant changes which occurred in the AST library between versions V7.0.4 and V7.0.5: \begin{enumerate} \item The FitsChan class can now read FITS headers that use the SAO convention for representing distorted TAN projections, based on the use of ``COi\_m'' keywords to hold the coefficients of the distortion polynomial. \end{enumerate} \subsection{Changes Introduced in V7.0.6} The following describes the most significant changes which occurred in the AST library between versions V7.0.5 and V7.0.6: \begin{enumerate} \item A bug has been fixed in astRebinSeq which could result in incorrect normalisation of the final binned data and variance values. \item When reading a FrameSet from a FITS-DSS header, the keywords CNPIX1 and CNPIX2 now default to zero if absent. Previously an error was reported. \end{enumerate} \subsection{Changes Introduced in V7.1.0} The following describes the most significant changes which occurred in the AST library between versions V7.0.6 and V7.1.0: \begin{enumerate} \item IMPORTANT! The default behaviour of astRebinSeq is now NOT to conserve flux. To conserve flux, the AST\_\_CONSERVEFLUX flag should be supplied when calling c+ astRebinSeq. c- f+ AST\_REBINSEQ. f- Without this flag, each output value is a weighted mean of the neighbouring input values. \item A new flag AST\_\_NONORM can be used with astRebinSeq to indicate that normalisation of the output arrays is not required. In this case no weights array need be supplied. \item A bug has been fixed in c+ astAddFrame method c- f+ AST\_ADDFRAME routine f- that could result in the incorrect inversion of Mappings within the FrameSet when the AST\_\_ALLFRAMES flag is supplied for the c+ "iframe" parameter. c- f+ IFRAME argument. f- \item The c+ astRate method c- f+ AST\_RATE function f- has been re-written to make it faster and more reliable. \end{enumerate} \subsection{Changes Introduced in V7.1.1} The following describes the most significant changes which occurred in the AST library between versions V7.1.0 and V7.1.1: \begin{enumerate} \item When a FitsChan is used to write an ``offset'' SkyFrame (see attribute SkyRefIs) to a FITS-WCS encoded header, two alternate axis descriptions are now created - one for the offset coordinates and one for the absolute coordinates. If such a header is subsequently read back into AST, the original offset SkyFrame is recreated. \item A bug has been fixed in FitsChan that caused inappropriate CTYPE values to be generated when writing a FrameSet to FITS-WCS headers if the current Frame describes generalised spherical coordinates (i.e. a SkyFrame with System=Unknown). \end{enumerate} \subsection{Changes Introduced in V7.2.0} The following describes the most significant changes which occurred in the AST library between versions V7.1.1 and V7.2.0: \begin{enumerate} \item A new method call c+ astMapDefined c- f+ AST\_MAPDEFINED f- has been added to the KeyMap class. It checks if a gtiven key name has a defined value in a given KeyMap. \end{enumerate} \subsection{Changes Introduced in V7.3.0} The following describes the most significant changes which occurred in the AST library between versions V7.2.0 and V7.3.0: \begin{enumerate} c+ \item The interface for the astRebinSeq family of functions has been changed in order to allow a greater number of pixels to be pasted into the output array. The "nused" parameter is now a pointer to a "int64\_t" variable, instead of an "int". APPLICATION CODE SHOULD BE CHANGED ACCORDINGLY TO AVOID SEGMENTATION FAULTS AND OTHER ERRATIC BEHAVIOUR. c- f+ \item The interface for the AST\_REBINSEQ family of routines has been changed in order to allow a greater number of pixels to be pasted into the output array. The NUSED parameter is now an INTEGER*8 variable, instead of an INTEGER. APPLICATION CODE SHOULD BE CHANGED ACCORDINGLY TO AVOID SEGMENTATION FAULTS AND OTHER ERRATIC BEHAVIOUR. f- \item Added a new facility to the FrameSet class to allow each Frame to be associated with multiple Mappings, any one of which can be used to connect the Frame to the other Frames in the FrameSet. The choice of which Mapping to use is controlled by the new ``Variant'' attribute of the FrameSet class. \item Mappings (but not Frames) that have a value set for their Ident attribute are now left unchanged by the c astSimplify function. f AST\_SIMPLIFY routine. \end{enumerate} \subsection{Changes Introduced in V7.3.1} The following describes the most significant changes which occurred in the AST library between versions V7.3.0 and V7.3.1: \begin{enumerate} \item Fix a bug that could cauise a segmentation violation when reading certain FITS headers that use a TNX projection. \end{enumerate} \subsection{Changes Introduced in V7.3.2} The following describes the most significant changes which occurred in the AST library between versions V7.3.1 and V7.3.2: \begin{enumerate} \item Fix support for reading FITS header that use a GLS projection. Previously, an incorrect transformation was used for such projections if any CRVAL or CROTA value was non-zero. \item The KeyMap class has new sorting options ``KeyAgeUp'' and ``KeyAgeDown'' that retain the position of an existing entry if its value is changed. See the SortBy attribute. \item A bug has been fixed in the FitsChan class that caused CDELT keywords for sky axes to be treated as radians rather than degrees when reading a FITS header, if the corresponding CTYPE values included no projection code. \end{enumerate} \subsection{Changes Introduced in V7.3.3} The following describes the most significant changes which occurred in the AST library between versions V7.3.2 and V7.3.3: \begin{enumerate} \item The FitsChan class has new attributes CardName and CardComm, which hold the keyword name and comment of the current card. \item When using the FitsChan class to read FITS-WCS headers that include polynomial distortion in the SIP format, any inverse transformation specified in the header is now ignored and a new inverse is created to replace it based on the supplied forward transformation. Previously, an inverse was created only if the header did not include an inverse. The accuracy of the inverse transformation has also been improved, although it may now be slower to evaluate in some circumstances. \end{enumerate} \subsection{Changes Introduced in V7.3.4} The following describes the most significant changes which occurred in the AST library between versions V7.3.3 and V7.3.4: \begin{enumerate} \item By default, the simplification of Polygons no longer checks that the edges are not bent by the simplification. A new attribute, SimpVertices, can be set to zero in order to re-instate this check. \item The Polygon class has a new mathod, c+ astConvex, c- f+ AST\_CONVEX, f- that returns a Polygon representing the shortest polygon (i.e. convex hull) enclosing a specified set of pixel values within a supplied array. \end{enumerate} \subsection{Changes Introduced in V8.0.0} The following describes the most significant changes which occurred in the AST library between versions V7.3.4 and V8.0.0: \begin{enumerate} \item AST is now distributed under the Lesser GPL licence. \item The PolyMap class now uses files copied from the C/C++ Minpack package (see \url{http://devernay.free.fr/hacks/cminpack/index.html}) to perform least squares fitting of N-dimensional polynomials. \item Use of the IAU SOFA library has been replaced by ERFA library, which is a re-badged copy of SOFA distributed under a less restrictive license. A copy of ERFA is included within AST. \end{enumerate} \subsection{Changes Introduced in V8.0.1} The following describes the most significant changes which occurred in the AST library between versions V8.0.0 and V8.0.1: \begin{enumerate} \item The Base and Current attributes of a FrameSet may now be set using the Domain name or the index of the required Frame. \item The order of WCS axes within new FITS-WCS headers created by astWrite can now be controlled using a new attribute called FitsAxisOrder. \item Supported added for FITS XPH (polar HEALPIX) projection. c+ \item The macro used to invoke the astAppendString utility function has changed to allow printf-style converstions to be included in the supplied text. Any code that uses this macro must be re-compiled. \item The astRebin and astRebinSeq family of functions now include support for arrays with char (byte) and unsigned char (unsigned byte) data types. c- f+ \item The AST\_REBIN and AST\_REBINSEQ family of functions now include support for arrays with \_BYTE (byte) and and \_UBYTE (unsigned byte) data types. f- \end{enumerate} \subsection{Changes Introduced in V8.0.2} c+ The following describes the most significant changes which occurred in the AST library between versions V8.0.1 and V8.0.2: \begin{enumerate} \item For security reasons, the change introduced to astAppendString in V8.0.1 has been moved to a new function called astAppendStringf, and astAppendString itself has been reverted to its V8.0.0 version. Any software that has been built against V8.0.1 will need to be re-compiled and re-linked against V8.0.2. \end{enumerate} c- f+ The changes that occurred in the AST library between versions V8.0.1 and V8.0.2 only affect the C interface. The Fortran interface remains the same as V8.0.1. f- \subsection{Changes Introduced in V8.0.3} The following describes the most significant changes which occurred in the AST library between versions V8.0.2 and V8.0.3: \begin{enumerate} \item Methods c+ astRebin, astRebinSeq, astResample and astTranGrid c- f+ AST\_REBIN, AST\_REBINSEQ, AST\_RESAMPLE and AST\_TRANGRID. f- now report an error if an array is specified that has more pixels than can be counted by a 32 bit integer. \item The hypertext documentation is now generated using Tex4HT rather than latex2html. The format of the hypertext docs has changed significantly. \item Another bug fix associated with reading CAR projections from FITS-WCS headers. f+ \item Trailing spaces supplied within attribute setting strings are now ignored. f- c+ \item Constructor options strings of the form ``\texttt{..., "\%s", text );}'' can now be supplied. This avoids a security issue associated with the alternative form ``\texttt{..., text );}''. c- \end{enumerate} \subsection{Changes Introduced in V8.0.4} The following describes the most significant changes which occurred in the AST library between versions V8.0.3 and V8.0.4: \begin{enumerate} \item The behaviour of the c+ astAddFrame method has been changed slightly. Previously, astAddFrame c- f+ AST\_ADDFRAME method has been changed slightly. Previously, AST\_ADDFRAME f- modified the FrameSet by storing references to the supplied Mapping and Frame objects within the FrameSet. This meant that any subsequent changes to the current Frame of the modified FrameSet also affected the supplied Frame object. Now, deep copies of the Mapping and Frame objects (rather than references) are stored within the modified FrameSet. This means that subsequent changes to the modified FrameSet will now have no effect on the supplied Frame. \item The choice of default tick-mark gaps for time axes has been improved, to avoid a previous issue which could result in no suitable gap being found. - A new method called c+ astRegionOutline c- f+ AST\_REGIONOUTLINE f- has been added to the Plot class. It draws the outline of a supplied AST Region. \item A bug has been fixed that could cause astSimplfy to enter an infinite loop. \item Some improvements have been made to the Mapping simplification process that allow more Mappings to be simplified. \item The Frame class has a new read-only attribute called InternalUnit, which gives the units used for the unformatted (i.e. floating-point) axis values used internally by application code. For most Frames, the InternalUnit value is just the same as the Unit value (i.e. formatted and unformatted axis values use the same units). However, the SkyFrame class always returns ``\texttt{rad}'' for InternalUnit, regardless of the value of Unit, indicating that floating-point SkyFrame axis values are always in units of radians. \item The LutMap class has a new attribute called LutEpsilon, which specifies the relative error of the values in the table. It is used to decide if the LutMap can be simplified to a straight line. \end{enumerate} \subsection{Changes Introduced in V8.0.5} The following describes the most significant changes which occurred in the AST library between versions V8.0.4 and V8.0.5: \begin{enumerate} \item The SkyFrame class has a new attribute called SkyTol, which specifies the smallest significant distance within the SkyFrame. It is used to decide if the Mapping between two SkyFrames can be considered a unit transformation. The default value is 0.001 arc-seconds. \item A bug has been fixed in the FitsChan class that prevented illegal characters within FITS keyword names (i.e. characters not allowed by the FITS standard) being detected. This bug could under some circumstances cause a subsequent segmentation violation to occur. \item A ``BadKeyName'' warning is now issued by the FitsChan class if a FITS keyword name is encountered that contains any illegal characters. See attribute ``Warnings'' and c+ function ``astWarnings''. c- f+ routine ``AST\_WARNINGS''. f- \end{enumerate} \subsection{Changes Introduced in V8.1.0} The following describes the most significant changes which occurred in the AST library between versions V8.0.5 and V8.1.0: \begin{enumerate} \item The configure script has a new option ``--without-fortran'' that allows AST to be built in situations where no Fortran compiler is available. The resulting library has no Fortran interface and so cannot be used within Fortran applications. Also, the link scripts do not attempt to include the fortran runtime libraries. \end{enumerate} \subsection{\xlabel{changes}\xlabel{list_of_most_recent_changes}Changes Introduced in V8.2} The following describes the most significant changes which occurred in the AST library between versions V8.1.0 and V8.2.0: \begin{enumerate} \item A new class of Mapping called UnitNormMap has been added that converts a vector to a unit vector relative to a specified centre, plus length. A UnitNormMap has N inputs and N+1 outputs.The lower N output coordinates represent a unit vector parallel to the supplied input vector, and the (N+1)'th output coordinate is the length of the input vector. \item The restriction that Mappings are immutable has been extended to all Mapping classes. This means that attributes representing parameters of a Mapping's forward or inverse transformation cannot be changed after the Mapping has been created. In order to minimise the risk to existing software, this rule does not apply to Mappings that have not yet been included in other objects such as CmpMaps or FrameSets, or which have not yet been cloned. In other words, an error is reported if an attempt is made to change the nature of a Mapping's transformation, but only if the reference count of the Mapping is greater than one. The Mapping classes affected include: GrismMap, LutMap, PcdMap, SphMap, WcsMap and ZoomMap. \end{enumerate} \subsection{Changes Introduced in V8.3} The following describes the most significant changes which occurred in the AST library between versions V8.2.0 and V8.3.0: \begin{enumerate} c+ \item A new method called astAxNorm c- f+ \item A new method called AST\_AXNORM f- has been added to the Frame class that normalises an array of axis values. When used with SkyFrames, it allows longitude values to be normalised into the shortest range. f+ \item A bug has been fixed in the Fortran include file AST\_PAR that caused constants related to $\pi$ to be defined as single rather than double precision. f- \item A bug has been fixed in the astGetRegionBounds method that could cause the wrong bounds to be returned for regions spanning a longitude = zero singularity. \end{enumerate} \subsection{Changes Introduced in V8.4} The following describes the most significant changes which occurred in the AST library between versions V8.3.0 and V8.4.0: \begin{enumerate} \item The PAL library files included in the AST distribution have been updated to PAL version 0.9.7. \item Multiple identical NormMaps in series will now be simplified to a single NormMap. \item A NormMap that encapsulates a basic Frame will now be simplified to a UnitMap. f+ \item The AST\_TIMEADD f- c+ \item The astTimeAdd c- method of the TimeMap class now include an extra argument that gives the number of values supplied in the arguments array. Note, any existing code that uses this method will need to be changed. f+ \item The AST\_SLAADD f- c+ \item The astSlaAdd c- method of the SlaMap class now include an extra argument that gives the number of values supplied in the arguments array. Note, any existing code that uses this method will need to be changed. f+ \item The AST\_SPECADD f- c+ \item The astSpecAdd c- method of the SpecMap class now include an extra argument that gives the number of values supplied in the arguments array. Note, any existing code that uses this method will need to be changed. \item Multiple identical NormMaps in series will now be simplified to a single NormMap. \item A NormMap that encapsulates a basic Frame will now be simplified to a UnitMap. \item If the c+ astMapRegion c- f+ AST\_MAPREGION f- method is used to map a Region into a new Frame that has fewer axes than the original Region, and if the inverse transformation of the supplied Mapping does not specify a value for the missing axes, then those axes are removed entirely from the Region. Previously they were retained, but automatically supplied with bad values. This affects the number of mesh points per axes for such Regions, and so affects the accuracy of overlap determination. \end{enumerate} \subsection{Changes Introduced in V8.5} The following describes the most significant changes which occurred in the AST library between versions V8.4.0 and V8.5.1: \begin{enumerate} \item - A new class of Mapping called ChebyMap has been added. This is a Mapping that implements Chebyshev polynomial transformations. \item A bug has been fixed in the PolyMap class that caused incorrect values to be returned for the TranForward and TranInverse attributes if the PolyMap has been inverted. \item The KeyMap class has a new method called c+ astMapGetC c- f+ AST\_MAPGETC f- which returns a named entry as a single string. If the entry is a vector the returned string is a comma-separated list of its elements, enclosed in parentheses. \item If the c+ function that delivers error messages to the user (astPutErr) c- f+ routine that delivers error messages to the user (AST\_PUTERR) f- is re-implemented, the new version can now be registered at run-time using the new c+ astSetPutErr function. c- f+ AST\_SETPUTERR routine. f- Previously, the new version needed to be linked into the application at build time. \item The Frame class now has a new attribute caled DTAI, which can be used to specify the number of leap seconds at the moment represented by the Frame's Epoch attribute. By default, the internal look-up table of leap seconds contained within AST is used. The DTAI attribute allows old versions of AST, which may not include the most recent leap seconds, to be used with new data. \item The TimeMap class has been changed so that some conversions now require a ``Dtai'' value (\emph{i.e.} the number of leap seconds) to be supplied by the caller. If AST\_\_BAD is supplied for ``Dtai'', the internal look-up table of leap seconds contained withn AST will be used. The conversions affected are those between TAI and UTC, and those between TT and TDB. \end{enumerate} \subsection{\xlabel{changes}\xlabel{list_of_most_recent_changes}Changes Introduced in V8.6} The following describes the most significant changes which have occurred in the AST library between versions V8.5.1 and V8.6.2 (the current version): \begin{enumerate} \item The behaviour of the astLinearApprox method of the Mapping class has been changed in cases where the Mapping being approximated generates bad (AST\_\_BAD) values for one or more of its outputs. Previously, any such Mapping would be deemed non-linear and no fit would be returned. Now, a fit is returned, provided the other outputs of the Mapping are linear, but the fit contains AST\_\_BAD values for the coefficients describing the bad Mapping output. \item The astWrite method of the FitsChan class can now create FITS-WCS headers that include keyords describing focal plane distortion using the conventions of the Spitzer SIP scheme. This is however only possible if the SipOK attribute of the FitsChan is set to a non-zero value (which is the default), and the FrameSet being written out contains an appropriate PolyMap that conforms to the requirements of the SIP convention. \item A new function call astCreatedAt is now available that returns the function name, file path and line number at which an AST object was first created. Note, there is no Fortran equivalent to this new C function. \item The number of digits used to format floating point values has been increased in order to avoid loss of precision when converting from binary to string and back to binary. This could cause very small changes in numerical values returned by AST functions. \item If a FrameSet is supplied as the ``map'' argument to astAddFrame, it now extracts and stores the base->current Mapping from the supplied FrameSet. Previously, the entire FrameSet was stored as the Mapping. \end{enumerate} Programs which are statically linked will need to be re-linked in order to take advantage of these new facilities. \end{document}