The common situation where an application takes two or more input NDFs and combines their array component values pixel-by-pixel (e.g. to multiply their data arrays together) raises a similar problem of how to cope with the diversity of NDF shapes which could be encountered. In this case the capabilities of the application are generally not an issue, because most applications will be written to process NDFs of arbitrary size. However, what steps should be taken if the NDFs supplied as input turn out to have different sizes?
A simple approach might be to check whether the shapes of the input NDFs
match and to report an error and abort if they do not.
This straightforward (but unfriendly) approach requires a significant number
of Fortran statements and quickly becomes a chore if it has to be performed
explicitly in every application.
However, by making use of NDF sections (see
§) to change the apparent pixel-index bounds of an
NDF, it is possible to do considerably better than this, and to allow
processing of NDFs whose bounds may not initially match.
To understand the principle, consider two NDFs with shapes which do not match, such as:
and
It would clearly be meaningless to attempt to multiply the data arrays of these two NDFs together pixel-by-pixel as they stand, since there are some pixels in the first one which do not exist in the second one, and vice versa. However, the pixels within the region:
do exist within both NDFs and can be multiplied together if we decide to discard the rest. This can be done by creating an appropriate section from each NDF which selects only these common pixels for further processing. This task, of calculating which pixels to use and of selecting the appropriate NDF sections, may be performed using the routine NDF_MBND, which matches the bounds of a pair of NDFs, as follows:
CALL NDF_MBND( 'TRIM', INDF1, INDF2, STATUS )
If necessary, NDF_MBND will annul the NDF identifiers supplied and replace them with identifiers to appropriate NDF sections with matching shapes. The array components of these sections may then be accessed and compared pixel-by-pixel, since they will be of equal size. If the two NDFs have no pixels in common, then NDF_MBND will report an appropriate error message and set a STATUS value.
In cases where more than two NDFs are involved, the similar routine NDF_MBNDN may be used. This will match the bounds of an arbitrary number of NDFs, whose identifiers should be held in an integer array, for instance:
INTEGER N, NDFS( N )
...
CALL NDF_MBNDN( 'TRIM', N, NDFS, STATUS )
This routine will create sections with matching bounds from each NDF supplied, selecting only those pixels which exist in all of the NDFs. Once again, an appropriate error report will be made, and a STATUS value set, if this cannot be achieved.
If access to any of the original NDFs is still required, then the routine NDF_CLONE may be used to retain an identifier for it before calling NDF_MBND or NDF_MBNDN, since either of these latter routines may annul the identifiers passed to them. For example:
CALL NDF_CLONE( INDF1, INDFK, STATUS )
CALL NDF_MBND( 'TRIM', INDF1, INDF2, STATUS )
would ensure that an identifier INDFK is retained for the NDF whose original identifier was held in INDF1, even although NDF_MBND may alter the INDF1 variable so that it refers only to a sub-section of the NDF.
An alternative method of matching NDF bounds is by padding, in which each NDF is padded out with bad pixels rather than by discarding pixels at the edges. It may be employed by specifying `PAD' for the first (OPTION) argument to NDF_MBND or NDF_MBNDN. For instance:
CALL NDF_MBND( 'PAD', INDF1, INDF2, STATUS )
In this case, the NDF sections produced may represent super-sets of the
original NDFs (see §).
This option is needed less often, but it has the advantage that no pixels
will be lost.
It is generally used in cases where an output pixel should be assigned a
value even if one of the input pixels is missing (or has a bad value).
For instance, an application to merge the data arrays of two
non-congruent NDFs by averaging their pixel values might use the `PAD'
option to match the bounds of the input NDFs.
It could then generate an output array using an algorithm along the
following lines, where an output pixel value is generated if either or both
input pixels are valid:
INTEGER EL, I
REAL A( EL ), B( EL ), C( EL )
INCLUDE 'PRM_PAR'
...
* Loop to process each array element.
DO 1 I = 1, EL
* If both input pixels are good, then take the average.
IF ( A( I ) .NE. VAL__BADR .AND. B( I ) .NE. VAL__BADR ) THEN
C( I ) = 0.5 * ( A( I ) + B( I ) )
* Otherwise, if the first one is good, then use it.
ELSE IF ( A( I ) .NE. VAL__BADR ) THEN
C( I ) = A( I )
* Otherwise use the second one.
ELSE
C( I ) = B( I )
END IF
1 CONTINUE
When using the `TRIM' option, the output NDF may be smaller than either of the two input NDFs, and when using the `PAD' option, it may be larger. However, the application usually need not be aware of this.