\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<VERSION_NUMBER>}
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 () {
   <normal C declarations and statements>

/* Enclose the parts which use AST between the astBegin and astEnd macros. */
   astBegin;
   <C statements which use AST>
   astEnd;

   <maybe more C statements>
}
\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
      <maybe other declarations>

*  Initialise the status to zero.
      STATUS = 0
      <maybe some Fortran statements>

*  Enclose the parts which use AST between AST_BEGIN and AST_END calls.
      CALL AST_BEGIN( STATUS )
      <Fortran statements which use AST>
      CALL AST_END( STATUS )

      <maybe more Fortran statements>
      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 <string.h>

...

if ( !astOK ) {
   <an error occurred (a message will have been issued)>
} else if ( wcsinfo == AST__NULL ) {
   <there was no WCS information present>
} else if ( strcmp( astGetC( wcsinfo, "Class" ), "FrameSet" ) ) {
   <something unexpected was read (i.e. not a FrameSet)>
} else {
   <WCS information was read OK>
}
\end{terminalv}
\end{small}
c-
f+
\small
\begin{terminalv}
      IF ( STATUS .NE. 0 ) THEN
         <an error occurred (a message will have been issued)>
      ELSE IF ( WCSINFO .EQ. AST__NULL ) THEN
         <there was no WCS information present>
      ELSE IF ( AST_GETC( WCSINFO, 'Class', STATUS ) .NE. 'FrameSet' ) THEN
         <something unexpected was read (i.e. not a FrameSet)>
      ELSE
         <WCS information was read OK>
      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 <stdio.h>
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 <stdio.h>
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 ) {
      <handle the error, or report your own message here>
      break;

   } else {
      <coordinates were read OK>
   }
}
\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
         <handle the error, or report your own message here>
         GO TO 99

      ELSE
         <coordinates were read OK>
      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 $<i>$):

\begin{description}
\item[CRPIX<i>]\mbox{}\\
hold the pixel coordinates at a reference point
\item[CRVAL<i>]\mbox{}\\
hold the corresponding WCS coordinates at the reference point
\item[CTYPE<i>]\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<i>\_<j>]\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. */
<details not shown - see below>

/* 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.
      <details not shown - see below>

*  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 <stdio.h>
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". */
<this stage depends on your data system, so is not shown>

/* 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 );

   <maybe some more AST graphics here>

/* 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.
      <this stage depends on your data system, so is not shown>

*  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 )

         <maybe some more AST graphics here>

*  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 <stdio.h>
#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 <float.h>

...

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" ) ) {
   <the Report attribute is set>
}
\end{terminalv}
\normalsize
c-
f+
\small
\begin{terminalv}
      IF ( AST_TEST( ZOOMMAP, 'Report', STATUS ) ) THEN
         <the Report attribute is set>
      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 ) {
   <an error has occurred>
}
\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
         <an error has occurred>
      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 ) {
   <an error has occurred>
}
\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
         <an error has occurred>
      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 ) {
   <an error has occurred>
}
\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
         <an error has occurred>
      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
         <various types of 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)
(<bad>, 2) --> (<bad>, 10)
(2, 4) --> (10, 20)
(3, 6) --> (15, 30)
(4, <bad>) --> (20, <bad>)
(5, 10) --> (25, 50)
(<bad>, <bad>) --> (<bad>, <bad>)
(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 <stdio.h>
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 ) {
      <error in input data>
   } else {
      <value read correctly>
   }
}
\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
            <error in input data>
         ELSE
            <value read correctly>
         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 ) {
      <error in input data>
   } else {
      <values read correctly>
   }
}
\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
            <error in input data>
         ELSE
            <values read correctly>
         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) --> (<bad>, 2, 3)
   (6, 4) --> (<bad>, 4, 6)
   (9, 6) --> (<bad>, 6, 9)
   (12, 8) --> (<bad>, 8, 12)
   (15, 10) --> (<bad>, 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 ) {
   <conversion is not possible>
} else {
   <conversion is possible>
}
\end{terminalv}
\normalsize
c-
f+
\small
\begin{terminalv}
      INTEGER CVT

      ...

      CVT = AST_CONVERT( SKYFRAME1, SKYFRAME2, ' ', STATUS )
      IF ( CVT .EQ. AST__NULL ) THEN
         <conversion is not possible>
      ELSE
         <conversion is possible>
      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 ) {
   <conversion is not possible>
} else {
   <conversion is possible>
}
\end{terminalv}
\normalsize
c-
f+
\small
\begin{terminalv}
      INTEGER CVT

      ...

      CVT = AST_CONVERT( SPECFRAME1, SPECFRAME2, ' ', STATUS )
      IF ( CVT .EQ. AST__NULL ) THEN
         <conversion is not possible>
      ELSE
         <conversion is possible>
      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 ) {
   <no conversion was possible>
} else {
   <conversion was possible>
}
\end{terminalv}
\normalsize
c-
f+
\small
\begin{terminalv}
      CVT = AST_CONVERT( FRAMESETA, FRAMESETB, 'SKY,PIXEL,GRID', STATUS )
      IF ( CVT .EQ. AST__NULL ) THEN
         <no conversion was possible>
      ELSE
         <conversion was possible>
      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 <file
\end{terminalv}
\normalsize

Alternatively, we could have assigned a value to the SinkFile attribute
before invoking
c+
astRead.
c-
f+
AST\_READ.
f-

\subsection{Saving and Restoring Multiple Objects}

I/O operations performed on a basic Channel are sequential. This
means that if you write more than one Object to a Channel,
each new Object's textual description is simply appended to the
previous one. You can store any number of Objects in this way,
subject only to the storage space you have available.

c+
After you read an Object back from a basic Channel, the
Channel is ``positioned'' at the end of that Object's
textual description. If you then perform another read, you will
read the next Object's textual description and therefore
retrieve the next Object.  This process may be repeated to read
each Object in turn. When there are no more Objects to be
read, astRead will return the value AST\_\_NULL to indicate an
\emph{end-of-file}.
c-
f+
After you read an Object back from a basic Channel, the
Channel is ``positioned'' at the end of that Object's
textual description. If you then perform another read, you will
read the next Object's textual description and therefore
retrieve the next Object.  This process may be repeated to read
each Object in turn. When there are no more Objects to be
read, AST\_READ will return the value AST\_\_NULL to indicate an
\emph{end-of-file}.
f-

\subsection{\label{ss:validatinginput}Validating Input}

c+
The pointer returned by astRead (\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 astIsA$<$Class$>$ 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 <string.h>

...

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" ) ) {
   <the Calibration Object has been read>
} else {
   <some other Object has been read>
}
\end{terminalv}
\normalsize
c-
f+
\small
\begin{terminalv}
      OBJECT = AST_READ( CHANNEL, STATUS )
      IF ( AST_GETC( OBJECT, 'ID', STATUS ) .EQ. 'Calibration' ) THEN
         <the Calibration Object has been read>
      ELSE
         <some other Object has been read>
      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 <stdio.h>
#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 );
   <access the current card>
}
\end{terminalv}
\normalsize
c-
f+
\small
\begin{terminalv}
      DO 1 ICARD = 1, AST_GETI( FITSCHAN, 'Ncard', STATUS )
         CALL AST_SETI( FITSCHAN, 'Card', ICARD, STATUS )
         <access the current card>
 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 <stdio.h>
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 ) {
   <process the card's contents>
}
\end{terminalv}
\normalsize
c-
f+
\small
\begin{terminalv}
 4    CONTINUE
      IF ( AST_FINDFITS( FITSCHAN, 'CD%6d', CARD, .TRUE., STATUS ) ) THEN
         <process the card's contents>
         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" ) ) {
   <the Object is a FrameSet, so use it>
} else {
   <something unexpected was read>
}
\end{terminalv}
\normalsize
c-
f+
\small
\begin{terminalv}
      IF ( AST_GETC( WCSINFO, 'Class', STATUS ) .EQ. 'FrameSet' ) THEN
         <the Object is a FrameSet, so use it>
      ELSE
         <something unexpected was read>
      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 <stdio.h>
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}
 <Frame xmlns="http://www.starlink.ac.uk/ast/xml/"
        desc="Coordinate system description">
    <_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"/>
    <Axis label="Ax1" desc="Coordinate axis">
       <!--Axis number 1-->
       <_attribute name="Unit" quoted="true" value="cm" desc="Axis units"/>
    </Axis>
 </Frame>
\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}
 <Frame xmlns="http://www.starlink.ac.uk/ast/xml/">
    <_attribute name="Naxes" value="1"/>
    <_attribute name="Domain" quoted="true" value="SCREEN"/>
    <Axis label="Ax1">
       <_attribute name="Unit" quoted="true" value="cm"/>
    </Axis>
 </Frame>
\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 <math.h>

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 <stdlib.h>. 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<X>)
c-
f+
(AST\_SETFITS<X>)
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<X>/astRebin<X>) has been added to
the Mapping class. These are flux-conserving alternatives to the existing
AST\_RESAMPLE<X>/astResample<X> 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<X>/astResample<X>
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<X> 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<X> 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<X>
c-
f+
AST\_MAPGETELEM<X>
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<X> 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<X>, etc.)
c-
f+
(AST\_MAPGET0<X>, 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 ``<bad>'' 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 ``<bad>'' 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<X>
c-
f+
AST\_MAPPUTELEM<X>
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<X>
c-
f+
AST\_MAPPUTELEM<X>
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<X>
c-
f+
AST\_GETFITS<X>
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
``$<major>.<minor>-<release>$'' to ``$<major>.<minor>.<release>$''.

\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<X> 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<X>.
c-
f+
AST\_REBINSEQ<X>.
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<X> 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<X> 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<X> 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}
