/* Name: stcschan-demo1.c Purpose: A demonstration of the facilities provided by the AST library for reading STC metadata encoded using the STC-S linear string format. Description: This program reads an STC-S description from a disk file, and tests a given position to see if it is inside or outside the AstroCoordsArea specified by the STC-S description. Usage: % stcschan-demo1 ... : The path to the disk file containing the STC-S description. ...: The axis values at the position to be tested. If insufficient values are supplied, a message describing the required values is displayed (label, units, etc). Example: % stcschan-demo1 stcs-ex1.txt 1996-01-01T00:00:15 11:56:00 -11:30:00 \ 1420.4 1000 To compile and link: Assuming your starlink distribution is in "/star": % gcc -o stcschan-demo1 stcschan-demo1.c -L/star/lib \ -I/star/include `ast_link` */ /* Include system headers. */ #include #include /* Include the AST library header. */ #include "ast.h" /* Maximum number of axes in an STC-S AstroCoordSystem. */ #define MAX_AXES 5 /* Maximum allowed length for a single line of text form the disk file. */ #define MAX_LINE_LEN 500 /* Prototype for the function that reads text from the disk file. */ const char *source( void ); int main( int argc, char **argv ){ /* Local variables: */ AstKeyMap *warnings; AstObject *object; AstRegion *region; AstStcsChan *channel; FILE *fd; char attrib[ 9 ]; char key[ 15 ]; const char *message; double inpos[ MAX_AXES ]; double outpos[ MAX_AXES ]; int axis; int iwarn; int naxis; int nc; int status; /* Initialised the returned system status to indicate success. */ status = 0; /* Check a file was specified on the command line, and attempt to open it for read access. */ if( argc < 2 ) { printf( "Usage: stcschan-demo1 ...\n" ); status = 1; } else { fd = fopen( argv[ 1 ], "r" ); if( !fd ) { printf("Failed to open input file '%s'.\n", argv[ 1 ] ); status = 1; } } /* If a disk file was opened successfully... */ if( !status ) { /* Start an AST object context. This means we do not need to annull each AST Object individually. Instead, all Objects created within this context will be annulled automatically by the corresponding invocation of astEnd. */ astBegin; /* Create an StcsChan. This is the object that converts external STC-S descriptions into corresponding AST Objects. Tell it to use the "source" function for obtaining lines of text from the disk file. Also tell it to store all warnings generated by the conversion for later use. Other attributes of the StcsChan class retain their default values. */ channel = astStcsChan( source, NULL, "ReportLevel=3" ); /* Associate the descriptor for the input disk file with the StcsChan. This makes it available to the "source" function. Since this application is single threaded, we could instead have made "fd" a global variable, but the ChannelData facility is used here to illustrate how to pass data to a source or sink function safely in a multi-threaded application. */ astPutChannelData( channel, fd ); /* The default behaviour of the astRead function when used on an StcsChan is to read and return the AstroCoordArea as an AST Region. This behaviour can be changed by assigning appropriate values to the StcsChan attributes "StcsArea", "StcsCoords" and "StcsProps". Options exist to return the AstroCoords as an AST PointList, and/or to return the individual property values read from the STC-S text in the form of an AST KeyMap (a sort of hashmap). For now, just take the default action of reading the AstroCoordsArea. */ object = astRead( channel ); /* The astRead function is a generic function and so returns a generic AstObject pointer. Check an Object was created successfully. */ if( !object ) { printf( "Failed to read an AST Object from file '%s'.\n", argv[ 1 ] ); status = 1; /* Now check that the object read is actually an AST Region, rather than some other class of AST Object. */ } else if( !astIsARegion( object ) ) { printf( "Expected a Region but read a %s from file '%s'.\n", astGetC( object, "Class" ), argv[ 1 ] ); status = 1; /* We now now know we have a Region so it is safe to use the pointer returned by astRead as a Region pointer. Do the cast now to avoid repeated casting in future. */ } else { region = (AstRegion *) object; /* Get the number of axes in the AstroCoordSystem, and check it is not larger than expected. */ naxis = astGetI( region, "Naxes" ); if( naxis > MAX_AXES ) { printf( "The coordinate system read from file '%s' has " "too many axes (%d). Up to %d axes are allowed.\n", argv[ 1 ], naxis, MAX_AXES ); status = 1; /* Now check that the correct number of axis values were supplied on the command line. If not, issue a warning message and give details of the label and units for each axis. Note, AST axis indices are one-based, in the range 1 to "Naxes". */ } else if( argc != 2 + naxis ) { printf( "The coordinate system read from file '%s' has " "%d axes, but %d axis values were supplied on the " "command line. ", argv[ 1 ], naxis, argc - 2 ); printf( "Values are required for the following axes:\n"); for( axis = 1; axis <= naxis; axis++ ) { sprintf( attrib, "Label(%d)", axis ); printf( " Axis %d: %s ", axis, astGetC( region, attrib ) ); sprintf( attrib, "Unit(%d)", axis ); printf( "(%s)\n", astGetC( region, attrib ) ); } status = 1; /* If the correct number of axis values was supplied on the command line, convert the supplied strings into floating point axis values. Each class of axis has its own formatting and unformatting rules that are controlled by various attributes such as "Format" and "Digits". Values for these attributes could be stored in the Region if different unformatting conventions were preferred. */ } else { for( axis = 1; axis <= naxis; axis++ ) { nc = astUnformat( region, axis, argv[ axis + 1 ], inpos + axis - 1 ); if( nc != strlen( argv[ axis + 1 ] ) ) { sprintf( attrib, "Label(%d)", axis ); printf( "Failed to interpret '%s' as a value for axis " "%d (%s).\n", argv[ axis + 1 ], axis, astGetC( region, attrib ) ); status = 1; break; } else { printf("%g ", inpos[ axis - 1 ] ); } } printf("\n"); } /* If we have obtained a full set of floating point axis values, use the Region as a Mapping to transform the supplied position. When a Region is used as a Mapping, the transformation leaves all axis values unchanged for interior positions, but assigns the magic value AST__BAD to all axes for exterior positions. */ if( !status ) { astTranN( region, 1, naxis, 1, inpos, 1, naxis, 1, outpos ); /* Issue a message describing the position tested and indicating if it is inside or outside the AstroCoordsArea. */ printf( "\nThe position ( %s=%s", astGetC( region, "Symbol(1)" ), argv[ 2 ] ); for( axis = 2; axis <= naxis; axis++ ) { sprintf( attrib, "Symbol(%d)", axis ); printf(", %s=%s", astGetC( region, attrib ), argv[ axis + 1 ] ); } printf( " ) is " ); if( outpos[ 0 ] == AST__BAD ) { printf( "OUTSIDE" ); } else { printf( "INSIDE" ); } printf( " the region read from file '%s'.\n\n", argv[ 1 ] ); } } /* We asked the StcsChan to record any warnings that were generated whilst converting the STC-S description into a corresponding AST Object (a Region in this case). We now see if any such warnings were generated by the earlier call to astRead. */ warnings = astWarnings( channel ); /* If any warnings were generated, and if no other error has occurred so far, display the warnings. */ if( warnings && !status && astOK ) { printf( "\nThe following warnings were issued reading file " "'%s':\n", argv[ 1 ] ); /* The warnings are stored in an AST KeyMap (a sort of hashmap). Each warning message is associated with a key of the form "Warning_1", "Warning_2", etc. Loop round successive keys, obtaining a value for each key from the warnings KeyMap, and displaying it. */ iwarn = 1; while( astOK ) { sprintf( key, "Warning_%d", iwarn++ ); if( astMapGet0C( warnings, key, &message ) ) { printf( "\n- %s\n", message ); } else { break; } } } /* End the AST Object context. All Objects created since the corresponding invocation of astbegin will be annulled automatically. */ astEnd; /* Close the disk file. */ (void) fclose( fd ); } /* If an error occurred in the AST library, set the retiurns system status non-zero. */ if( !astOK ) status = 1; return status; } /* This is a function that reads a line of text from the disk file and returns it to the AST library. It is called from within the astRead function. */ const char *source( void ){ static char buffer[ MAX_LINE_LEN + 2 ]; FILE *fd = astChannelData; return fgets( buffer, MAX_LINE_LEN + 2, fd ); }