4 An AST Object Primer

 4.1 AST Objects
 4.2 Object Creation and Pointers
 4.3 The Object Hierarchy
 4.4 Displaying Objects
 4.5 Getting Attribute Values
 4.6 Setting Attribute Values
 4.7 Testing, Clearing and Defaulting Attributes
 4.8 Transforming Coordinates
 4.9 Managing Object Pointers
 4.10 AST Pointer Contexts—Begin and End
 4.11 Exporting, Importing and Exempting AST Pointers
 4.12 Copying Objects
 4.13 Error Detection

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 via fairly conventional Fortran and C interfaces, so some things have to be done a little differently.

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.

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.

4.1 AST Objects

An AST Object is an entity which is used to store information and Objects come in various kinds, called 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 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.

4.2 Object Creation and Pointers

Let us first consider how to create a ZoomMap. This is done very simply as follows:

        INCLUDE ’AST_PAR’
        INTEGER STATUS, ZOOMMAP
  
        STATUS = 0
  
        ...
  
        ZOOMMAP = AST_ZOOMMAP( 2, 5.0D0, ’ ’, STATUS )

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.

These functions are called constructor functions, or simply constructors (you can find an individual description of all AST functions in Appendix B) 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 (i.e. we are going to work in a 2-dimensional 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 (§4.6 and §4.13).

The integer value returned by the constructor is termed an Object pointer or, in this case, a 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.

4.3 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:

  Object
     Mapping
        ZoomMap

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

  Object
     Mapping
        Frame
           FrameSet
              Plot
        UnitMap
        ZoomMap
     Channel
        FitsChan
        XmlChan

Notice that there are several different types of Mapping available (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 §21). As you can see, a Plot is a FrameSet… and a Frame… and a Mapping… 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 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 class hierarchy, as this is called, for the AST library in Appendix A and you may need to refer to it occasionally until you are familiar with the classes you need to use.

4.4 Displaying Objects

Let us now return to the ZoomMap that we created earlier (§4.2) and examine what it’s made of. There is a routine for doing this, called AST_SHOW, which is provided mainly for looking at Objects while you are debugging programs.

If you consult the description of AST_SHOW in Appendix B, 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 (§4.3), 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:

        CALL AST_SHOW( ZOOMMAP, STATUS )

The output from this will appear on the standard output stream and should look like the following:

  Begin ZoomMap
     Nin = 2
  IsA Mapping
     Zoom = 5
  End ZoomMap

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 (§4.2). 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 parent class (Mapping). The “Zoom” value, however, is specific to a ZoomMap and isn’t shared by other kinds of Mappings.

4.5 Getting Attribute Values

We saw above (§4.4) 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 attributes. A description of all the attributes used by the AST library can be found in Appendix 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:

        INTEGER NIN
  
        ...
  
        NIN = AST_GETI( ZOOMMAP, ’Nin’, STATUS )

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.

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:

        DOUBLE PRECISION ZOOM
  
        ...
  
        ZOOM = AST_GETD( ZOOMMAP, ’Zoom’, STATUS )

However, we could equally well have read the Nin value as double precision, or the Zoom value as an integer, or whatever we wanted.

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

4.6 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:

        CALL AST_SETD( ZOOMMAP, ’Zoom’, 99.6D0, STATUS )

which sets the value to 99.6 (double precision). As when getting an attribute value (§4.5), 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:

        CALL AST_SETI( ZOOMMAP, ’Zoom’, 99, STATUS )

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.

An alternative way of setting attribute values for Objects is to use the AST_SET routine (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:

        CALL AST_SET( ZOOMMAP, ’Zoom=99.6, Report=1’, STATUS )

would set values for both the Zoom attribute and the Report attribute (about which more shortly—§4.8). 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:

        CALL AST_SET( SKYFRAME, ’SkyRef="12:13:32,-23:12:44"’, STATUS )

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:

        ZOOMMAP = AST_ZOOMMAP( 2, 5.0D0, ’Report=1’, STATUS )

and it would then start life with its Report attribute set to 1.

4.7 Testing, Clearing and Defaulting Attributes

You can use the AST_GETx family of routines (§4.5) 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.

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 un-set. Conversely, once an explicit value has been assigned to an attribute, it becomes set and will always return precisely that value, never a default.

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:

        IF ( AST_TEST( ZOOMMAP, ’Report’, STATUS ) ) THEN
           <the Report attribute is set>
        END IF

(as usual, remember to include the AST_PAR file to declare the function as LOGICAL, or make this declaration yourself).

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:

        CALL AST_CLEAR( ZOOMMAP, ’Report’, STATUS )

would ensure that the default value of the Report attribute is used subsequently.

4.8 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:

        CALL AST_SET( ZOOMMAP, ’Report=1’, STATUS )

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.

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:

        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 /

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 Appendix B, 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.

Fortunately, a ZoomMap is a Mapping (Appendix A), so we can use it with AST_TRAN2 to transform our coordinates, as follows:

        CALL AST_TRAN2( ZOOMMAP, 10, XIN, YIN, .TRUE., XOUT, YOUT, STATUS )

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 forward direction (from input to output).

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:

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

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.

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:

        CALL AST_TRAN2( ZOOMMAP, 10, XOUT, YOUT, .FALSE., XIN, YIN, STATUS )

The output would then look like:

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

This is termed the 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.

4.9 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.

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—§4.10) is to annul each Object pointer once you have finished with it, using AST_ANNUL. For example:

        CALL AST_ANNUL( ZOOMMAP, STATUS )

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.

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.

4.10 AST Pointer Contexts—Begin and End

The use of AST_ANNUL4.9) is not completely foolproof, however. Consider the following:

        CALL AST_SHOW( AST_ZOOMMAP( 2, 5.ODO, ’ ’, STATUS ), STATUS )

This creates a ZoomMap and displays it on standard output (§4.4). 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.

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:

        CALL AST_BEGIN( STATUS )
        CALL AST_SHOW( AST_ZOOMMAP( 2, 5.ODO, ’ ’, STATUS ), STATUS )
        CALL AST_END( STATUS )

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.

If necessary, calls to AST_BEGIN and AST_END may be nested, like IF…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.

4.11 Exporting, Importing and Exempting AST Pointers

The AST_EXPORT routine allows you to export particular pointers from one AST context (§4.10) to the next outer one, as follows:

        CALL AST_EXPORT( ZOOMMAP, STATUS )

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…AST_END block is basically to generate an Object pointer. Without this, there is no way of getting that pointer out.

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.

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:

        CALL AST_EXEMPT( ZOOMMAP, STATUS )

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.

4.12 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 via the common Object.

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, e.g.:

        INTEGER ZOOMMAP1, ZOOMMAP2
  
        ...
  
        ZOOMMAP2 = AST_COPY( ZOOMMAP1, STATUS )

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.

4.13 Error Detection

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.

To indicate that an error has occurred, each AST routine that can potentially fail has a final integer 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 (i.e. set to zero9 to indicate no error). If an error occurs, the STATUS argument is returned set to a different error value, which allows you to detect the error, as follows:

        STATUS = 0
  
        ...
  
        ZOOMMAP = AST_ZOOMMAP( 2, 5.0D0, ’Title=My ZoomMap’, STATUS )
        IF ( STATUS .NE. 0 ) THEN
           <an error has occurred>
        END IF

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.

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:

        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

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:

        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

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 Appendix B.

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.

9We 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.