summaryrefslogtreecommitdiffstats
path: root/ast/stcschan-demo3.c
blob: b7f4e6f0b91fa2ef9d1a968e2eff65a6150f9969 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
/* Name:
      stcschan-demo3.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 text file, and also
      reads a set of 2-D spatial FITS-WCS headers from another (text) file.
      It then opens a specified graphics device, and displays an annotated
      coordinate grid covering the region described by the FITS headers.
      Finally, it draws the outline of the spatial extent of the STC-S
      description over the top of the annotated coordinate grid.

   Usage:
      % stcschan-demo3 <stcs-file> <header-file> <device>

      <stcs-file>: The path to a text file containing the STC-S
      description.

      <header-file>: The path to a text file containing a set of FITS-WCS
      headers.

      <device>: The name of an available PGPLOT graphics device. If not
      supplied, the available device names will be listed and the program
      will then exit.

   Example:
      % stcschan-demo3 m31.stcs andromeda.head /xserve

   To compile and link:
      Assuming your starlink distribution is in "/star":

      % gcc -o stcschan-demo3 stcschan-demo3.c -g -L/star/lib \
             -I/star/include `ast_link -pgplot` -lcpgplot

*/

/* Include system headers. */
#include <stdio.h>
#include <string.h>

/* Include the PGPLOT header. */
#include "cpgplot.h"

/* Include the AST library header. */
#include "ast.h"

/* Maximum allowed length for a single line of text from the disk file. */
#define MAX_LINE_LEN 100

/* Prototypes */
const char *source( void );
AstFrameSet *ReadFitsHeaders( const char *, int *, int * );
AstRegion *ReadStcs( const char * );


int main( int argc, char **argv ){

/* Local variables: */
   AstBox *pixbox;
   AstFrame *pixfrm;
   AstFrameSet *align_fs = NULL;
   AstFrameSet *fset = NULL;
   AstPlot *plot;
   AstRegion *reg = NULL;
   AstRegion *wcsbox = NULL;
   AstRegion *wcsreg = NULL;
   const char *dev;
   double bbox[ 4 ];
   float gbox[ 4 ];
   int overlap_flag, status, naxis1, naxis2, ibase;

/* Initialise the returned system status to indicate failure. */
   status = 1;

/* 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;

/* Check there are enough command line arguments. */
   if( argc < 3 ) {
      printf( "Usage: stcschan-demo3 <stcs-file> <header-file> <device>\n" );

/* If so, attempt to read the STC-S description, creating a corresponding
   AST Region. If this is successful, attempt to read the FITS header file
   and create an equivalent AST FrameSet. */
   } else {
      reg = ReadStcs( argv[ 1 ] );
      if( reg ) fset = ReadFitsHeaders( argv[ 2 ], &naxis1, &naxis2 );
   }

/* Check we obtained a Region and a FrameSet successfully. */
   if( reg && fset ){

/* Check that we can align the FITS WCS with the spatial axes in the
   STC-S description. AST contains various built-in conversions between
   standard celestial coordinate system. The necessary conversion will be
   identified and used automatically if necessary to achieve alignment.
   The returned object "align_fs" is a FrameSet that encapsulates the
   the FITS WCS coordinate system, the STC-S spatial coordinate system and
   the Mapping between them. A NULL pointer is returned if alignment is
   not possible. Note, the astConvert method changes the base Frame in
   any supplied FrameSet to indicate which coordinate frame was used for
   alignment. So we first note the original base Frame index and then
   re-instate it afterwards. */
      ibase = astGetI( fset, "Base" );
      align_fs = astConvert( reg, fset, " " );
      astSetI( fset, "Base", ibase );

      if( !align_fs ) {
         printf( "Could not align the FITS WCS with the spatial axes "
                 "in the STC-S\n" );

/* If alignment was possible, use the alignment FrameSet to create a new
   Region representing the same region as the supplied STC-S, but
   expressed in the coordinate system of the FITS WCS. The FrameSet class
   inherits from both Frame and Mapping, and so the "align_fs" FrameSet can
   be used both as the Mapping in this call, and as the Frame. When used
   as a Mapping, a FrameSet represents the transformation between its base
   and current Frame. When used as a Frame, a FrameSet represents its
   current Frame. */
      } else {
         wcsreg = astMapRegion( reg, align_fs, align_fs );

/* It would be nice to warn the user if the STC-S AstroCoordsArea does
   not overlap the FITS grid. To do so we need a Region representing the
   FITS grid. Create one now (an AST Box). First store the bounds of the
   FITS grid, in pixel coordinates (i.e. a system in which the centre of
   the bottom left pixel is at (1.0,1.0) ). */
         bbox[ 0 ] = 0.5;
         bbox[ 1 ] = 0.5;
         bbox[ 2 ] = (double) naxis1 + 0.5;
         bbox[ 3 ] = (double) naxis2 + 0.5;

/* Get a pointer to the Frame describing the FITS pixel coordinate system
   (the base Frame in the FITS FrameSet). */
         pixfrm = astGetFrame( fset, AST__BASE );

/* Create a Box that encompasses the required range of axis values within
   the pixel coordinate Frame. */
         pixbox = astBox( pixfrm, 1, bbox, bbox + 2, NULL, " " );

/* Create another Region that represents the same area, but in the FITS
   WCS. */
         wcsbox = astMapRegion( pixbox, fset, fset );

/* If the previous step failed, it probably means the FITS header covers
   the entire sky, resulting the corners of the pixel grid having invalid
   sky positions. So cancel the error and omit the overlap test. */
         if( !wcsbox ) {
            astClearStatus;
            printf("\nContinuing, but omitting overlap test...\n\n");

/* Now see if the Region representing the FITS grid overlaps the region
   read from the STC-S description.*/
         } else {
            overlap_flag = astOverlap( wcsreg, wcsbox );

            if( overlap_flag == 1 || overlap_flag == 6 ) {
               printf( "\nThere is no overlap between the FITS grid and the "
                       "STC-S AstroCoordsArea\n\n" );

            } else if( overlap_flag == 3 || overlap_flag == 5 ) {
               printf( "\nThe FITS grid is completely contained within the "
                       "STC-S AstroCoordsArea\n\n" );
            }

         }
      }
   }

/* Check we obtained a FITS-WCS to STC-S Mapping, and that no error has
   occurred in AST. */
   if( wcsreg && astOK ){

/* Open PGPLOT using the specified device. Prompt the user for a device if
   none was supplied on the command line. */
      if( argc < 4 ) {
         dev = "?";
      } else {
         dev = argv[ 3 ];
      }
      if( cpgbeg( 0, dev, 1, 1 ) == 1 ) {

/* Clear the screen. */
         cpgpage();

/* Ensure the graphics window has equal scales on both axes. */
         cpgwnad( 0.0f, 1.0f, 0.0f, 1.0f );

/* Find the extent of the graphics window, and store in an array suitable
   for passing to the astPlot function. */
         cpgqwin( gbox, gbox + 2, gbox + 1, gbox + 3 );

/* Create an AST Plot. This is a special sort of FrameSet in which the
   base Frame corresponds to graphics coordinates. All the coordinate
   Frames and Mappings read from the FITS-WCS headers are added into the
   Plot so that graphics can be drawn in any coordinate system. The extent
   of the FITS array in pixel coordinates is mapped onto the extent of the
   graphics device as returned above by cpgqwin. The AST library comes with
   a driver module that provides primitive drawing functions by calling
   appropriate PGFPLOT functions. It is simple to write driver modules
   for other graphics systems such as Tcl/Tk, Java/Swing, etc. Set a few
   graphics attributes to show the sort of thing that can be done. */
         plot = astPlot( fset, gbox, bbox, "Colour(border)=2,Colour(ticks)=2,"
                                       "Colour(axes)=2,Grid=1,Colour(grid)=3,"
                                       "Style(grid)=4" );

/* Draw a set of annotated coordinate axes labelling the FITS WCS axes. */
         astGrid( plot );

/* If there is any overlap (or if the overlap test could not be performed),
   add the STC-S Region into the Plot. We use a UnitMap to connect it to
   the current Frame (the FITS WCS frame). */
         if( 1 || overlap_flag == 2 || overlap_flag == 4 || !wcsbox ) {
            astAddFrame( plot, AST__CURRENT, astUnitMap( 2, " " ), wcsreg );

/* Now draw the border round the STC-S Region. A Region is a sub-class of
   Mapping and so can be used to transform positions. When a Region is used
   as a Mapping, positions that are inside the Region are left unchanged
   by the transformation, and positions that are outside the Region are
   transformed into "bad" positions (i.e. every axis value has the nmagic
   value AST__BAD indicating that the axis value is undefined). The
   astBorder method is a generic function that will outline the areas
   within the current coordinate Frame of the Plot that correspond to
   valid (i.e. non-bad) positions. Set the colour and line thickness first
   to emphasise the border. */
            astSet( plot, "Colour(border)=4,Width(border)=8" );
            (void) astBorder( plot );
         }

/* Set the returned system status to indicate success. */
         status = 0;

/* Close down PGPLOT. */
         cpgend();
      }
   }

/* End the AST Object context. All Objects created since the
   corresponding invocation of astbegin will be annulled automatically. */
   astEnd;

/* If an error occurred in the AST library, set the returned 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 );
}



/* -------------------------------------------------------------------
 * This function reads a set of FITS-WCS headers from a given text file,
 * and attempts to convert them into an AST FrameSet. If successful, a
 * pointer to the FrameSet is returned. A NULL pointer is returned if
 * anything goes wrong, or if the WCS is not 2-dimensional. The values of
 * the NAXIS1 and NAXIS2 headers are returned in "*naxis1" and "*naxis2".
*/

AstFrameSet *ReadFitsHeaders( const char *file, int *naxis1, int *naxis2 ){
   AstFitsChan *chan;
   AstFrameSet *result;
   AstObject *object;
   FILE *fd;

/* Initialise the returned pointer to indicate that no FrameSet has yet
   been read. */
   result = NULL;

/* Attempt to open the FITS header file */
   fd = fopen( file, "r" );
   if( !fd ) {
      printf("Failed to open FITS header file '%s'.\n", file );

/* If successful, create a FitsChan. This is the object that converts
   external FITS headers into corresponding AST Objects. Tell it to use
   the "source" function for obtaining lines of text from the disk file. */
   } else {
      chan = astFitsChan( source, NULL, " " );

/* 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( chan, fd );

/* Attempt to read the FITS heades and convert them into an AST FrameSet. */
      object = astRead( chan );

/* 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 FITS header file '%s'.\n",
                 file );

/* Now check that the object read is actually an AST FrameSet, rather than
   some other class of AST Object. */
      } else if( !astIsAFrameSet( object ) ) {
         printf( "Expected a FrameSet but read a %s from FITS header "
                 "file '%s'.\n", astGetC( object, "Class" ), file );

/* If the Object is a FrameSet, return the FrameSet pointer. */
      } else {
         result = (AstFrameSet *) object;

/* Check the WCS is 2-dimensional. If not, report an error and set the
   returned pointer to NULL. The memory used to store the FrameSet will
   be released when the current AST object context is ended (by calling
   astEnd). */
        if( astGetI( result, "Naxes" ) != 2 ) {
           printf( "The FITS WCS is not 2-dimensional.\n");
           result = NULL;

/* If it is 2-dimensional, get the NAXIS1 and NAXIS2 keyword values. */
        } else {

            if( !astGetFitsI( chan, "NAXIS1", naxis1 ) ){
               printf("Keyword 'NAXIS1' not found in header\n" );
               result = NULL;
            }

            if( !astGetFitsI( chan, "NAXIS2", naxis2 ) ){
               printf("Keyword 'NAXIS2' not found in header\n" );
               result = NULL;
            }

         }
      }

/* Close the file. */
      fclose( fd );
   }

   return result;
}


/* -------------------------------------------------------------------
 * This function reads an STC-S description from a given text file,
 * and attempts to convert them into an AST Region. If successful, a
 * pointer to the Region is returned. A NULL pointer is returned if
 * anything goes wrong.
*/

AstRegion *ReadStcs( const char *file ){
   AstStcsChan *chan;
   AstRegion *result;
   AstObject *object;
   FILE *fd;

/* Initialise the returned pointer to indicate that no Region has yet
   been read. */
   result = NULL;

/* Attempt to open the STC-S file */
   fd = fopen( file, "r" );
   if( !fd ) {
      printf("Failed to open STC-S descrption file '%s'.\n", file );

/* If successful, 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. */
   } else {
      chan = astStcsChan( source, NULL, " " );

/* 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( chan, 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( chan );

/* 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 STC-S description "
                 "file '%s'.\n", file );

/* 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 STC-S description "
                 "file '%s'.\n", astGetC( object, "Class" ), file );

/* If the Object is a Region, return the Region pointer. */
      } else {
         result = (AstRegion *) object;
      }

/* Close the file. */
      fclose( fd );
   }

   return result;
}