### 14 Higher Level Operations on FrameSets

#### 14.1 Creating FrameSets with AST_CONVERT

Before considering the important subject of using FrameSets to convert between coordinate systems (§14.2), let us return briefly to reconsider the output generated by ??. We used this function earlier (§12), when converting between the coordinate systems represented by various kinds of ??, and indicated that it returns a ?? to represent the coordinate conversion it identifies. We are now in a position to examine the structure of this FrameSet.

Take our earlier example (§12.1) of converting between the celestial coordinate systems represented by two SkyFrames:

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 )

This will produce a pointer, CVT, to the FrameSet shown in Figure 15.

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.

As we noted earlier (§12.1), 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 ?? (§13.7), for example:

INTEGER MAPPING

...

MAPPING = AST_GETMAPPING( CVT, AST__BASE, AST__CURRENT, STATUS )

#### 14.2 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 (§14.3) will show, and is illustrated in Figure 16.

Recalling (§13.8) that a FrameSet will behave like its current ?? when necessary, conversion between two FrameSets is performed using ?? (§12.1), 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:

INTEGER FRAMESETA, FRAMESETB

...

CVT = AST_CONVERT( FRAMESETA, FRAMESETB, ’SKY’, STATUS )

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

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 (§7.12), but can go 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:

(1)
Not every possible pairing of Frames is legitimate. For example, you cannot convert directly between a basic Frame and a ?? which belong to different classes, so such pairings will be ignored.
(2)
In a similar way, you cannot convert directly between Frames with different Domain values (§7.12). 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.
(3)
The third argument of AST_CONVERT may then be used to specify explicitly which Domain value the paired Frames should have. You may also supply a comma-separated list of preferences here (see below).
(4)
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 the description of the AST_CONVERT function in Appendix B for details of the search order).24

In the example above we supplied the string “SKY” as the third argument of AST_CONVERT. This constitutes a request that a pair of Frames with the Domain value SKY (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 domain search path, as the third argument to AST_CONVERT, such as the following:

CVT = AST_CONVERT( FRAMESETA, FRAMESETB, ’SKY,PIXEL,GRID,’, STATUS )

Now, if the two FrameSets cannot be inter-related using the SKY domain, AST_CONVERT will attempt to use the PIXEL domain instead. If this 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.

If astConvert succeeds in identifying a conversion, it will return a pointer to a FrameSet (§14.1) 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.

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 ?? attribute for each FrameSet so that the Frame used in the pairing becomes its base Frame (§13.4).

Finally, note that AST_CONVERT may also be used to convert between a FrameSet and a Frame, or 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.

#### 14.3 Example—Registering Two Images

Consider two images which have been calibrated by attaching FrameSets to them, such that the base ?? of each ?? corresponds to the raw data grid coordinates of each image (the GRID domain of §7.13). 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.

The first step will probably involve making a copy of both FrameSets (using ??—§4.12), 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 (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:

CALL AST_INVERT( FRAMESETA, STATUS )
CALL AST_INVERT( FRAMESETB, STATUS )

To identify the required conversion, we now use ??, supplying a suitable domain search path with which we would like our two images to be registered:

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

The effects of this are:

(1)
AST_CONVERT first attempts to register the two images on the celestial sphere (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.
(2)
If this fails, AST_CONVERT next tries to match positions in the PIXEL domain (§7.12). 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.
(3)
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.
(4)
If all of the above fail, AST_CONVERT will return the value AST__NULL. Otherwise a pointer to a FrameSet will be returned.

The resulting CVT FrameSet may then be used directly (§12.1) to convert between positions in the data grid of the first image and corresponding positions in the data grid of the second image.

To determine which domain was used to achieve registration, we can use the fact that the ?? attribute of each FrameSet is set by AST_CONVERT to indicate which intermediate Frames were used. We can therefore simply invert either FrameSet (to make its base Frame become the current one) and then enquire the ?? value:

CHARACTER * ( 20 ) DOMAIN

...

CALL AST_INVERT( FRAMESETA, STATUS )
DOMAIN = AST_GETC( FRAMESETA, ’Domain’, STATUS )

If conversion was successful, the result will be one of the strings “SKY”, “PIXEL” or “GRID”.

#### 14.4 Re-Defining a FrameSet Coordinate System

As discussed earlier (§13.4), an important application of a ?? 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 ?? 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:

(1)
Create a ?? which represents the relationship between the original data grid coordinate system and the new one.
(2)
Obtain a Frame to represent the new data grid coordinate system (we could re-use the original base Frame here, using ?? to obtain a pointer to it).
(3)
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.25
(4)
Remove the original base Frame (representing the old data grid coordinate system).
(5)
Make the new Frame the base Frame and restore the original current Frame.

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

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 (§14.5).

#### 14.5 Example—Binning an Image

As an example of using ??, consider a case where the pixels of a 2-dimensional image have been binned 2$×$2, so as to reduce the image size by a factor of two in each dimension. We must now modify the associated ?? to reflect this change to the image. Much the same process would be needed for any other geometrical change the image might undergo.

We first set up a ?? (a ?? in this case) which relates the data grid coordinates in the original image to those in the new one:

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 )

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). ?? then creates a WinMap which performs the required transformation. We do not need to know the size of the image.

We can then pass this WinMap to AST_REMAPFRAME. This modifies the relationship between our FrameSet’s base ?? 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:

INTEGER FRAMESET

...

CALL AST_REMAPFRAME( FRAMESET, AST__BASE, WINMAP, STATUS )

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.

#### 14.6 Maintaining the Integrity of FrameSets

When constructing a ??, 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 ??. 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!

Consider, for example, a FrameSet whose current ?? is a ??. We can set a new value for this SkyFrame’s ?? attribute simply by using ?? on the FrameSet, as follows:

CALL AST_SET( FRAMESET, ’Equinox=J2010’, STATUS )

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.

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 ?? had been used—§14.4) in response to the use of AST_SET. One way of illustrating this process is as follows:

INTEGER SKYFRAME

...

SKYFRAME = AST_SKYFRAME( ’ ’, STATUS )
FRAMESET = AST_FRAMESET( SKYFRAME, STATUS )
CALL AST_ADDFRAME( FRAMESET, 1, AST_UNITMAP( 2, ’ ’, STATUS )
:                   SKYFRAME, STATUS )

This constructs a trivial FrameSet whose base and current Frames are both the same SkyFrame connected by a ??. 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:

CALL AST_SET( FRAMESET, ’System=Ecliptic, Equinox=J2010’, STATUS )

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:

CALL AST_INVERT( FRAMESET )
CALL AST_SET( FRAMESET, ’System=Galactic’, STATUS )
CALL AST_INVERT( FRAMESET )

The FrameSet would then convert between galactic and ecliptic coordinates.

Note that AST_SET is not the only function which has this effect: ?? behaves similarly, as also does ?? (§7.9). 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:

SKYFRAME = AST_GETFRAME( FRAMESET, AST__CURRENT, STATUS )
CALL AST_SET( SKYFRAME, ’Equinox=J2010’, STATUS )
CALL AST_ANNUL( SKYFRAME, STATUS )

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.

#### 14.7 Merging FrameSets

As well as adding individual Frames to a ?? (§13.3), 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 18).

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 (§13.3). 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 18 as follows:

INTEGER MAPPING

...

CALL AST_ADDFRAME( FRAMESETA, 1, MAPPING, FRAMESETB, STATUS )

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.

Note that while this process modifies the first FrameSet (FRAMESETA), it leaves the original contents of the one being added (FRAMESETB) unchanged.

24If 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 ??.

25This is because any transformation to or from this new Frame must go via the base Frame representing the original data grid coordinate system, which we assume was correctly related to all the other Frames present.