### 19 AXIS COMPONENTS

#### 19.1 Overview of an NDF’s Axis Components

Information about an NDF’s axis coordinate system is stored in its axis components, which are conveniently categorised as follows:

 Axis character components: LABEL — Axis labels UNITS — Axis units Axis array components: CENTRE — Pixel centre coordinates WIDTH — Pixel width values VARIANCE — Variance estimates for pixel positions

As with the main components of an NDF, the names of these axis components are significant,18 since they are used by the NDF_ routines to identify the component(s) to which certain operations should be applied. Axis component names are specified in the same way as those of the main components of an NDF, including the use of abbreviations, mixed case and comma-separated lists where appropriate (see §5.1 for details).

Access to an axis component must also specify the number of the NDF axis to be used. This is normally an integer lying between 1 and the number of NDF dimensions, but many routines will also accept a value of zero, indicating that an operation is to be applied to all of an NDF’s axes. This additional item of information means that a separate set of routines must be provided for accessing axis components. Nevertheless, many of the principles described in earlier sections for accessing other NDF components are also applicable here, so the descriptions given below are relatively brief. References to more complete descriptions are given where appropriate.

The following describes the purpose and interpretation of each axis component in slightly more detail.

Axis Character Components:

LABEL –
This is a character string, whose value is intended for general use for such things as labelling the axes of graphs or as a heading for columns in tabulated output; e.g. ‘Scanner X offset’. There is a separate axis label value for each NDF dimension.
UNITS –
This is a character string, whose value describes the physical units of the quantity measured along an NDF’s axis; e.g. ‘micron’. There is a separate axis units value for each NDF dimension.

Axis Array Components:

CENTRE –
This is a 1-dimensional array which holds the coordinates of the pixel centres as described in §18.3. The values in this array should either increase or decrease monotonically with position in the array. There is a separate 1-dimensional axis centre array for each dimension of an NDF, whose size matches the size of the corresponding NDF dimension.
WIDTH –
This is a 1-dimensional array which holds a set of non-negative width values for the NDF’s pixels as described in §18.3. There is a separate 1-dimensional axis width array for each dimension of an NDF, whose size matches the size of the corresponding NDF dimension. These width values should be such that no point can lie inside more than two NDF pixels simultaneously (i.e. although pixels are allowed to overlap with their neighbours, they may not overlap with more distant pixels).
VARIANCE –
This is a 1-dimensional array which holds a set of non-negative variance estimates representing any statistical uncertainty in the value of the corresponding pixel centre coordinate, as described in §18.3. There is a separate 1-dimensional axis variance array for each dimension of an NDF, whose size matches the size of the corresponding NDF dimension.

#### 19.2 Axis Component States

Like all NDF components, each axis component has a logical state attribute associated with it which indicates whether or not it has a previously-assigned value. The state of an axis component may be determined by using the routine NDF_ASTAT, specifying the component name and the number of the axis about which information is required, as follows:

INTEGER IAXIS
LOGICAL STATE

...

CALL NDF_ASTAT( INDF, ’Width’, IAXIS, STATE, STATUS )

In this example, a .TRUE. result would be returned via the logical STATE argument if values had previously been assigned to the width array of the axis identified by the IAXIS argument.

Unlike other NDF components, no error will result from attempting to read the value of an axis component while it is in an undefined state. This is because the NDF_ system will always supply a default value if necessary. Thus, an axis component’s state merely serves to indicate whether a pre-assigned value will be used, as opposed to an internally-generated default.

NDF_ASTAT will also accept a list of axis component names and will return the logical “AND” of the results for all the specified components. An IAXIS value of zero may also be supplied to indicate that all the NDF’s axes should be considered at the same time. Thus, the single call:

CALL NDF_ASTAT( INDF, ’Label,Units’, 0, STATE, STATUS )

could be used to determine whether all the NDF’s axes had previously-assigned values for both their label and units components.

#### 19.3 Restrictions on Axis Component States

In general, the logical state attribute of each axis component is independent and may be manipulated freely. However, there is one notable and important exception to this:

If any axis component is to be in a defined state, then the centre arrays for all the NDF’s axes must also be defined

Thus, all an NDF’s axis centre arrays behave as a single unit, and it is a pre-requisite that all of these arrays should be in a defined state before any other axis component may be assigned a value.

Of course, it would be very inconvenient if values had to be explicitly generated and assigned to all the axis centre arrays before any other axis values could be defined, so the the NDF_ system has an implicit mechanism for assigning default values to the axis centre arrays whenever they are required (i.e. whenever any axis component is assigned a value but the axis centre arrays are still undefined). A routine is also provided to perform this task explicitly if required, and is described in the next section.

If an NDF’s axis centre arrays are in a defined state, then the axis coordinate system of the NDF as a whole is regarded as being defined. Otherwise (i.e. if no axis components are defined at all), then the axis coordinate system is undefined. The routine NDF_STATE can be used to test whether or not an axis coordinate system is defined by using a component name of ‘Axis’, thus:

CALL NDF_STATE( INDF, ’Axis’, STATE, STATUS )

A .TRUE. value will be returned via the logical STATE argument if the axis coordinate system is defined.

#### 19.4 Defining a Default Axis Coordinate System

The routine NDF_ACRE is provided to assign default values to all of an NDF’s axis arrays, thereby defining a default axis coordinate system, as follows:

CALL NDF_ACRE( INDF, STATUS )

If the axis coordinate system is already defined, then all the axis centre arrays will already have values, so this routine will return without action. However, if this coordinate system (and hence each axis centre array) is undefined, then NDF_ACRE will assign default values to all the centre arrays, effectively defining a default axis coordinate system which is equal to the NDF’s pixel coordinate system (§18.1).

After this operation, the values available from any of the NDF’s axis components will be unchanged. This is because the NDF_ system would provide these same axis centre values as defaults in any case. Nevertheless, the definition of a default axis coordinate system is a significant step because it effectively takes a “copy” of the current pixel coordinate system. Any operation which subsequently changes the NDF’s pixel indices (e.g. the application of pixel-index shifts – see §21.2) cannot then affect the pixel centre values, whereas previously it would have done.

#### 19.5 Resetting Axis Components

The axis coordinate system of an NDF may be reset by specifying the component name ‘Axis’ in a call to NDF_RESET, as follows:

CALL NDF_RESET( INDF, ’Axis’, STATUS )

This will cause all the NDF’s axis components to become undefined, so that subsequent attempts to read values from any of them will return default values appropriate to the NDF’s pixel coordinate system. A subsequent enquiry about the state of the ‘Axis’ coordinate system using NDF_STATE would return a value of .FALSE..

The routine NDF_AREST is also provided for resetting individual axis components. Thus, a particular axis variance array could be reset as follows:

CALL NDF_AREST( INDF, ’Variance’, IAXIS, STATUS )

and any subsequent attempt to read from it would result in the default values being returned.

A list of component names may also be supplied to NDF_AREST, along with an optional IAXIS value of zero to indicate that the resetting operation should apply to all the NDF’s axes at once. Thus, the following call:

CALL NDF_AREST( INDF, ’Wid,Var’, 0, STATUS )

would reset all of an NDF’s axis width and variance arrays.

Note that a component name of ‘Centre’ may not be supplied to NDF_AREST because all the axis centre arrays behave as a single unit and cannot be independently reset. The only way to remove all axis centre information from an NDF is to reset the entire axis coordinate system via a call to NDF_RESET, as above.

#### 19.6 Accessing Axis Character Components

The axis label and units components of an NDF are both accessed via the same set of routines which behave in a similar manner to those for accessing an NDF’s main character components (§6).

The value of either of these axis components may be obtained by calling the routine NDF_ACGET, as follows:

CHARACTER * ( 80 ) VALUE

...

VALUE = ’Default label’
CALL NDF_ACGET( INDF, ’Label’, IAXIS, VALUE, STATUS )

This will return the value of the specified component, if it is defined. If its value is not defined and a non-blank default value has been set for the VALUE argument beforehand (as here), then this default value will be returned unchanged. However, if a blank VALUE string is provided, then the routine will generate its own default if necessary, returning either the value ‘Axis n’ for the axis label component (where n is the axis number) or ‘pixel’ for the axis units.

If an axis character component value is to be used in constructing a message, then it may be assigned directly to an MSG_ message token by means of the NDF_ACMSG routine. Thus, a message showing the label and units values for a particular NDF axis could be generated as follows:

CALL NDF_ACMSG( ’LABEL’, INDF, ’Label’, IAXIS, STATUS )
CALL NDF_ACMSG( ’UNITS’, INDF, ’Units’, IAXIS, STATUS )
CALL MSG_OUT( ’MESSAGE’, ’^LABEL (^UNITS)’, STATUS )

Here, ‘LABEL’ and ‘UNITS’ are the names of message tokens (see SUN/104).

New values may be assigned to axis character components by using the routine NDF_ACPUT. For instance:

CALL NDF_ACPUT( ’Wavelength’, INDF, ’Lab’, 1, STATUS )
CALL NDF_ACPUT( ’nm’, INDF, ’Unit’, 1, STATUS )

would assign the label value ‘Wavelength’ and the units value ‘nm’ to axis 1 of an NDF. Note that the entire character string will be assigned (including trailing blanks if present) and the length of the component will be adjusted to match the new value. A value of zero for the fourth (IAXIS) argument would cause the value to be assigned to all of the NDF’s axes.

After a successful call to NDF_ACPUT, the axis character component’s state becomes defined. So, also, does the NDF’s axis coordinate system—this means that default values will be assigned to all the NDF’s axis centre arrays if these were not previously defined. The effect of this is exactly the same as if the routine NDF_ACRE19.4) had been called immediately before the call to NDF_ACPUT.

The length of an axis character component (i.e. the number of characters it contains) is determined by the last assignment made to it, (e.g. by NDF_ACPUT) and may be obtained using the routine NDF_ACLEN. For instance:

INTEGER LENGTH

...

CALL NDF_ACLEN( INDF, ’Units’, IAXIS, LENGTH, STATUS )

will return the number of characters in the specified axis units component via the LENGTH argument. If the component is in an undefined state, then NDF_ACLEN will return the number of characters in the default value which would be returned by NDF_ACGET under these circumstances (see §19.6).

#### 19.7 Mapping Axis Arrays for Reading

Access to axis array components takes place in much the same way as access to the main array components of an NDF. It also depends on the concept of mapping, as described in §8.

The routine NDF_AMAP provides mapped access to an axis array. Thus, to read values from an NDF’s axis centre array, the following call might be used:

INTEGER PNTR( 1 ), EL

...

CALL NDF_AMAP( INDF, ’Centre’, IAXIS, ’_REAL’, ’READ’, PNTR, EL, STATUS )

Here, a numeric type of ‘_REAL’ has been specified to indicate that an array of single-precision values is required and the mapping mode of ‘READ’ indicates that values are to be read, but not modified. The routine returns an integer pointer to the mapped values via its PNTR argument and a count of the number of elements mapped via its EL argument (PNTR is actually a 1-dimensional array, so the pointer value in this example will be returned in its first element). The value returned for EL will be equal to the size of the NDF dimension being accessed.19 The mapped values may be accessed in the normal way by passing them to a subroutine using the %VAL facility (see §8.2).

If the axis array being accessed is in an undefined state, then a set of default values will be returned (see §18.5). Note that the mapping mode initialisation options available when mapping the main NDF array components (§8.6) cannot be applied to axis arrays.

NDF_AMAP will also accept a list of axis component names and will map all of them in the same way (i.e. with the same type and mapping mode). Pointers to each mapped array will be returned via the PNTR argument, which must have sufficient elements to accommodate the returned values. The following example shows how access to all the axis arrays for a particular NDF dimension could be obtained using this facility, and then passed to another routine for processing:

INTEGER PNTR( 3 ), EL

...

CALL NDF_AMAP( INDF, ’Cent,Width,Var’, IAXIS, ’_DOUBLE’, ’READ’, PNTR, EL,
:               STATUS )
CALL DOAXIS( EL, %VAL( PNTR( 1 ) ), %VAL( PNTR( 2 ) ), %VAL( PNTR( 3 ) ),
:             STATUS )

Note that it is not possible to map axis arrays for all the axes of an NDF in a single call to NDF_AMAP, because each would require a different value of EL to be returned. An IAXIS value of zero is therefore not permitted when calling NDF_AMAP.

#### 19.8 Unmapping Axis Arrays

When access to an axis array is complete, it should be unmapped in the usual way (see §8.2). There are a number of methods by which this can be done. Most simply, the cleaning-up action of NDF_END3.4) may be relied upon to annul an NDF identifier and to unmap any mapped arrays associated with it as part of this process (§8.3). This will correctly deal with any axis arrays which may be mapped.

Alternatively, the routine NDF_UNMAP may be used by specifying a component name of ‘Axis’, as follows:

CALL NDF_UNMAP( INDF, ’Axis’, STATUS )

This will unmap all axis arrays which are mapped. (A “wild-card” component name of ‘$\ast$’ will also affect axis arrays in the same way.)

If axis arrays are to be unmapped individually, then the routine NDF_AUNMP should be used. Thus,

CALL NDF_AUNMP( INDF, ’Width’, IAXIS, STATUS )

might be used to unmap a particular axis width array. As usual, a list of axis component names may be supplied to NDF_AUNMP and a “wild-card” axis component name of ‘$\ast$’ can also be used. In addition, an IAXIS value of zero may be specified to indicate that the unmapping operation is to be applied to all the NDF’s axes. Thus,

CALL NDF_AUNMP( INDF, ’*’, 1, STATUS )

would unmap all the axis arrays for axis 1 of an NDF, while:

CALL NDF_AUNMP( INDF, ’Centre’, 0, STATUS )

would unmap the centre array for all of the axes.

#### 19.9 Writing and Modifying Axis Arrays

New values may be written to an axis array by mapping it using NDF_AMAP and specifying a mapping mode of ‘UPDATE’ or ‘WRITE’. Any new values assigned to the mapped array (or modifications made in the case of ‘UPDATE’ access) will then be written back to the NDF when the array is unmapped, as described in detail in §8.5. The following example shows how an axis width array might be mapped for write access, passed to a routine SETVAL which assigns values to it, and then unmapped:

INTEGER PNTR( 1 ), EL

...

CALL NDF_AMAP( INDF, ’Width’, IAXIS, ’_REAL’, ’WRITE’, PNTR, EL, STATUS )
CALL SETVAL( 3.5, EL, %VAL( PNTR( 1 ) ), STATUS )
CALL NDF_AUNMP( INDF, ’Width’, IAXIS, STATUS )

...

*  Routine to assign axis width values.
SUBROUTINE SETVAL( VALUE, EL, WIDTH, STATUS )
INCLUDE ’SAE_PAR’
INTEGER EL, STATUS
REAL VALUE, WIDTH( EL )

IF ( STATUS .NE. SAI__OK ) RETURN

DO 1 I = 1, EL
WIDTH( I ) = VALUE
1    CONTINUE
END

When using update access, NDF_AMAP will ensure that the mapped array is filled with the appropriate default values if the axis array is initially in an undefined state.

After successfully unmapping an axis array mapped for update or write access, the array’s state will become defined. This process of assigning values to an axis array will also cause the NDF’s axis coordinate system to become defined (see §19.3), so default values will be assigned to all the NDF’s axis centre arrays if these have not already been defined. This process takes place whenever NDF_AMAP is called with a mapping mode of ‘UPDATE’ or ‘WRITE’, and is exactly equivalent to calling the routine NDF_ACRE19.4) immediately beforehand.

#### 19.10 Accessing Axis Variance Values as Standard Deviations

The values of an axis variance array may also be accessed directly as standard deviation values by specifying the special component name ‘Error’ in a call to NDF_AMAP. This facility operates in exactly the same way as the equivalent operation on the main variance component of an NDF (see §8.9) and causes the square root of the mapped values to be taken before they are returned. If a mapping mode of ‘UPDATE’ or ‘WRITE’ is specified, then the new or modified values are also squared before being written back to the axis array when it is unmapped.

Note that the component name ‘Error’ may only be used with mapping routines. The name ‘Variance’ should be used when later unmapping values accessed in this way.

#### 19.11 Axis Normalisation Flags

The value of an axis normalisation flag (§18.8) may be obtained by means of the routine NDF_ANORM, as follows:

LOGICAL NORM

...

CALL NDF_ANORM( INDF, IAXIS, NORM, STATUS )

This will return the normalisation flag value for the specified axis via the logical NORM argument. An IAXIS value of zero may also be given, in which case the routine will return the logical “OR” of the results for each NDF axis. By default, the value returned will be .FALSE., indicating that no corrections to preserve data normalisation need be applied.

A new value for an axis normalisation flag may be set by using the NDF_ASNRM routine, as follows:

CALL NDF_ASNRM( NORM, INDF, IAXIS, STATUS )

The new flag value is supplied via the NORM argument. A value of zero for the IAXIS argument will cause the same normalisation flag value to be set for all the NDF’s axes.

Note that the axis normalisation flag is regarded as an axis attribute (like numeric type and storage form) rather than an axis component, so setting a new normalisation flag value does not automatically cause the axis coordinate system to become defined. Normalisation flag values will only be retained if the axis coordinate system is in a defined state when the NDF is finally released from the NDF system (i.e. when the last identifier which refers to it is annulled).

#### 19.12 The Numeric Type of Axis Arrays

An NDF’s axis array values may be stored using any of the seven non-complex numeric types described in §7.1, and may also be accessed using any of these types. Type conversion will be performed automatically when required. By default, all axis arrays use a numeric type of ‘_REAL’, although this may be changed if required (see below).

The routine NDF_ATYPE is provided for determining the numeric type of an axis array, as follows:

INCLUDE ’NDF_PAR’
CHARACTER * ( NDF__SZTYP ) TYPE

...

CALL NDF_ATYPE( INDF, ’Centre’, IAXIS, TYPE, STATUS )

This will return the numeric type as an upper-case character string (e.g. ‘_REAL’) via the TYPE argument. Note the use of the symbolic constant NDF__SZTYP (as defined in the include file NDF_PAR) to define the length of the character variable which is to receive the returned value.

NDF_ATYPE will also accept a list of axis array names, and a value of zero may be given for the IAXIS argument to indicate that all the NDF’s axes should be considered at once. In this case, the routine will return the lowest-precision numeric type to which all the specified axis arrays may be converted without unnecessary loss of information.

So long as suitable access is available (see §23.1), the numeric type of an axis array may be changed at any time. If the array is in a defined state when this occurs, its values will be converted to the new type and will not be lost—the array may be reset beforehand (e.g. using NDF_AREST) if its values are not to be retained. A component list and/or an IAXIS value of zero may also be used. Thus, the following call would ensure that all of an NDF’s axis centre and width values were stored in double-precision arrays:

CALL NDF_ASTYP( ’_DOUBLE’, INDF, ’Cen,Wid’, 0, STATUS )

Note that the numeric type attribute of an axis array exists regardless of the array’s state. A call such as that above can therefore be made before any axis values are assigned and will ensure that arrays of the required type are used when values are later assigned to them.

#### 19.13 The Storage Form of Axis Arrays

An NDF’s axis arrays may be stored using either simple or primitive storage form (see §12), the default being chosen to match that of the NDF’s main data component. The storage form of an axis array may be obtained by using the routine NDF_AFORM, as follows:

INCLUDE ’NDF_PAR’
CHARACTER * ( NDF__SZFRM ) FORM

...

CALL NDF_AFORM( INDF, ’Centre’, IAXIS, FORM, STATUS )

This example would return the storage form of the specified axis centre array (e.g. ‘SIMPLE’) as an upper-case character string via the FORM argument. Note the use of the symbolic constant NDF__SZFRM (defined in the include file NDF_PAR) to define the size of the character variable which is to receive the returned storage form information.

As with other NDF array components, the primitive storage form places certain restrictions on the use to which an axis array may be put (see §12.6). In practice, however, there is usually little need to be aware of this, because the NDF_ system will implicitly convert the storage form of primitive axis arrays to become simple whenever one of these restrictions would be violated. This is a straightforward operation which costs little. Since bad-pixel flags are not relevant to axis arrays, the only changes which can precipitate such a storage form conversion are those which modify the NDF’s pixel-index bounds. The lower bound of an axis array is conceptually equal to the lower bound of the corresponding NDF dimension, so a primitive axis array will be converted to simple storage form if the lower bound of the relevant NDF dimension ceases to be equal to 1.

#### 19.14 Accessing Axis Components via NDF Sections

The values of axis components and their attributes may be obtained (i.e. read) freely via identifiers which refer to NDF sections (see §15). In fact, in the case of axis character components there is no difference between using NDF sections and base NDFs for this purpose. With axis arrays, however, it is necessary that the appropriate part of each array be selected so that it correctly matches the NDF pixels to which the section refers. This operation is performed automatically by the NDF_ system.

In contrast, the writing or modification of axis component values and attributes has to be handled very differently when NDF sections are involved, in order to adhere to the principles described in §15.7. Accordingly, the following major restriction is placed on such operations:

No changes to axis component values or attributes may be made via an NDF section

This restriction is necessary so that an application which is applied to an NDF section is prevented from modifying axis values which may affect the interpretation of NDF pixels lying outside the section in question.

Applications should, nevertheless, still be able to operate on NDF sections with the possible limitation that axis modifications may be lost. To allow this, the NDF_ system permits attempts to modify axis values or attributes via NDF sections to proceed without error. However, all the relevant routines will simply return without action under these circumstances, so the attempted changes to the axis components will be disregarded, leaving the components unaffected.

#### 19.15 Axis Extrapolation

When using NDF sections, the possibility also exists of reading axis array values from sections which refer to super-sets of the associated base NDF (§15.4). Although the section’s axis arrays may be in a defined state (values having previously been assigned via the base NDF), parts of them will still lie outside the bounds of the base NDF, and so cannot have any values. In such cases, axis arrays must be extrapolated in order to return useful values. This process uses a set of axis extrapolation rules which are an extension of the rules normally used to generate the default axis values (§18.5), as follows:

Centre:
Axis centre arrays are linearly extrapolated from the centre values of the two nearest base-NDF pixels on the same axis. If only one of these pixels exists, then the extrapolation assumes unit spacing between pixel centres.
Width:
If an axis width array is in a defined state, it is extrapolated by duplicating the nearest base-NDF width value on the same axis. If it is in an undefined state, its new values are derived from the corresponding extrapolated centre values by employing the usual method for generating default axis width values (§18.5)—this normally gives rise to width values which match the pixel centre spacing of the two nearest base-NDF pixels on the same axis.
Variance:
Axis variance arrays are always extrapolated by padding with the value zero.

There is also the possibility that an NDF section may have more dimensions than its associated base NDF (see §15.6), in which case associated axis components will also exist. These “virtual” axis components may be accessed in the same way as those associated with other dimensions, but they are always regarded as being in an undefined state. Default values will be supplied for them, if necessary, using the normal defaulting rules (see §18.5), but any new values assigned will simply be discarded.

18Note that the name “CENTRE” used by the NDF_ routines to refer to an NDF’s centre component differs from the actual name of the HDS object in which it is stored, which is “DATA_ARRAY”.

19Under error conditions a “safe” value of 1 will be returned for EL, as discussed in §4.4.