summaryrefslogtreecommitdiffstats
path: root/ast/stcschan.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2017-12-08 18:59:21 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2017-12-08 18:59:21 (GMT)
commit4432c8d7e1ccb371db03e13cdb5378fceaa5ad04 (patch)
tree52449d211eba99b24d2e4f6e66193f9511c60b59 /ast/stcschan.c
parentd8a2dbd825159e4a57ec0c6f4465f62be9e05111 (diff)
downloadblt-4432c8d7e1ccb371db03e13cdb5378fceaa5ad04.zip
blt-4432c8d7e1ccb371db03e13cdb5378fceaa5ad04.tar.gz
blt-4432c8d7e1ccb371db03e13cdb5378fceaa5ad04.tar.bz2
upgrade AST
Diffstat (limited to 'ast/stcschan.c')
-rw-r--r--ast/stcschan.c8732
1 files changed, 8732 insertions, 0 deletions
diff --git a/ast/stcschan.c b/ast/stcschan.c
new file mode 100644
index 0000000..4b43524
--- /dev/null
+++ b/ast/stcschan.c
@@ -0,0 +1,8732 @@
+/*
+*class++
+* Name:
+* StcsChan
+
+* Purpose:
+* I/O Channel using STC-S to represent Objects.
+
+* Constructor Function:
+c astStcsChan
+f AST_STCSCHAN
+
+* Description:
+* A StcsChan is a specialised form of Channel which supports STC-S
+* I/O operations. Writing an Object to an StcsChan (using
+c astWrite) will, if the Object is suitable, generate an
+f AST_WRITE) will, if the Object is suitable, generate an
+* STC-S description of that Object, and reading from an StcsChan will
+* create a new Object from its STC-S description.
+*
+* When an STC-S description is read using
+c astRead,
+f AST_READ,
+* the returned AST Object may be 1) a PointList describing the STC
+* AstroCoords (i.e. a single point of interest within the coordinate frame
+* described by the STC-S description), or 2) a Region describing the STC
+* AstrCoordsArea (i.e. an area or volume of interest within the coordinate
+* frame described by the STC-S description), or 3) a KeyMap
+* containing the uninterpreted property values read form the STC-S
+* description, or 4) a KeyMap containing any combination of the first
+* 3 options. The attributes StcsArea, StcsCoords and StcsProps
+* control which of the above is returned by
+c astRead.
+f AST_READ.
+*
+* When an STC-S description is created from an AST Object using
+c astWrite,
+f AST_WRITE,
+* the AST Object must be either a Region or a KeyMap. If it is a
+* Region, it is assumed to define the AstroCoordsArea or (if the
+* Region is a single point) the AstroCoords to write to the STC-S
+* description. If the Object is a KeyMap, it may contain an entry
+* with the key "AREA", holding a Region to be used to define the
+* AstroCoordsArea. It may also contain an entry with the key "COORDS",
+* holding a Region (a PointList) to be used to create the
+* AstroCoords. It may also contain an entry with key "PROPS", holding
+* a KeyMap that contains uninterpreted property values to be used as
+* defaults for any STC-S properties that are not determined by the
+* other supplied Regions. In addition, a KeyMap supplied to
+c astWrite
+f AST_WRITE
+* may itself hold the default STC-S properties (rather than defaults
+* being held in a secondary KeyMap, stored as the "PROPS" entry in the
+* supplied KeyMap).
+*
+* The
+c astRead and astWrite
+f AST_READ and AST_WRITE
+* functions work together so that any Object returned by
+c astRead can immediately be re-written using astWrite.
+f AST_READ can immediately be re-written using AST_WRITE.
+*
+* Normally, when you use an StcsChan, you should provide "source"
+c and "sink" functions which connect it to an external data store
+c by reading and writing the resulting text. These functions
+f and "sink" routines which connect it to an external data store
+f by reading and writing the resulting text. These routines
+* should perform any conversions needed between external character
+c encodings and the internal ASCII encoding. If no such functions
+f encodings and the internal ASCII encoding. If no such routines
+* are supplied, a Channel will read from standard input and write
+* to standard output.
+*
+* Alternatively, an XmlChan can be told to read or write from
+* specific text files using the SinkFile and SourceFile attributes,
+* in which case no sink or source function need be supplied.
+*
+* Support for STC-S is currently based on the IVOA document "STC-S:
+* Space-Time Coordinate (STC) Metadata Linear String Implementation",
+* version 1.30 (dated 5th December 2007), available at
+* http://www.ivoa.net/Documents/latest/STC-S.html. Note, this
+* document is a recommednation only and does not constitute an accepted
+* IVOA standard.
+*
+* The full text of version 1.30 is supported by the StcsChan class,
+* with the following exceptions and provisos:
+*
+* - When reading an STC-S phrase, case is ignored except when reading
+* units strings.
+* - There is no support for multiple intervals specified within a
+* TimeInterval, PositionInterval, SpectralInterval or RedshiftInterval.
+* - If the ET timescale is specified, TT is used instead.
+* - If the TEB timescale is specified, TDB is used instead.
+* - The LOCAL timescale is not supported.
+* - The AST TimeFrame and SkyFrame classes do not currently allow a
+* reference position to be specified. Consequently, any <refpos>
+* specified within the Time or Space sub-phrase of an STC-S document
+* is ignored.
+* - The Convex identifier for the space sub-phrase is not supported.
+* - The GEO_C and GEO_D space frames are not supported.
+* - The UNITSPHERE and SPHER3 space flavours are not supported.
+* - If any Error values are supplied in a space sub-phrase, then the
+* number of values supplied should equal the number of spatial axes,
+* and the values are assumed to specify an error box (i.e. error
+* circles, ellipses, etc, are not supported).
+* - The spectral and redshift sub-phrases do not support the
+* following <refpos> values: LOCAL_GROUP_CENTER, UNKNOWNRefPos,
+* EMBARYCENTER, MOON, MERCURY, VENUS, MARS, JUPITER, SATURN, URANUS,
+* NEPTUNE, PLUTO.
+* - Error values are supported but error ranges are not.
+* - Resolution, PixSize and Size values are ignored.
+* - Space velocity sub-phrases are ignored.
+
+* Inheritance:
+* The StcsChan class inherits from the Channel class.
+
+* Attributes:
+* In addition to those attributes common to all Channels, every
+* StcsChan also has the following attributes:
+*
+* - StcsArea: Return the CoordinateArea component after reading an STC-S?
+* - StcsCoords: Return the Coordinates component after reading an STC-S?
+* - StcsLength: Controls output buffer length
+* - StcsProps: Return the STC-S properties after reading an STC-S?
+
+* Functions:
+c The StcsChan class does not define any new functions beyond those
+f The StcsChan class does not define any new routines beyond those
+* which are applicable to all Channels.
+
+* Copyright:
+* Copyright (C) 2009 Science & Technology Facilities Council.
+* All Rights Reserved.
+
+* Licence:
+* This program is free software: you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation, either
+* version 3 of the License, or (at your option) any later
+* version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General
+* License along with this program. If not, see
+* <http://www.gnu.org/licenses/>.
+
+* Authors:
+* DSB: David Berry (Starlink)
+
+* History:
+* 18-DEC-2008 (DSB):
+* Original version.
+* 22-MAY-2008 (DSB):
+* Retain default Equinox values in SkyFrame when reading an STC-S.
+* 30-OCT-2009 (DSB):
+* Make case insensitive (except for units strings).
+* 21-FEB-2014 (DSB):
+* Split long properties up into words when writing out an STC-S
+* description.
+* 26-MAR-2015 (DSB):
+* Guard against seg faults if an error has already occured.
+*class--
+*/
+
+/* Module Macros. */
+/* ============== */
+/* Set the name of the class we are implementing. This indicates to
+ the header files that define class interfaces that they should make
+ "protected" symbols available. */
+#define astCLASS StcsChan
+
+/* Values identifying particular forms of CoordArea */
+#define NULL_ID 1
+#define TIME_INTERVAL_ID 2
+#define START_TIME_ID 3
+#define STOP_TIME_ID 4
+#define POSITION_INTERVAL_ID 5
+#define ALLSKY_ID 6
+#define CIRCLE_ID 7
+#define ELLIPSE_ID 8
+#define BOX_ID 9
+#define POLYGON_ID 10
+#define CONVEX_ID 11
+#define POSITION_ID 12
+#define TIME_ID 13
+#define SPECTRAL_INTERVAL_ID 14
+#define SPECTRAL_ID 15
+#define REDSHIFT_INTERVAL_ID 16
+#define REDSHIFT_ID 17
+#define VELOCITY_INTERVAL_ID 18
+#define UNION_ID 19
+#define INTERSECTION_ID 20
+#define DIFFERENCE_ID 21
+#define NOT_ID 22
+#define VELOCITY_ID 23
+
+/* The number of words used to form an extract from an STC-S description
+ for use in an error message. */
+#define NEWORD 10
+
+/* Max length of string returned by GetAttrib */
+#define GETATTRIB_BUFF_LEN 50
+
+/* Include files. */
+/* ============== */
+/* Interface definitions. */
+/* ---------------------- */
+
+#include "frame.h" /* Generic cartesian coordinate systems */
+#include "globals.h" /* Thread-safe global data access */
+#include "error.h" /* Error reporting facilities */
+#include "memory.h" /* Memory allocation facilities */
+#include "object.h" /* Base Object class */
+#include "channel.h" /* Interface for parent class */
+#include "stcschan.h" /* Interface definition for this class */
+#include "loader.h" /* Interface to the global loader */
+#include "skyframe.h" /* Celestial coordinate systems */
+#include "timeframe.h" /* Time coordinate systems */
+#include "specframe.h" /* Spectral coordinate systems */
+#include "wcsmap.h" /* PI-related constants */
+#include "region.h" /* Abstract regions */
+#include "interval.h" /* Axis intervals */
+#include "unitmap.h" /* Unit mappings */
+#include "nullregion.h" /* Boundless regions */
+#include "cmpregion.h" /* Compound regions */
+#include "box.h" /* Box regions */
+#include "prism.h" /* Prism regions */
+#include "circle.h" /* Circle regions */
+#include "ellipse.h" /* Ellipse regions */
+#include "polygon.h" /* Polygon regions */
+#include "pointlist.h" /* Lists of points */
+#include "keymap.h" /* KeyMap interface */
+
+
+/* Error code definitions. */
+/* ----------------------- */
+#include "ast_err.h" /* AST error codes */
+
+/* C header files. */
+/* --------------- */
+#include <ctype.h>
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* Module Types. */
+/* ============= */
+typedef struct WordContext {
+ char *line;
+ char *wnext;
+ char *e;
+ char f;
+ int done;
+ char *words[ NEWORD ];
+ int next;
+ int close;
+ int open;
+} WordContext;
+
+/* Module Variables. */
+/* ================= */
+
+/* Pointers to parent class methods which are extended by this class. */
+static const char *(* parent_getattrib)( AstObject *, const char *, int * );
+static int (* parent_testattrib)( AstObject *, const char *, int * );
+static void (* parent_clearattrib)( AstObject *, const char *, int * );
+static void (* parent_setattrib)( AstObject *, const char *, int * );
+static int (* parent_getindent)( AstChannel *, int * );
+
+/* Address of this static variable is used as a unique identifier for
+ member of this class. */
+static int class_check;
+
+/* Define macros for accessing each item of thread specific global data. */
+#ifdef THREAD_SAFE
+
+/* Define how to initialise thread-specific globals. */
+#define GLOBAL_inits \
+ globals->GetAttrib_Buff[ 0 ] = 0; \
+ globals->Class_Init = 0;
+
+/* Create the function that initialises global data for this module. */
+astMAKE_INITGLOBALS(StcsChan)
+
+/* Define macros for accessing each item of thread specific global data. */
+#define getattrib_buff astGLOBAL(StcsChan,GetAttrib_Buff)
+#define class_init astGLOBAL(StcsChan,Class_Init)
+#define class_vtab astGLOBAL(StcsChan,Class_Vtab)
+
+
+/* If thread safety is not needed, declare and initialise globals at static
+ variables. */
+#else
+
+/* Define the class virtual function table and its initialisation flag
+ as static variables. */
+static AstStcsChanVtab class_vtab; /* Virtual function table */
+static int class_init = 0; /* Virtual function table initialised? */
+
+/* Buffer returned by GetAttrib. */
+static char getattrib_buff[ GETATTRIB_BUFF_LEN + 1 ];
+
+#endif
+
+
+/* External Interface Function Prototypes. */
+/* ======================================= */
+/* The following functions have public prototypes only (i.e. no
+ protected prototypes), so we must provide local prototypes for use
+ within this module. */
+AstStcsChan *astStcsChanForId_( const char *(*)( void ),
+ char *(*)( const char *(*)( void ), int * ),
+ void (*)( const char * ),
+ void (*)( void (*)( const char * ), const char *, int * ),
+ const char *, ... );
+AstStcsChan *astStcsChanId_( const char *(* source)( void ),
+ void (* sink)( const char * ),
+ const char *options, ... );
+
+/* Prototypes for Private Member Functions. */
+/* ======================================== */
+static AstKeyMap *ReadProps( AstStcsChan *, int * );
+static AstObject *Read( AstChannel *, int * );
+static AstPointList *SinglePointList( AstFrame *, double *, AstRegion *, int *);
+static AstRegion *MakeSpaceRegion( AstKeyMap *, AstFrame *, double, int * );
+static char *AddItem( AstStcsChan *, AstKeyMap *, const char *, const char *, char *, int *, int *, int, int * );
+static char *ContextFragment( WordContext *, char **, int * );
+static char *PutRegionProps( AstStcsChan *, AstKeyMap *, const char *, int, char *, int *, int *, int, int * );
+static char *SourceWrap( const char *(*)( void ), int * );
+static const char *GetNextWord( AstStcsChan *, WordContext *, int * );
+static const char *ReadSpaceArgs( AstStcsChan *, const char *, int, int, WordContext *, AstKeyMap *, int * );
+static double *BoxCorners( AstFrame *, const double[2], const double[2], int * );
+static int GetIndent( AstChannel *, int * );
+static int GetRegionProps( AstStcsChan *, AstRegion *, AstKeyMap *, int, int, double, int, int * );
+static int SpaceId( const char *, int * );
+static int Write( AstChannel *, AstObject *, int * );
+static int WriteRegion( AstStcsChan *, AstRegion *, AstKeyMap *, int * );
+static void Dump( AstObject *, AstChannel *, int * );
+static void FreeContext( WordContext *, int * );
+static void GetFmt( const char *, AstKeyMap *, int, int, char *, int * );
+static void MapPut0C( AstKeyMap *, const char *, const char *, const char *, int, int * );
+static void MapPut0D( AstKeyMap *, const char *, double, double, int, int * );
+static void SetUnc( AstRegion *, AstRegion *, AstFrame *, int, double, double *, int, int * );
+static void SinkWrap( void (*)( const char * ), const char *, int * );
+static void WriteProps( AstStcsChan *, AstKeyMap *, int * );
+
+static int GetStcsArea( AstStcsChan *, int * );
+static int TestStcsArea( AstStcsChan *, int * );
+static void ClearStcsArea( AstStcsChan *, int * );
+static void SetStcsArea( AstStcsChan *, int, int * );
+
+static int GetStcsCoords( AstStcsChan *, int * );
+static int TestStcsCoords( AstStcsChan *, int * );
+static void ClearStcsCoords( AstStcsChan *, int * );
+static void SetStcsCoords( AstStcsChan *, int, int * );
+
+static int GetStcsProps( AstStcsChan *, int * );
+static int TestStcsProps( AstStcsChan *, int * );
+static void ClearStcsProps( AstStcsChan *, int * );
+static void SetStcsProps( AstStcsChan *, int, int * );
+
+static void ClearAttrib( AstObject *, const char *, int * );
+static const char *GetAttrib( AstObject *, const char *, int * );
+static void SetAttrib( AstObject *, const char *, int * );
+static int TestAttrib( AstObject *, const char *, int * );
+
+static int TestStcsLength( AstStcsChan *, int * );
+static void ClearStcsLength( AstStcsChan *, int * );
+static void SetStcsLength( AstStcsChan *, int, int * );
+static int GetStcsLength( AstStcsChan *, int * );
+
+/* Member functions. */
+/* ================= */
+
+static char *AddItem( AstStcsChan *this, AstKeyMap *km, const char *key,
+ const char *prefix, char *line, int *nc, int *crem,
+ int linelen, int *status ){
+/*
+* Name:
+* AddItem
+
+* Purpose:
+* Add an STC-S property item to a buffer.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* char *AddItem( AstStcsChan *this, AstKeyMap *km, const char *key,
+* const char *prefix, char *line, int *nc, int *crem,
+* int linelen, int *status )
+
+* Class Membership:
+* StcsChan member function
+
+* Description:
+* This function appends text describing a singlke STC-S property to
+* a supplied text buffer, handling the splitting of text into lines.
+
+* Parameters:
+* this
+* The StcsChan.
+* km
+* Pointer to a KeyMap containing the STC-S properties.
+* key
+* The key name associated with the property to be checked.
+* prefix
+* if not NULL, this is a string that is to be written out before
+* the property value. It should usually include a trailing space.
+* line
+* Pointer to the buffer to recieve the prefix and property value.
+* nc
+* Pointer to an int in which to store the number of characters in
+* the buffer. Updated on exit.
+* crem
+* Pointer to an int in which to store the maximum number of
+* characters before a new line. Ignored if linelen is zero. Updated
+* on exit.
+* linelen
+* The maximum number of character per line, or zero if all text is
+* to be included in a single line.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the buffer. This will usually be "line", but may be
+* different to "line" if it was necessary to expand the memory to make
+* room for the new property.
+
+*/
+
+/* Local Variables: */
+ char *result; /* Returned pointer */
+ char **words; /* All words */
+ const char *text; /* Property value */
+ const char *word; /* Single word */
+ int iw; /* Word index */
+ int len; /* Length of new text */
+ int nw; /* Number of words in property */
+
+/* Initialise */
+ result = line;
+ len = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* If the KeyMap contains the required property... */
+ if( astMapGet0C( km, key, &text ) ) {
+
+/* Add any supplied prefix to the returned buffer. */
+ if( prefix ) {
+ len = strlen( prefix );
+ if( len > *crem && len < linelen ) {
+ astPutNextText( this, result );
+ *nc = 0;
+ result = astAppendString( result, nc, " " );
+ *crem = linelen - 3;
+ }
+ result = astAppendString( result, nc, prefix );
+ *crem -= len;
+ }
+
+/* Split the property into words. */
+ words = astChrSplit( text, &nw );
+
+/* Append each word to the buffer. */
+ for( iw = 0; iw < nw; iw++ ) {
+ word = words[ iw ];
+
+/* If required, get the number of characters to be added to the buffer. */
+ if( linelen ) {
+ len = strlen( word );
+
+/* If there is insufficient room left, write out the text through the
+ Channel sink function, and start a new line with three spaces. Then
+ reset the number of character remaining in the line. */
+ if( len > *crem && len < linelen ) {
+ astPutNextText( this, result );
+ *nc = 0;
+ result = astAppendString( result, nc, " " );
+ *crem = linelen - 3;
+ }
+
+/* Reduce crem to account for the text that is about to be added to the
+ line. */
+ *crem -= len;
+ }
+
+/* Add the property value to the returned buffer. */
+ result = astAppendString( result, nc, word );
+
+/* Add a traling space to the returned buffer, if there is room. */
+ if( !linelen || *crem > 0 ) {
+ result = astAppendString( result, nc, " " );
+ (*crem)--;
+ }
+ }
+
+/* Free the words buffer. */
+ if( words ) {
+ for( iw = 0; iw < nw; iw++ ) words[ iw ] = astFree( words[ iw ] );
+ words = astFree( words );
+ }
+ }
+
+/* Return the buffer pointer. */
+ return result;
+}
+
+static double *BoxCorners( AstFrame *frm, const double centre[2],
+ const double bsize[2], int *status ) {
+/*
+* Name:
+* BoxCorners
+
+* Purpose:
+* Determine the positions of the corners of an STC Box.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* double *BoxCorners( AstFrame *frm, const double centre[2],
+* const double bsize[2], int *status )
+
+* Class Membership:
+* StcsChan member function
+
+* Description:
+* This function returns a pointer to a dynamically allocated array
+* holding the positions of the corners of the STC Box defined by the
+* supplied "centre" and "bsize" arrays.
+
+* Parameters:
+* frm
+* Pointer to the Frame in which the Box is defined. Must be 2-D.
+* centre
+* Two element array holding the Frame co-ordinates at the centre
+* of the Box.
+* bsize
+* Two element array holding the full width and height of the Box.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to a dynamically allocated array holding the axis values
+* at the four corners, in a form suitable for passing to the
+* astPolygon constructor function. NULL is returned if an error has
+* already occurred, of if this function fails for any reason.
+*/
+
+/* Local Variables: */
+ double *result; /* Returned pointer. */
+ double bh1[ 2 ]; /* A first point on the bottom horizontal edge */
+ double bh2[ 2 ]; /* A second point on the bottom horizontal edge */
+ double blc[ 2 ]; /* Position of bottom left corner */
+ double brc[ 2 ]; /* Position of bottom right corner */
+ double lv1[ 2 ]; /* A first point on the left vertical edge */
+ double lv2[ 2 ]; /* A second point on the left vertical edge */
+ double pa; /* Position angle of great circle/straight line */
+ double rv1[ 2 ]; /* A first point on the right vertical edge */
+ double rv2[ 2 ]; /* A second point on the right vertical edge */
+ double th1[ 2 ]; /* A first point on the top horizontal edge */
+ double th2[ 2 ]; /* A second point on the top horizontal edge */
+ double tlc[ 2 ]; /* Position of top left corner */
+ double trc[ 2 ]; /* Position of top right corner */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Check the Frame is 2-dimensional. */
+ if( astGetNaxes( frm ) != 2 ) {
+ astError( AST__BADIN, "astRead(StcsChan): Supplied space frame has "
+ "%d axes.", status, astGetNaxes( frm ) );
+ astError( AST__BADIN, "astRead(StcsChan): Can only use STC Box regions "
+ "with 2-dimensional space frames.", status );
+ }
+
+/* Offset away from the centre by half the Box width along a great circle
+ initially parallel to the positive first frame axis (i.e. position
+ angle +pi/2). The end position goes in "rv1" and the position angle of
+ the great circle (or straight line) at that point is returned as the
+ function value. NOTE, the use of the words "left" and "right" below is
+ vague because it depends on whether we are using a SkyFrame (which has
+ a reversed first axis) or a basic Frame. In general, the choice of "left"
+ and "right" below is appropriate for a basic Frame. */
+ pa = astOffset2( frm, centre, AST__DPIBY2, bsize[ 0 ]/2, rv1 );
+
+/* Turn by 90 degrees and offset away by half the box height. This is done
+ so that we have a second point (rv2) to define the great circle (or
+ straight line) that forms the first vertical edge of the Box (i.e. the
+ great circle or straight line through rv1 and rv2). Note, for spherical
+ Frames (i.e. SkyFrames) "rv2" is not necessarily a corner of the box. */
+ (void) astOffset2( frm, rv1, pa + AST__DPIBY2, bsize[ 1 ]/2, rv2 );
+
+/* In the same way, get two points on the second vertical Box edge. */
+ pa = astOffset2( frm, centre, -AST__DPIBY2, bsize[ 0 ]/2, lv1 );
+ (void) astOffset2( frm, lv1, pa + AST__DPIBY2, bsize[ 1 ]/2, lv2 );
+
+/* In the same way, get two points on the top horizontal Box edge. */
+ pa = astOffset2( frm, centre, 0.0, bsize[ 1 ]/2, th1 );
+ (void) astOffset2( frm, th1, pa + AST__DPIBY2, bsize[ 0 ]/2, th2 );
+
+/* In the same way, get two points on the bottom horizontal Box edge. */
+ pa = astOffset2( frm, centre, AST__DPI, bsize[ 1 ]/2, bh1 );
+ (void) astOffset2( frm, bh1, pa + AST__DPIBY2, bsize[ 0 ]/2, bh2 );
+
+/* The first corner of the Box is at the intersection of the first
+ vertical and top horizontal edges. */
+ astIntersect( frm, lv1, lv2, th1, th2, tlc );
+
+/* The top right corner of the Box is at the intersection of the right
+ vertical and top horizontal edges. */
+ astIntersect( frm, rv1, rv2, th1, th2, trc );
+
+/* The bottom left corner of the Box is at the intersection of the left
+ vertical and bottom horizontal edges. */
+ astIntersect( frm, lv1, lv2, bh1, bh2, blc );
+
+/* The bottom right corner of the Box is at the intersection of the right
+ vertical and bottom horizontal edges. */
+ astIntersect( frm, rv1, rv2, bh1, bh2, brc );
+
+/* Gather the corners together into an array suitable for use with
+ astPolygon. Make sure the vertices are traversed in an ant-clockwise
+ sense whether in a SkyFrame or a basic Frame. */
+ result = astMalloc( 8*sizeof( *result ) );
+ if( result ) {
+ if( astIsASkyFrame( frm ) ) {
+ result[ 0 ] = tlc[ 0 ];
+ result[ 1 ] = trc[ 0 ];
+ result[ 2 ] = brc[ 0 ];
+ result[ 3 ] = blc[ 0 ];
+ result[ 4 ] = tlc[ 1 ];
+ result[ 5 ] = trc[ 1 ];
+ result[ 6 ] = brc[ 1 ];
+ result[ 7 ] = blc[ 1 ];
+ } else {
+ result[ 3 ] = tlc[ 0 ];
+ result[ 2 ] = trc[ 0 ];
+ result[ 1 ] = brc[ 0 ];
+ result[ 0 ] = blc[ 0 ];
+ result[ 7 ] = tlc[ 1 ];
+ result[ 6 ] = trc[ 1 ];
+ result[ 5 ] = brc[ 1 ];
+ result[ 4 ] = blc[ 1 ];
+ }
+
+ }
+
+/* Return the pointer. */
+ return result;
+}
+
+static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* ClearAttrib
+
+* Purpose:
+* Clear an attribute value for a StcsChan.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* void ClearAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* StcsChan member function (over-rides the astClearAttrib protected
+* method inherited from the Channel class).
+
+* Description:
+* This function clears the value of a specified attribute for a
+* StcsChan, so that the default value will subsequently be used.
+
+* Parameters:
+* this
+* Pointer to the StcsChan.
+* attrib
+* Pointer to a null terminated string specifying the attribute
+* name. This should be in lower case with no surrounding white
+* space.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ AstStcsChan *this; /* Pointer to the StcsChan structure */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the StcsChan structure. */
+ this = (AstStcsChan *) this_object;
+
+/* Check the attribute name and clear the appropriate attribute. */
+
+ if ( !strcmp( attrib, "stcsarea" ) ) {
+ astClearStcsArea( this );
+
+ } else if ( !strcmp( attrib, "stcscoords" ) ) {
+ astClearStcsCoords( this );
+
+ } else if ( !strcmp( attrib, "stcsprop" ) ) {
+ astClearStcsProps( this );
+
+ } else if ( !strcmp( attrib, "stcslength" ) ) {
+ astClearStcsLength( this );
+
+/* If the attribute is still not recognised, pass it on to the parent
+ method for further interpretation. */
+ } else {
+ (*parent_clearattrib)( this_object, attrib, status );
+ }
+}
+
+static char *ContextFragment( WordContext *con, char **buf, int *status ){
+/*
+* Name:
+* ContextFragment
+
+* Purpose:
+* Returns a string holding a fragment of the document being read.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* char *ContextFragment( WordContext *con, char **buf, int *status )
+
+* Class Membership:
+* StcsChan member function
+
+* Description:
+* This function returns a pointer to a string that holds a fragment
+* of the STC-S document currently being read. The fragment ends at
+* the last word read by function GetNextWord, and starts a certain
+* number of words earlier in the document, as specified by the NEWORD
+* macro.
+
+* Parameters:
+* con
+* Pointer to the context structure, managed by GetNextWord.
+* buf
+* Address of a pointer to a dynamically allocated buffer. This
+* pointer should be NULL on the first call to this function, and
+* will be updated by this function. The pointer should be freed
+* using astFree when no longer needed.
+* status
+* Address of the inherited status value.
+
+* Returned Value:
+* A pointer to the buffer.
+*/
+
+/* Local Variables: */
+ int i; /* Word count */
+ int j; /* Word index */
+ int nc; /* Text length */
+
+/* Initialise the number of characters written to the buffer. */
+ nc = 0;
+
+/* Get the index of the first word to add to the buffer. The "next"
+ component of the context structure holds the index at which the word
+ returned by the next call to GetNextWord will be stored. So at the
+ moment, this is the index of the oldest word in the cyclic list. */
+ j = con->next;
+
+/* Loop round all non-NULL words in the cyclic list. */
+ for( i = 0; i < NEWORD; i++ ) {
+ if( con->words[ j ] ) {
+
+/* Append this word to the buffer, extending the buffer size as
+ necessary. */
+ *buf = astAppendString( *buf, &nc, con->words[ j ] );
+
+/* Append a trailingh space. */
+ *buf = astAppendString( *buf, &nc, " " );
+ }
+
+/* Increment the index of the next word to use in the cyclic list. Wrap
+ back to zerp when the end of the list is reached. */
+ if( ++j == NEWORD ) j = 0;
+ }
+
+/* Remove the final trailing space. */
+ if( nc ) (*buf)[ nc - 1 ] = 0;
+
+/* Return a pointer to the supplied buffer. */
+ return *buf;
+}
+
+static void FreeContext( WordContext *con, int *status ){
+/*
+* Name:
+* FreeContext
+
+* Purpose:
+* Free the resources used by a word-reading context structure.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* voidFreeContext( WordContext *con, int *status );
+
+* Class Membership:
+* StcsChan member function
+
+* Description:
+* This function frees the resources used by the supplied WordContext
+* structure. This structure is used by GetNextWord to keep track of
+* which word to return next.
+*
+* This function frees the dynamic memory pointers stored within the
+* WordContext structure, but does not free the memory holding the
+* WordContext structure itself.
+
+* Parameters:
+* con
+* Pointer to a structure holding the context.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ int i; /* Word index */
+
+/* Check the supplied pointer. */
+ if ( !con ) return;
+
+/* Free the resources. */
+ con->line = astFree( con->line );
+
+ for( i = 0; i < NEWORD; i++ ) {
+ con->words[ i ] = astFree( con->words[ i ] );
+ }
+
+}
+
+static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* GetAttrib
+
+* Purpose:
+* Get the value of a specified attribute for a StcsChan.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* const char *GetAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* StcsChan member function (over-rides the protected astGetAttrib
+* method inherited from the Channel class).
+
+* Description:
+* This function returns a pointer to the value of a specified
+* attribute for a StcsChan, formatted as a character string.
+
+* Parameters:
+* this
+* Pointer to the StcsChan.
+* attrib
+* Pointer to a null terminated string containing the name of
+* the attribute whose value is required. This name should be in
+* lower case, with all white space removed.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* - Pointer to a null terminated string containing the attribute
+* value.
+
+* Notes:
+* - The returned string pointer may point at memory allocated
+* within the StcsChan, or at static memory. The contents of the
+* string may be over-written or the pointer may become invalid
+* following a further invocation of the same function or any
+* modification of the StcsChan. A copy of the string should
+* therefore be made if necessary.
+* - A NULL pointer will be returned if this function is invoked
+* with the global error status set, or if it should fail for any
+* reason.
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+ AstStcsChan *this; /* Pointer to the StcsChan structure */
+ const char *result; /* Pointer value to return */
+ int ival; /* Integer attribute value */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(this_object);
+
+/* Obtain a pointer to the StcsChan structure. */
+ this = (AstStcsChan *) this_object;
+
+/* Compare "attrib" with each recognised attribute name in turn,
+ obtaining the value of the required attribute. If necessary, write
+ the value into "getattrib_buff" as a null terminated string in an appropriate
+ format. Set "result" to point at the result string. */
+
+/* StcsArea. */
+/* --------- */
+ if ( !strcmp( attrib, "stcsarea" ) ) {
+ ival = astGetStcsArea( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%d", ival );
+ result = getattrib_buff;
+ }
+
+/* StcsCoords. */
+/* ----------- */
+ } else if ( !strcmp( attrib, "stcscoords" ) ) {
+ ival = astGetStcsCoords( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%d", ival );
+ result = getattrib_buff;
+ }
+
+
+/* StcsProps. */
+/* ---------- */
+ } else if ( !strcmp( attrib, "stcsprops" ) ) {
+ ival = astGetStcsProps( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%d", ival );
+ result = getattrib_buff;
+ }
+
+/* StcsLength */
+/* --------- */
+ } else if ( !strcmp( attrib, "stcslength" ) ) {
+ ival = astGetStcsLength( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%d", ival );
+ result = getattrib_buff;
+ }
+
+/* If the attribute name was not recognised, pass it on to the parent
+ method for further interpretation. */
+ } else {
+ result = (*parent_getattrib)( this_object, attrib, status );
+ }
+
+/* Return the result. */
+ return result;
+
+}
+
+static void GetFmt( const char *key, AstKeyMap *props, int i, int defdigs,
+ char *fmt, int *status ){
+/*
+* Name:
+* GetFmt
+
+* Purpose:
+* Decide how many digits to use when formatting a numerical STC-S
+* property value.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* void GetFmt( const char *key, AstKeyMap *props, int i,
+* int defdigs, char *fmt, int *status )
+
+* Class Membership:
+* StcsChan member function
+
+* Description:
+* This function locates the named property in the supplied KeyMap. If
+* it is found, a printf format specifier is generated that matches
+* the value is determined and returned. Otherwise, a default format
+* specified based on the supplied default number of digits is returned.
+
+* Parameters:
+* key
+* The key name associated with the property.
+* km
+* Pointer to a KeyMap containing the STC-S properties.
+* i
+* For vector values, this is the index of the vector element to be
+* checked. Should be zero for scalar values. If "i" is greater
+* than the number of values in the vector, then the number of digits
+* in the first element is found and returned.
+* defdigs
+* The value to return if the KeyMap does not contain an entry with
+* the supplied key.
+* fmt
+* Pointer to a string in which to return the format specifier.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ const char *dot; /* Pointer to decimal point */
+ const char *p; /* Pointer to next character */
+ const char *word; /* Property value */
+ int after0; /* Digits after the decimal point in first word */
+ int after; /* Digits after the decimal point in current word */
+ int before0; /* Digits before the decimal point in first word */
+ int before; /* Digits before the decimal point in current word */
+ int exp0; /* Was an exponent found in first word? */
+ int exp; /* Was an exponent found in current word? */
+ int j; /* Index of current word */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Initialise. */
+ exp = 1;
+ before = defdigs;
+ after = 0;
+ exp0 = 0;
+ before0 = 0;
+ after0 = 0;
+
+/* If the KeyMap contains the required property... */
+ if( astMapGet0C( props, key, &word ) ) {
+
+/* Skip over the words in the string. */
+ p = word;
+ for( j = 0; j <= i; j++ ) {
+
+/* Find the next space or terminating null at the end of the current word.
+ Also count the number of digits before and after the decimal point and
+ see if the word includes an exponent. */
+ exp = 0;
+ before = 0;
+ after = 0;
+ dot = NULL;
+
+ while( *p != 0 && *p != ' ' ) {
+ if( ! exp ) {
+ if( isdigit( *p ) ) {
+ if( dot ) {
+ after++;
+ } else {
+ before++;
+ }
+
+ } else if( *p == '.' ) {
+ dot = p;
+
+ } else if( *p == 'e' || *p == 'E' ) {
+ exp = 1;
+ }
+ }
+ p++;
+ }
+
+/* Note the values for the first word. */
+ if( j == 0 ) {
+ exp0 = exp;
+ before0 = before;
+ after0 = after;
+ }
+
+/* Find the following non-space marking the start of the next word,
+ or the terminating null. */
+ while( *p != 0 && *p == ' ' ) p++;
+
+/* If we find the terminating null before we have found the i'th word,
+ break out of the loop using the first word instead of the i'th word. */
+ if( *p == 0 ) {
+ exp = exp0;
+ before = before0;
+ after = after0;
+ break;
+ }
+ }
+ }
+
+ if( exp ) {
+ sprintf( fmt, "%%.%dg", before + after );
+ } else {
+ sprintf( fmt, "%%.%df", after );
+ }
+}
+
+static int GetIndent( AstChannel *this, int *status ) {
+/*
+* Name:
+* GetIndent
+
+* Purpose:
+* Get the value of the Indent attribute for a StcsChan.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* int GetIndent( AstChannel *this, int *status )
+
+* Class Membership:
+* StcsChan member function (over-rides the protected astGetIndent
+* method inherited from the Channel class).
+
+* Description:
+* This function returns the value of the Indent attribute, supplying
+* a default value appropriate to an StcsChan.
+
+* Parameters:
+* this
+* Pointer to the StcsChan.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* - The Indent value to use.
+
+*/
+
+/* If the attribute is set, return its value. Otherwise return a value of
+ zero. */
+ return astTestIndent( this ) ? (*parent_getindent)( this, status ) : 0;
+}
+
+static const char *GetNextWord( AstStcsChan *this, WordContext *con,
+ int *status ){
+/*
+* Name:
+* GetNextWord
+
+* Purpose:
+* Get a pointer to the next input word read from an STC-S source.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* const char *GetNextWord( AstStcsChan *this, WordContext *con,
+* int *status )
+
+* Class Membership:
+* StcsChan member function
+
+* Description:
+* This function returns a pointer to the next word of an STC-S
+* description.
+
+* Parameters:
+* this
+* Pointer to the StcsChan, or NULL (to initialise "con").
+* con
+* Pointer to a structure holding context. The structure should be
+* initialised by calling this function with a NULL "this" pointer
+* before making further use of this function. When finished, it
+* should be released using FreeContext.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new word. NULL is returned if an error has already
+* occurred, of if "this" is NULL.
+*/
+
+/* Local Variables: */
+ const char *result; /* Returned pointer. */
+ int i; /* Word index */
+ size_t len; /* Word length */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* If no StcChan was supplied, initialise the supplied WordContext. */
+ if( !this ) {
+ con->e = NULL;
+ con->line = NULL;
+ con->done = 0;
+ con->next = 0;
+ con->wnext = NULL;
+ con->close = 0;
+ con->open = 0;
+ for( i = 0; i < NEWORD; i++ ) con->words[ i ] = NULL;
+
+/* Words that end with an opening parenthesis are treated as two words. If the
+ previous word ended in an opening parenthesis, it will have been removed by
+ the previous call to this function and the "con->open" flag set. In
+ this case, we just return a pointer to the second of the two words - a
+ single "(" character - and clear the "con->open" flag. */
+ } else if( con->open && ! con->done ) {
+ con->open = 0;
+ result = "(";
+
+/* Likewise deal with words that end with a closing parenthesis. */
+ } else if( con->close && ! con->done ) {
+ con->close = 0;
+ result = ")";
+
+/* Words that begin with an opening parenthesis are treated as two words. If
+ the previous word was such an opening parenthesis, the rest of the word
+ will have been removed by the previous call to this function and the
+ "con->wnext" pointer set to the start of the remaining word. In
+ this case, re-instate the original character that was replaced by a
+ terminating null when the previous word was returned, return the
+ "con->wnext" pointer, and then clear the pointer. */
+ } else if( con->wnext && ! con->done ) {
+ *(con->wnext) = con->f;
+ result = con->wnext;
+ con->wnext = NULL;
+
+/* Otherwise... */
+ } else {
+
+/* If the previous invocation of this function converted a space
+ character into a null character, change it back again. */
+ if( con->e ) *(con->e) = ' ';
+
+/* Get a pointer to the next non-white character in the current line of
+ input text. */
+ result = con->e;
+ if( result ) {
+ while( *result && isspace( *result ) ) result++;
+ }
+
+/* If we have exhausted the current line, get the next line by invoking
+ the source function. We loop until we read a line that is not entirely
+ blank. */
+ while( ( !result || ! *result ) && astOK ) {
+
+/* First free the memory holding the previous line. */
+ if( con->line ) con->line = astFree( con->line );
+ con->e = NULL;
+
+/* Get the next line of text from the source function. */
+ con->line = astGetNextText( this );
+ result = con->line;
+
+/* Break when we reach the end of the input text. */
+ if( !result ) break;
+
+/* Get a pointer to the first non-white character in the new line. */
+ while( *result && isspace( *result ) ) result++;
+ }
+
+/* Find the end of the word. */
+ if( result && *result ) {
+ con->e = (char *) result + 1;
+ while( *(con->e) && !isspace( *(con->e) ) ) (con->e)++;
+
+/* If the word is already null-terminated, nullify the "e" pointer to
+ indicate this. Otherwise, change the white-space character into a
+ null. */
+ if( *(con->e) ) {
+ *(con->e) = 0;
+ len = con->e - result;
+ } else {
+ con->e = NULL;
+ len = strlen( result );
+ }
+
+/* Add the word into the cyclic list of words used to form a document
+ fragment to include in error and warning messages. */
+ con->words[ con->next ] = astStore( con->words[ con->next ],
+ result, len + 1 );
+ if( ++(con->next) == NEWORD ) con->next = 0;
+
+/* Deal with words that include an opening or closing parenthesis at
+ start or end. These words must have 2 or more characters. */
+ if( len > 1 ) {
+
+/* If the word ends with an opening parenthesis, replace the parenthesis
+ with a null character and set a flag indicating that the next word
+ returned should consist of just an opening parenthesis. */
+ if( result[ len - 1 ] == '(' ) {
+ ((char *) result)[ len - 1 ] = 0;
+ con->open = 1;
+
+/* If the word ends with a closing parenthesis, replace the parenthesis
+ with a null character and set a flag indicating that the next word
+ returned should consist of just a closing parenthesis. */
+ } else if( result[ len - 1 ] == ')' ) {
+ ((char *) result)[ len - 1 ] = 0;
+ con->close = 1;
+
+/* If the word starts with an opening parenthesis, replace the parenthesis
+ with a null character and set a flag indicating that the next word
+ returned should consist of just a closing parenthesis. */
+ } else if( result[ 0 ] == '(' ) {
+ con->wnext = ( (char *) result ) + 1;
+ con->f = *(con->wnext);
+ *(con->wnext) = 0;
+ }
+ }
+
+/* If we have run out of input words, but we have not yet finished
+ interpreting the previous word returned, return a null string, rather
+ than a null pointer in order to allow further interpretation of the
+ previous word. */
+ } else if( ! con->done ) {
+ result = "";
+ }
+ }
+
+/* Return the pointer to the next word. */
+ return result;
+}
+
+static int GetRegionProps( AstStcsChan *this, AstRegion *spreg,
+ AstKeyMap *spprops, int nspace, int defdigs,
+ double scale, int issky, int *status ) {
+/*
+* Name:
+* GetRegionProps
+
+* Purpose:
+* Create STC-S properties to describe a given Region and store in a
+* KeyMap.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* int GetRegionProps( AstStcsChan *this, AstRegion *spreg,
+* AstKeyMap *spprops, int *status )
+
+* Class Membership:
+* StcsChan member function
+
+* Description:
+* This function creates a set of STC-S properties to describe the
+* supplied spatial (2D) Region, and stores them in the supplied KeyMap.
+
+* Parameters:
+* this
+* The StcsChan being used.
+* spreg
+* The 2-D spatial Region to be described.
+* spprops
+* A KeyMap in which to store the created properties.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Returns the integer code for the spatial region, or NULL_ID if the
+* properties could not be created for any reason.
+
+*/
+
+
+/* Local Variables: */
+ AstKeyMap *new_props; /* KeyMap holding component Region properties */
+ AstMapping *sreg; /* Simplified Region */
+ AstRegion **reg_list; /* Array of component Regioon pointers */
+ char *prop; /* Formatted property string */
+ char buf[ 100 ]; /* Buffer for formatted values */
+ char fmt[ 10 ]; /* Buffer for format specifier */
+ double *p; /* Pointer to next axis value */
+ double *points; /* Pointer to array of Region axis values */
+ double a; /* Circle or ellipse radius */
+ double angle; /* Ellipse position angle */
+ double b; /* Ellipse radius */
+ double centre[ 3 ]; /* Circle or ellipse centre */
+ double lbnd[ 3 ]; /* Region lower bounds */
+ double ubnd[ 3 ]; /* Region upper bounds */
+ int i; /* Loop index */
+ int j; /* Loop index */
+ int nc; /* Number of characters in "prop" string */
+ int np; /* Number of points defining the Region */
+ int nreg; /* Number of component Regions */
+ int ok; /* Can the Region be written out? */
+ int oper; /* Code for CmpRegion boolean operator */
+ int spaceid; /* Identifier for STC-S spatial region type */
+
+/* Check inherited status */
+ if( !astOK ) return NULL_ID;
+
+/* Initialise */
+ spaceid = NULL_ID;
+ ok = 1;
+ prop = NULL;
+
+/* If the Region has been negated, temporarily negate the Region, and
+ write its properties into a new KeyMap by calling this function
+ recursively. Then store the new KeyMap in the supplied KeyMap. */
+ if( astGetNegated( spreg ) ) {
+ spaceid = NOT_ID;
+ astNegate( spreg );
+ new_props = astKeyMap( " ", status );
+
+ if( GetRegionProps( this, spreg, new_props, nspace, defdigs,
+ scale, issky, status ) == NULL_ID ) ok = 0;
+
+ astMapPut0C( spprops, "ID", "Not", NULL );
+ astMapPut0A( spprops, "REGION1", new_props, NULL );
+ astMapPut0I( spprops, "NREG", 1, NULL );
+ astNegate( spreg );
+
+/* Store properties that are specific to AllSky sub-phrases (i.e. none)... */
+ } else if( astIsANullRegion( spreg ) && astGetNegated( spreg ) ) {
+ spaceid = ALLSKY_ID;
+ astMapPut0C( spprops, "ID", "AllSky", NULL );
+
+/* Store properties that are specific to Circle sub-phrases... */
+ } else if( astIsACircle( spreg ) ) {
+ spaceid = CIRCLE_ID;
+ astMapPut0C( spprops, "ID", "Circle", NULL );
+
+/* Get the geometric parameters of the Circle. */
+ astCirclePars( spreg, centre, &a, NULL );
+
+/* Create a string holding the formatted centre axis values, scaling
+ to the required units. Use the Frame's Digits attribute to specify
+ how many digits to use when formatting the axis values. */
+ nc = 0;
+ for( i = 0; i < nspace; i++ ) {
+ if( centre[ i ] != AST__BAD ) {
+ GetFmt( "CENTRE", spprops, i, defdigs, fmt, status );
+ (void) sprintf( buf, fmt, scale*centre[ i ] );
+ prop = astAppendString( prop, &nc, buf );
+ prop = astAppendString( prop, &nc, " " );
+
+ } else {
+ ok = 0;
+ astAddWarning( this, 1, "The supplied Circle contains "
+ "one or more bad centre axis values.",
+ "astWrite", status );
+ break;
+ }
+ }
+
+/* Remove the trailing space, and store the property value in the KeyMap. */
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( spprops, "CENTRE", prop, NULL );
+
+/* Scale, format and store the radius. */
+ if( a != AST__BAD ) {
+ GetFmt( "RADIUS", spprops, 0, defdigs, fmt, status );
+ (void) sprintf( buf, fmt, scale*a );
+ astMapPut0C( spprops, "RADIUS", buf, NULL );
+ } else {
+ ok = 0;
+ astAddWarning( this, 1, "The supplied Circle has an "
+ "undefined radius.", "astWrite", status );
+ }
+
+/* Store properties that are specific to PositionInterval sub-phrases... */
+ } else if( astIsAInterval( spreg ) || astIsABox( spreg ) ) {
+ spaceid = POSITION_INTERVAL_ID;
+ astMapPut0C( spprops, "ID", "PositionInterval", NULL );
+
+/* Get the bounds of the Region. */
+ astGetRegionBounds( spreg, lbnd, ubnd );
+
+/* Create a string holding the formatted low limits, scaling to the
+ required units. Use the Frame's Digits attribute to specify how
+ many digits to use when formatting the axis values. */
+ nc = 0;
+ for( i = 0; i < nspace; i++ ) {
+ if( lbnd[ i ] == AST__BAD || lbnd[ i ] == DBL_MAX ||
+ lbnd[ i ] == -DBL_MAX ) {
+ astAddWarning( this, 1, "Spatial axis %d has an undefined "
+ "lower limit.", "astWrite", status, i + 1 );
+ ok = 0;
+ break;
+ } else {
+ GetFmt( "LOLIMIT", spprops, i, defdigs, fmt, status );
+ (void) sprintf( buf, fmt, scale*lbnd[ i ] );
+ prop = astAppendString( prop, &nc, buf );
+ prop = astAppendString( prop, &nc, " " );
+ }
+ }
+
+/* Remove the trailing space, and store the property value in the KeyMap. */
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( spprops, "LOLIMIT", prop, NULL );
+
+/* Do the same for the upper limits. */
+ nc = 0;
+ for( i = 0; i < nspace; i++ ) {
+ if( ubnd[ i ] == AST__BAD || ubnd[ i ] == DBL_MAX ||
+ ubnd[ i ] == -DBL_MAX ) {
+ astAddWarning( this, 1, "Spatial axis %d has an undefined "
+ "upper limit.", "astWrite", status, i + 1 );
+ ok = 0;
+ break;
+ } else {
+ GetFmt( "HILIMIT", spprops, i, defdigs, fmt, status );
+ (void) sprintf( buf, fmt, scale*ubnd[ i ] );
+ prop = astAppendString( prop, &nc, buf );
+ prop = astAppendString( prop, &nc, " " );
+ }
+ }
+
+/* Remove the trailing space, and store the property value in the KeyMap. */
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( spprops, "HILIMIT", prop, NULL );
+
+/* Store properties that are specific to Ellipse sub-phrases... */
+ } else if( astIsAEllipse( spreg ) ) {
+ spaceid = ELLIPSE_ID;
+ astMapPut0C( spprops, "ID", "Ellipse", NULL );
+
+/* Get the geometric parameters of the Ellipse. */
+ astEllipsePars( spreg, centre, &a, &b, &angle, NULL, NULL );
+
+/* Create a string holding the formatted centre axis values, scaling
+ to the required units. Use the Frame's Digits attribute to specify
+ how many digits to use when formatting the axis values. */
+ nc = 0;
+ for( i = 0; i < nspace; i++ ) {
+ if( centre[ i ] != AST__BAD ) {
+ GetFmt( "CENTRE", spprops, i, defdigs, fmt, status );
+ (void) sprintf( buf, fmt, scale*centre[ i ] );
+ prop = astAppendString( prop, &nc, buf );
+ prop = astAppendString( prop, &nc, " " );
+
+ } else {
+ ok = 0;
+ astAddWarning( this, 1, "The supplied Ellipse contains "
+ "one or more bad centre axis values.",
+ "astWrite", status );
+ break;
+ }
+ }
+
+/* Remove the trailing space, and store the property value in the KeyMap. */
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( spprops, "CENTRE", prop, NULL );
+
+/* Scale, format and store the two radii. */
+ if( a != AST__BAD && b != AST__BAD && angle != AST__BAD ) {
+ GetFmt( "RADIUS1", spprops, 0, defdigs, fmt, status );
+ (void) sprintf( buf, fmt, scale*a );
+ astMapPut0C( spprops, "RADIUS1", buf, NULL );
+
+ GetFmt( "RADIUS2", spprops, 0, defdigs, fmt, status );
+ (void) sprintf( buf, fmt, scale*b );
+ astMapPut0C( spprops, "RADIUS2", buf, NULL );
+
+/* Convert the angle to degrees in the direction required by STC-S,
+ format and store. */
+ angle *= AST__DR2D;
+ if( !issky ) angle = 90 - angle;
+ while( angle < 0.0 ) angle += 360.0;
+ while( angle >= 360.0 ) angle -= 360.0;
+
+ GetFmt( "POSANGLE", spprops, 0, defdigs, fmt, status );
+ (void) sprintf( buf, fmt, angle );
+ astMapPut0C( spprops, "POSANGLE", buf, NULL );
+
+ } else {
+ astAddWarning( this, 1, "The gemeotric parameters of the "
+ "supplied Ellipse are undefined.",
+ "astWrite", status );
+ ok = 0;
+ }
+
+/* Store properties that are specific to Polygon sub-phrases... */
+ } else if( astIsAPolygon( spreg ) ) {
+ spaceid = POLYGON_ID;
+ astMapPut0C( spprops, "ID", "Polygon", NULL );
+
+/* Get an array holding the axis values at the polygon vertices. */
+ astGetRegionPoints( spreg, 0, 0, &np, NULL );
+ points = astMalloc( sizeof( double )*np*nspace );
+ astGetRegionPoints( spreg, np, nspace, &np, points );
+
+/* Create a string holding the formatted vertex axis values, scaling
+ to the required units. Use the Frame's Digits attribute to specify
+ how many digits to use when formatting the axis values. */
+ GetFmt( "VERTICES", spprops, 0, defdigs, fmt, status );
+ nc = 0;
+ for( j = 0; j < np; j++ ) {
+ p = points + j;
+ for( i = 0; i < nspace; i++ ) {
+ if( *p != AST__BAD ) {
+ (void) sprintf( buf, fmt, scale*(*p) );
+ prop = astAppendString( prop, &nc, buf );
+ prop = astAppendString( prop, &nc, " " );
+ p += np;
+ } else {
+ astAddWarning( this, 1, "The supplied Polygon contains "
+ "one or more bad axis values.", "astWrite",
+ status );
+ ok = 0;
+ break;
+ }
+ }
+ }
+
+/* Remove the trailing space, and store the property value in the KeyMap. */
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( spprops, "VERTICES", prop, NULL );
+
+/* Free resources. */
+ points = astFree( points );
+
+/* Store properties that are specific to Position sub-phrases... */
+ } else if( astIsAPointList( spreg ) ) {
+ spaceid = POSITION_ID;
+ astMapPut0C( spprops, "ID", "Position", NULL );
+
+/* Check the PointList contains only a single point. */
+ astGetRegionPoints( spreg, 0, 0, &np, NULL );
+ if( np > 1 ) {
+ astAddWarning( this, 1, "The supplied PointList contains "
+ "more than one position.", "astWrite", status );
+ ok = 0;
+
+/* If so, get the axis values at the point. */
+ } else {
+ astGetRegionPoints( spreg, 1, nspace, &np, centre );
+
+/* Create a string holding the formatted axis values, scaling to the
+ required units. Use the Frame's Digits attribute to specify how many
+ digits to use when formatting the axis values. */
+ nc = 0;
+ for( i = 0; i < nspace; i++ ) {
+ if( centre[ i ] != AST__BAD ) {
+ GetFmt( "POSITION", spprops, i, defdigs, fmt, status );
+ (void) sprintf( buf, fmt, scale*centre[ i ] );
+ prop = astAppendString( prop, &nc, buf );
+ prop = astAppendString( prop, &nc, " " );
+
+ } else {
+ astAddWarning( this, 1, "The supplied PointList contains "
+ "one or more bad axis values.", "astWrite",
+ status );
+ ok = 0;
+ break;
+ }
+ }
+
+/* Remove the trailing space, and store the property value in the KeyMap. */
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( spprops, "POSITION", prop, NULL );
+ }
+
+/* Store properties that are specific to compound Position sub-phrases... */
+ } else {
+
+/* If the Region is not a CmpRegion (e.g. a Prism?) see if simplifying it
+ produces a CmpRegion. */
+ if( !astIsACmpRegion( spreg ) ) {
+ sreg = astSimplify( spreg );
+ } else {
+ sreg = astClone( spreg );
+ }
+
+/* If we now have a CmpRegion, write its properties into a new KeyMap by
+ calling this function recursively. Then store the new KeyMap in the
+ supplied KeyMap. */
+ if( astIsACmpRegion( sreg ) ) {
+
+/* Get the list of Regions that the CmpRegion combines together. This
+ also returns the boolean operator with which they are combined. */
+ nreg = 0;
+ reg_list = NULL;
+ oper = astCmpRegionList( (AstCmpRegion *) sreg, &nreg, &reg_list );
+
+/* Store compound region type in the supplied KeyMap. */
+ if( oper == AST__AND ) {
+ spaceid = INTERSECTION_ID;
+ astMapPut0C( spprops, "ID", "Intersection", NULL );
+ } else if( oper == AST__OR ) {
+ spaceid = UNION_ID;
+ astMapPut0C( spprops, "ID", "Union", NULL );
+ } else {
+ spaceid = DIFFERENCE_ID;
+ astMapPut0C( spprops, "ID", "Difference", NULL );
+ }
+
+/* Loop round each of the combined Regions. */
+ for( i = 0; i < nreg; i++ ) {
+
+/* Create a new KeyMap, and then call this function recursively to store
+ the properties of the i'th component Region in the new KeyMap. */
+ if( ok ) {
+ new_props = astKeyMap( " ", status );
+ if( GetRegionProps( this, reg_list[ i ], new_props, nspace,
+ defdigs, scale, issky, status )
+ == NULL_ID ) ok = 0;
+
+/* Store the new KeyMap in the supplied KeyMap. */
+ sprintf( buf, "REGION%d", i + 1 );
+ astMapPut0A( spprops, buf, new_props, NULL );
+
+/* Free resources. */
+ new_props = astAnnul( new_props );
+ }
+ reg_list[ i ] = astAnnul( reg_list[ i ] );
+ }
+ reg_list = astFree( reg_list );
+ astMapPut0I( spprops, "NREG", nreg, NULL );
+
+/* All other classes of Region are unsupported. */
+ } else {
+ astAddWarning( this, 1, "The supplied %s cannot be written "
+ "out since STC-S does not support %s regions.",
+ "astWrite", status, astGetClass( spreg ),
+ astGetClass( spreg ) );
+ ok = 0;
+ }
+
+/* Free resources. */
+ sreg = astAnnul( sreg );
+ }
+
+ if( prop ) prop = astFree( prop );
+
+/* If an error has occurred, return NULL_ID. */
+ if( !ok || !astOK ) spaceid = NULL_ID;
+
+/* Return the identifier for the STC-S spatial region type. */
+ return spaceid;
+}
+
+void astInitStcsChanVtab_( AstStcsChanVtab *vtab, const char *name, int *status ) {
+/*
+*+
+* Name:
+* astInitStcsChanVtab
+
+* Purpose:
+* Initialise a virtual function table for an StcsChan.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "stcschan.h"
+* void astInitStcsChanVtab( AstStcsChanVtab *vtab, const char *name )
+
+* Class Membership:
+* StcsChan vtab initialiser.
+
+* Description:
+* This function initialises the component of a virtual function
+* table which is used by the StcsChan class.
+
+* Parameters:
+* vtab
+* Pointer to the virtual function table. The components used by
+* all ancestral classes will be initialised if they have not already
+* been initialised.
+* name
+* Pointer to a constant null-terminated character string which contains
+* the name of the class to which the virtual function table belongs (it
+* is this pointer value that will subsequently be returned by the Object
+* astClass function).
+*-
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstObjectVtab *object; /* Pointer to Object component of Vtab */
+ AstChannelVtab *channel; /* Pointer to Channel component of Vtab */
+
+/* Check the local error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Initialize the component of the virtual function table used by the
+ parent class. */
+ astInitChannelVtab( (AstChannelVtab *) vtab, name );
+
+/* Store a unique "magic" value in the virtual function table. This
+ will be used (by astIsAStcsChan) to determine if an object belongs
+ to this class. We can conveniently use the address of the (static)
+ class_check variable to generate this unique value. */
+ vtab->id.check = &class_check;
+ vtab->id.parent = &(((AstChannelVtab *) vtab)->id);
+
+/* Initialise member function pointers. */
+/* ------------------------------------ */
+
+ vtab->ClearStcsArea = ClearStcsArea;
+ vtab->GetStcsArea = GetStcsArea;
+ vtab->SetStcsArea = SetStcsArea;
+ vtab->TestStcsArea = TestStcsArea;
+
+ vtab->ClearStcsCoords = ClearStcsCoords;
+ vtab->GetStcsCoords = GetStcsCoords;
+ vtab->SetStcsCoords = SetStcsCoords;
+ vtab->TestStcsCoords = TestStcsCoords;
+
+ vtab->ClearStcsProps = ClearStcsProps;
+ vtab->GetStcsProps = GetStcsProps;
+ vtab->SetStcsProps = SetStcsProps;
+ vtab->TestStcsProps = TestStcsProps;
+
+ vtab->SetStcsLength = SetStcsLength;
+ vtab->ClearStcsLength = ClearStcsLength;
+ vtab->TestStcsLength = TestStcsLength;
+ vtab->GetStcsLength = GetStcsLength;
+
+/* Save the inherited pointers to methods that will be extended, and
+ replace them with pointers to the new member functions. */
+ object = (AstObjectVtab *) vtab;
+ channel = (AstChannelVtab *) vtab;
+
+ parent_clearattrib = object->ClearAttrib;
+ object->ClearAttrib = ClearAttrib;
+ parent_getattrib = object->GetAttrib;
+ object->GetAttrib = GetAttrib;
+ parent_setattrib = object->SetAttrib;
+ object->SetAttrib = SetAttrib;
+ parent_testattrib = object->TestAttrib;
+ object->TestAttrib = TestAttrib;
+
+ channel->Write = Write;
+ channel->Read = Read;
+
+ parent_getindent = channel->GetIndent;
+ channel->GetIndent = GetIndent;
+
+/* Declare the Dump function for this class. There is no destructor or
+ copy constructor. */
+ astSetDump( vtab, Dump, "StcsChan", "STC-S I/O Channel" );
+
+/* If we have just initialised the vtab for the current class, indicate
+ that the vtab is now initialised, and store a pointer to the class
+ identifier in the base "object" level of the vtab. */
+ if( vtab == &class_vtab ) {
+ class_init = 1;
+ astSetVtabClassIdentifier( vtab, &(vtab->id) );
+ }
+}
+
+static AstRegion *MakeSpaceRegion( AstKeyMap *props, AstFrame *frm,
+ double scale, int *status ){
+/*
+* Name:
+* MakeSpaceRegion
+
+* Purpose:
+* Create a Region to describe the space coverage of the STC-S
+* description being read.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* AstRegion *MakeSpaceRegion( AstKeyMap *props, AstFrame *frm,
+* double scale, int *status )
+
+* Class Membership:
+* StcsChan member function
+
+* Description:
+* This function returns a pointer to a new Region that describes the
+* spatial coverage of an STC-S description.
+
+* Parameters:
+* props
+* A KeyMap holding properties read from the STC-S space sub-phrase.
+* frm
+* The Frame in which the Region is to be defined.
+* scale
+* A factor that must be applied to the raw axis values read from the
+* STC-S description in order to convert them into the units used by
+* the supplied Frame.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The Region pointer.
+
+*/
+
+
+/* Local Variables: */
+ AstKeyMap *reg_props; /* KeyMap holding argument properties */
+ AstRegion *reg; /* Current argument Region */
+ AstRegion *result; /* Returned Region */
+ AstRegion *tmp; /* Temporary Region pointer */
+ char key[ 20 ]; /* Key for argument region */
+ const char *id; /* Sub-phrase identifier */
+ double *p; /* Pointer to next axis value */
+ double *temp; /* Pointer to array of reordered polygon vertex axis values */
+ double *vertices; /* Pointer to array of polygon vertex axis values */
+ double val1; /* Scalar value read from KeyMap */
+ double val2; /* Scalar value read from KeyMap */
+ double val3; /* Scalar value read from KeyMap */
+ double vec1[ 10 ]; /* Vector read from KeyMap */
+ double vec2[ 10 ]; /* Vector read from KeyMap */
+ int iaxis; /* Axis index */
+ int ireg; /* Index of argument regions */
+ int ivert; /* Vertex index */
+ int naxes; /* Number of spatial axes */
+ int nreg; /* Number of argument regions */
+ int nval; /* Number of values read from KeyMap */
+ int nvert; /* Number of vertices */
+ int spaceid; /* Integer identifier for spatial shape */
+ int oper; /* Boolean operator code for CmpRegion */
+
+/* Initialise */
+ result = NULL;
+
+/* Check inherited status */
+ if( !astOK ) return result;
+
+/* Temporarily ensure that an error is reported if an attempt is made to
+ access a non-existent KeyMap entry. */
+ astSetKeyError( props, 1 );
+
+/* Get the space sub-phrase identifier from the properties KeyMap, and
+ find the corresponding integer identifier. */
+
+ astMapGet0C( props, "ID", &id );
+ spaceid = SpaceId( id, status );
+
+/* Get the number of axes in the Frame. */
+ naxes = astGetNaxes( frm );
+
+/* Create a suitable Region to enclose the space positions. This
+ includes scaling the supplied axis values to the units used by
+ the Frame. */
+ if( spaceid == POSITION_INTERVAL_ID ) {
+ astMapGet1D( props, "DLOLIMIT", naxes, &nval, vec1 );
+ astMapGet1D( props, "DHILIMIT", naxes, &nval, vec2 );
+
+ for( iaxis = 0; iaxis < naxes; iaxis++ ) {
+ vec1[ iaxis ] *= scale;
+ vec2[ iaxis ] *= scale;
+ }
+
+ result = (AstRegion *) astBox( frm, 1, vec1, vec2, NULL, " ", status );
+
+ } else if( spaceid == ALLSKY_ID ) {
+ result = (AstRegion *) astNullRegion( frm, NULL, "Negated=1", status );
+
+ } else if( spaceid == CIRCLE_ID ) {
+ astMapGet1D( props, "DCENTRE", naxes, &nval, vec1 );
+ astMapGet0D( props, "RADIUS", &val1 );
+ for( iaxis = 0; iaxis < naxes; iaxis++ ) vec1[ iaxis ] *= scale;
+ val1 *= scale;
+ result = (AstRegion *) astCircle( frm, 1, vec1, &val1, NULL, " ",
+ status );
+
+ } else if( spaceid == ELLIPSE_ID ) {
+ astMapGet1D( props, "DCENTRE", naxes, &nval, vec1 );
+ astMapGet0D( props, "RADIUS1", &val1 );
+ astMapGet0D( props, "RADIUS2", &val2 );
+ astMapGet0D( props, "POSANGLE", &val3 );
+ for( iaxis = 0; iaxis < naxes; iaxis++ ) vec1[ iaxis ] *= scale;
+ vec2[ 0 ] = val1*scale;
+ vec2[ 1 ] = val2*scale;
+ if( !astIsASkyFrame( frm ) ) val3 = 90.0 - val3;
+ val3 *= AST__DD2R;
+ result = (AstRegion *) astEllipse( frm, 1, vec1, vec2, &val3, NULL, " ",
+ status );
+
+ } else if( spaceid == BOX_ID ) {
+ astMapGet1D( props, "DCENTRE", naxes, &nval, vec1 );
+ astMapGet1D( props, "DBSIZE", naxes, &nval, vec2 );
+
+ for( iaxis = 0; iaxis < naxes; iaxis++ ) {
+ vec1[ iaxis ] *= scale;
+ vec2[ iaxis ] *= scale;
+ }
+
+ vertices = BoxCorners( frm, vec1, vec2, status );
+ result = (AstRegion *) astPolygon( frm, 4, 4, vertices, NULL, " ",
+ status );
+ vertices = astFree( vertices );
+
+ } else if( spaceid == POLYGON_ID ) {
+ nval = astMapLength( props, "DVERTICES" );
+ temp = astMalloc( sizeof( double )*nval );
+ astMapGet1D( props, "DVERTICES", nval, &nval, temp );
+
+/* An STC-S polygon description holds the vertex axis values in the wrong
+ order for the AstPolygon constructor. Therefore, transpose the temp
+ array (scale them at the same time). */
+ vertices = astMalloc( sizeof( double )*nval );
+ if( astOK ) {
+ nvert = nval/naxes;
+ p = temp;
+ for( ivert = 0; ivert < nvert; ivert++ ) {
+ for( iaxis = 0; iaxis < naxes; iaxis++,p++ ) {
+ vertices[ iaxis*nvert + ivert ] = *p*scale;
+ }
+ }
+
+ result = (AstRegion *) astPolygon( frm, nvert, nvert, vertices, NULL,
+ " ", status );
+ }
+
+ vertices = astFree( vertices );
+ temp = astFree( temp );
+
+ } else if( spaceid == POSITION_ID ) {
+ astMapGet1D( props, "DPOSITION", naxes, &nval, vec1 );
+ for( iaxis = 0; iaxis < naxes; iaxis++ ) vec1[ iaxis ] *= scale;
+ result = (AstRegion *) SinglePointList( frm, vec1, NULL, status );
+
+ } else if( spaceid == CONVEX_ID ) {
+ astError( AST__INTER, "astRead(StcsChan): No support for Convex in "
+ "MakeSpaceRegion (internal AST programming error).", status );
+
+/* All remaining valid space id values are compound - their arguments are held
+ within separate KeyMaps nested inside the supplied KeyMap. */
+ } else if( spaceid != NULL_ID ) {
+
+/* The number of arguments is defined in the NREG entry. */
+ astMapGet0I( props, "NREG", &nreg );
+
+/* Get the CmpRegion operator code. */
+ if( spaceid == UNION_ID ) {
+ oper = AST__OR;
+ } else if( spaceid == INTERSECTION_ID ) {
+ oper = AST__AND;
+ } else if( spaceid == DIFFERENCE_ID ) {
+ oper = AST__XOR;
+ } else {
+ oper = 0; /* To avoid compiler warnings */
+ }
+
+/* Loop over all argument Regions. */
+ for( ireg = 0; ireg < nreg; ireg++ ) {
+
+/* Get the KeyMap holding the STC-S properties of the current argument
+ region. */
+ sprintf( key, "REGION%d", ireg + 1 );
+ astMapGet0A( props, key, &reg_props );
+
+/* Construct an AST Region from this list of STC-S properties. */
+ reg = MakeSpaceRegion( reg_props, frm, scale, status );
+
+/* If we are creating a "Not" element, just negate the argument region
+ and return it. */
+ if( spaceid == NOT_ID ) {
+ astNegate( reg );
+ result = astClone( reg );
+
+/* If we are creating a "Union", "Difference" or "Intersection" element,
+ combine the first two arguments into a CmpRegion, and then add in each
+ subsequent argument. */
+ } else {
+ if( ireg == 0 ) {
+ result = astClone( reg );
+ } else {
+ tmp = (AstRegion *) astCmpRegion( result, reg, oper, " ",
+ status );
+ (void) astAnnul( result );
+ result = tmp;
+ }
+ }
+
+/* Free resources */
+ reg = astAnnul( reg );
+ reg_props = astAnnul( reg_props );
+ }
+ }
+
+/* Ensure that no error is reported if an attempt is made to access a
+ non-existent KeyMap entry. */
+ astSetKeyError( props, 0 );
+
+/* Return the Region. */
+ return result;
+}
+
+static void MapPut0C( AstKeyMap *km, const char *key, const char *value,
+ const char *def, int defs, int *status ){
+/*
+* Name:
+* MapPut0C
+
+* Purpose:
+* Store a text STC-S property in the supplied keymap.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* void MapPut0C( AstKeyMap *km, const char *key, const char *value,
+* const char *def, int defs, int *status )
+
+* Class Membership:
+* StcsChan member function.
+
+* Description:
+* This function stors the supplied value in the given KeyMap,
+* handling default values.
+
+* Parameters:
+* km
+* Pointer to the KeyMap in which to store the value.
+* key
+* Pointer to a string holding the property name associated with
+* the value.
+* value
+* The property value. If this is NULL then the function
+* returns without action.
+* def
+* The default property value.
+* defs
+* If zero, then the value is not stored in the KeyMap if the value
+* is equal to the default value.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Check the inherited status */
+ if( !astOK ) return;
+
+/* If the value is NULL, ignore the entry. */
+ if( value ) {
+
+/* If the value is equal to the default value, and we are NOT storing
+ default values, ensure the KeyMap has no entry for the given key. */
+ if( astChrMatch( value, def ) && !defs ) {
+ astMapRemove( km, key );
+
+/* Otherwise, store the value. */
+ } else {
+ astMapPut0C( km, key, value, NULL );
+ }
+ }
+}
+
+static void MapPut0D( AstKeyMap *km, const char *key, double value, double def,
+ int defs, int *status ){
+/*
+* Name:
+* MapPut0D
+
+* Purpose:
+* Store a floating point STC-S property in the supplied keymap.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* void MapPut0D( AstKeyMap *km, const char *key, double value, double def,
+* int defs, int *status )
+
+* Class Membership:
+* StcsChan member function.
+
+* Description:
+* This function stors the supplied value in the given KeyMap,
+* handling default values.
+
+* Parameters:
+* km
+* Pointer to the KeyMap in which to store the value.
+* key
+* Pointer to a string holding the property name associated with
+* the value.
+* value
+* The property value. If this is AST__BAD then the function
+* returns without action.
+* def
+* The default property value.
+* defs
+* If zero, then the value is not stored in the KeyMap if the value
+* is equal to the default value.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Check the inherited status */
+ if( !astOK ) return;
+
+/* If the value is bad, ignore the entry. */
+ if( value != AST__BAD ) {
+
+/* If the value is equal to the default value, and we are NOT storing
+ default values, ensure the KeyMap has no entry for the given key. */
+ if( value == def && !defs ) {
+ astMapRemove( km, key );
+
+/* Otherwise, store the value. */
+ } else {
+ astMapPut0D( km, key, value, NULL );
+ }
+ }
+}
+
+static char *PutRegionProps( AstStcsChan *this, AstKeyMap *km, const char *id,
+ int indent, char *line, int *nc, int *crem,
+ int linelen, int *status ){
+/*
+* Name:
+* PutRegionProps
+
+* Purpose:
+* Append STC-S space sub-phrase properties to the end of a string.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* char *PutRegionProps( AstStcsChan *this, AstKeyMap *km, const char *id,
+* int indent, char *line, int *nc, int *crem,
+* int linelen, int *status )
+
+* Class Membership:
+* StcsChan member function
+
+* Description:
+* This function converts the STC-S properties for the space sub-phrase
+* supplied in a KeyMap into text, and appends them to the supplied
+* line of text in the order required by STC-S.
+*
+* It is assumed that the sub-phrase identifier has already been put
+* into the string.
+
+* Parameters:
+* this
+* The StcsChan.
+* km
+* Pointer to a KeyMap containing the STC-S properties.
+* id
+* Pointer to the sub-phrase identifier.
+* indent
+* If greater than or equal to zero, then it gives the number of
+* spaces indentation to place before the first word (also indicates
+* that a new-line should follow the last word of the argument). If
+* negative, never use indentation.
+* line
+* Pointer to the buffer to receive the property values.
+* nc
+* Pointer to an int in which to store the number of characaters in
+* the buffer. Updated on exit.
+* crem
+* Pointer to an int in which to store the maximum number of
+* characters before a new line. Ignored if zero. Updated on exit.
+* linelen
+* The maximum number of character per line, or zero if all text is
+* to be included in a single line.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the buffer. This will usually be "line", but may be
+* different to "line" if it was necessary to expand the memory to make
+* room for new properties.
+
+*/
+
+/* Local Variables: */
+ AstKeyMap *reg_props;
+ char *result;
+ char key[ 20 ];
+ int i;
+ int ireg;
+ int nreg;
+ int spaceid;
+
+/* Initialise */
+ result = line;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Temporarily ensure that an error is reported if an attempt is made to
+ access a non-existent KeyMap entry. */
+ astSetKeyError( km, 1 );
+
+/* Get the integer code for the space sub-phrase identifier. */
+ spaceid = SpaceId( id, status );
+
+/* Do each type of space sub-phrase. */
+ if( spaceid == NULL_ID ) {
+ astError( AST__INTER, "astWrite(StcsChan): Illegal 'spaceid' value "
+ "in function PutRegionProps (internal AST programming "
+ "error).", status );
+
+ } else if( spaceid == POSITION_INTERVAL_ID ) {
+ result = AddItem( this, km, "LOLIMIT", NULL, result, nc, crem, linelen, status );
+ result = AddItem( this, km, "HILIMIT", NULL, result, nc, crem, linelen, status );
+
+ } else if( spaceid == ALLSKY_ID ) {
+
+ } else if( spaceid == CIRCLE_ID ) {
+ result = AddItem( this, km, "CENTRE", NULL, result, nc, crem, linelen, status );
+ result = AddItem( this, km, "RADIUS", NULL, result, nc, crem, linelen, status );
+
+ } else if( spaceid == ELLIPSE_ID ) {
+ result = AddItem( this, km, "CENTRE", NULL, result, nc, crem, linelen, status );
+ result = AddItem( this, km, "RADIUS1", NULL, result, nc, crem, linelen, status );
+ result = AddItem( this, km, "RADIUS2", NULL, result, nc, crem, linelen, status );
+ result = AddItem( this, km, "POSANGLE", NULL, result, nc, crem, linelen, status );
+
+ } else if( spaceid == BOX_ID ) {
+ result = AddItem( this, km, "CENTRE", NULL, result, nc, crem, linelen, status );
+ result = AddItem( this, km, "BSIZE", NULL, result, nc, crem, linelen, status );
+
+ } else if( spaceid == POLYGON_ID ) {
+ result = AddItem( this, km, "VERTICES", NULL, result, nc, crem, linelen, status );
+
+ } else if( spaceid == CONVEX_ID ) {
+ astError( AST__INTER, "astWrite(StcsChan): No Convex support yet "
+ "(internal AST programming error).", status );
+
+ } else if( spaceid == POSITION_ID ) {
+ result = AddItem( this, km, "POSITION", NULL, result, nc, crem, linelen, status );
+
+/* All remaining space id values are compound regions. */
+ } else {
+
+/* Append an opening parenthesis. */
+ result = astAppendString( result, nc, "( " );
+
+/* If required, write out the text through the Channel sink function,
+ and start a new line. */
+ if( indent >= 0 ) {
+ astPutNextText( this, result );
+ *nc = 0;
+ *crem = linelen;
+ }
+
+/* Set the indentation for the next level down. */
+ if( indent == 0 ) {
+ indent = 6;
+ } else if( indent > 0 ){
+ indent += 3;
+ }
+
+/* Loop round all argument Regions. */
+ astMapGet0I( km, "NREG", &nreg );
+ for( ireg = 0; ireg < nreg; ireg++ ) {
+ sprintf( key, "REGION%d", ireg + 1 );
+ astMapGet0A( km, key, &reg_props );
+
+/* Put any required indentation at the start of the line. */
+ if( indent > 0 ) {
+ for( i = 0; i < indent; i++ ) {
+ result = astAppendString( result, nc, " " );
+ }
+ *crem -= indent;
+ }
+
+/* Append the identifier for the next argument to the string. */
+ result = AddItem( this, reg_props, "ID", NULL, result, nc, crem,
+ linelen, status );
+
+/* Append the arguments to the string. */
+ astMapGet0C( reg_props, "ID", &id );
+ result = PutRegionProps( this, reg_props, id, indent, result, nc,
+ crem, linelen, status );
+
+/* Write the text out to the sink function, and start a new line. */
+ if( indent > 0 ) {
+ astPutNextText( this, result );
+ *nc = 0;
+ *crem = linelen;
+ }
+
+/* Free resources. */
+ reg_props = astAnnul( reg_props );
+ }
+
+/* Decrease any indentation, and then append a closing parenthesis. */
+ if( indent > 2 ) {
+ indent -= 3;
+ for( i = 0; i < indent; i++ ) {
+ result = astAppendString( result, nc, " " );
+ }
+ }
+ result = astAppendString( result, nc, ") " );
+
+/* If we are about to return fomr the top-level, start a new line. */
+ if( indent > 0 && indent < 6 ) {
+ astPutNextText( this, result );
+ *nc = 0;
+ for( i = 0; i < indent; i++ ) {
+ result = astAppendString( result, nc, " " );
+ }
+ *crem = linelen - indent;
+ }
+ }
+
+/* Ensure that no error is reported if an attempt is made to access a
+ non-existent KeyMap entry. */
+ astSetKeyError( km, 0 );
+
+/* Return the buffer pointer. */
+ return result;
+}
+
+static AstObject *Read( AstChannel *this_channel, int *status ) {
+/*
+* Name:
+* Read
+
+* Purpose:
+* Read an Object from a Channel.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* AstObject *Read( AstChannel *this_channel, int *status )
+
+* Class Membership:
+* StcsChan member function (over-rides the astRead method
+* inherited from the Channel class).
+
+* Description:
+* This function reads an Object from an StcsChan.
+
+* Parameters:
+* this
+* Pointer to the StcsChan.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Object.
+*/
+
+/* Local Variables: */
+ AstFrame *spacefrm; /* Pointer to SpaceFrame for space sub-phrase */
+ AstFrameSet *fs; /* Temporary FrameSet */
+ AstKeyMap *full_props; /* KeyMap holding all sub-phrase properties */
+ AstKeyMap *props; /* KeyMap holding current sub-phrase properties */
+ AstObject *new; /* Pointer to returned Object */
+ AstObject *obj; /* Pointer to Object extracted from a KeyMap */
+ AstPrism *tr; /* Temporary Region pointer */
+ AstRegion *full_co; /* Region describing full coord position */
+ AstRegion *full_enc; /* Region describing full enclosure */
+ AstRegion *red_co; /* Region describing red-shift coord */
+ AstRegion *red_enc; /* Region describing red-shift enclosure */
+ AstRegion *space_co; /* Region describing space coord */
+ AstRegion *space_enc; /* Region describing space enclosure */
+ char **words; /* Array of pointers to individual words */
+ int nword; /* Number of words returned */
+ AstRegion *spec_co; /* Region describing spectral coord */
+ AstRegion *spec_enc; /* Region describing spectral enclosure */
+ AstRegion *time_co; /* Region describing time coord */
+ AstRegion *time_enc; /* Region describing time enclosure */
+ AstSpecFrame *redfrm; /* Pointer to SpecFrame for redshift sub-phrase */
+ AstSpecFrame *specfrm; /* Pointer to SpecFrame for spectral sub-phrase */
+ AstStcsChan *this; /* Pointer to the StcsChan structure */
+ AstStdOfRestType sor; /* Standard of rest */
+ AstSystemType sys; /* Frame System attribute value */
+ AstTimeFrame *tf1; /* Temporary TimeFrame */
+ AstTimeFrame *timefrm; /* Pointer to TimeFrame for time sub-phrase */
+ AstTimeScaleType ts; /* TimeFrame TimeScale attribute value */
+ WordContext con; /* Context for finding next source word */
+ char *fbuf; /* Pointer to buffer holding document fragment */
+ const char *new_ts; /* Time scale string */
+ double epoch; /* Value to use for the Epoch attribue */
+ double fill; /* Filling factor */
+ double hilim; /* Axis upper limit */
+ double lolim; /* Axis lower limit */
+ double scale; /* Units scaling factor */
+ int nval; /* No. of values read from KeyMap */
+ double vals[ 10 ]; /* Values read from KeyMap */
+ double start; /* Start time */
+ double stop; /* Stop time */
+ double time; /* Time value */
+ double time_origin; /* Value to use as TimeFrame TimeOrigin*/
+ double value; /* Axis value */
+ int iaxis; /* Axis index */
+ int is_skyframe; /* Is the space frame a SkyFrame? */
+ int level; /* Warning reporting level */
+ int naxes; /* No. of space Frame axes */
+ int nwant; /* Number of objects to return */
+ int use_co; /* Do we have a full coordinate position? */
+ int use_enc; /* Do we have a full enclosure? */
+ int want_co; /* Is the Coordinates component wanted? */
+ int want_enc; /* Is the enclosure region wanted? */
+ int want_props; /* Are the STC-S properties wanted? */
+ const char *cval; /* Pointer to property value */
+ const char *type; /* Type of redshift axis */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Obtain a pointer to the StcsChan structure. */
+ this = (AstStcsChan *) this_channel;
+
+/* Initialise. */
+ epoch = AST__BAD;
+ start = AST__BAD;
+ stop = AST__BAD;
+ time = AST__BAD;
+ time_co = NULL;
+ time_enc = NULL;
+ space_co = NULL;
+ space_enc = NULL;
+ spacefrm = NULL;
+ spec_co = NULL;
+ spec_enc = NULL;
+ red_co = NULL;
+ red_enc = NULL;
+ use_co = 1;
+ use_enc = 0;
+ scale = 1.0;
+
+/* Read the STC-S description from the external source, parse it, and
+ create a KeyMap containing the parsed property values. */
+ full_props = ReadProps( this, status );
+
+/* If the STC-S description contained a time sub-phrase, get the KeyMap
+ containing the proprties of the time sub-phrase, and then create AST
+ Regions describing the time coordinate value and its enclosing Region. */
+ if( astMapGet0A( full_props, "TIME_PROPS", &obj ) ) {
+ props = (AstKeyMap *) obj;
+
+/* Create the default TimeFrame */
+ timefrm = astTimeFrame( " ", status );
+
+/* Get the TIMESCALE property from the KeyMap, and identify the corresponding
+ AST TimeScale. */
+ ts = AST__BADTS;
+ new_ts = NULL;
+ level = 3;
+
+ if( astMapGet0C( props, "TIMESCALE", &cval ) ) {
+
+ if( astChrMatch( cval, "TT" ) ) {
+ ts = AST__TT;
+
+ } else if( astChrMatch( cval, "TDT" ) ) {
+ ts = AST__TT;
+ new_ts = "TT";
+
+ } else if( astChrMatch( cval, "ET" ) ) {
+ ts = AST__TT;
+ new_ts = "TT";
+
+ } else if( astChrMatch( cval, "TAI" ) ) {
+ ts = AST__TAI;
+
+ } else if( astChrMatch( cval, "IAT" ) ) {
+ ts = AST__TAI;
+ new_ts = "TAI";
+
+ } else if( astChrMatch( cval, "UTC" ) ) {
+ ts = AST__UTC;
+
+ } else if( astChrMatch( cval, "TEB" ) ) {
+ ts = AST__TDB;
+ new_ts = "TDB";
+ level = 1;
+
+ } else if( astChrMatch( cval, "TDB" ) ) {
+ ts = AST__TDB;
+
+ } else if( astChrMatch( cval, "TCG" ) ) {
+ ts = AST__TCG;
+
+ } else if( astChrMatch( cval, "TCB" ) ) {
+ ts = AST__TCB;
+
+ } else if( astChrMatch( cval, "LST" ) ) {
+ ts = AST__LMST;
+
+ } else if( astChrMatch( cval, "nil" ) ) {
+ astAddWarning( this, 2, "Time scale defaulting to 'TAI'.",
+ "astRead", status );
+
+ } else if( astOK ){
+ astError( AST__BADIN, "astRead(StcsChan): Unknown time scale '%s'.",
+ status, cval );
+ }
+
+ } else {
+ astAddWarning( this, 2, "Time scale defaulting to 'TAI'.",
+ "astRead", status );
+ }
+
+/* Issue a warning if a different time-scale was substituted for the supplied
+ time-scale. */
+ if( new_ts ) {
+ astAddWarning( this, level, "AST does not support the '%s' time "
+ "scale. The '%s' timescale is being used instead.",
+ "astRead", status, cval, new_ts );
+ }
+
+/* If we got a time scale, set the TimeScale attribute in the TimeFrame
+ to the same value. */
+ if( ts != AST__BADTS ) astSetTimeScale( timefrm, ts );
+
+/* The AST TimeFrame class has no reference position, so allow any reference
+ position but issue a warning for anything other than "TOPOCENTER" and
+ "UNKNOWNRefPos". */
+ if( !astMapGet0C( props, "REFPOS", &cval ) ) cval = "UNKNOWNRefPos";
+ if( !astChrMatch( cval, "TOPOCENTER" ) ) {
+ astAddWarning( this, 1, "AST only supports topocentric time frames, "
+ "so 'TOPOCENTER' will be used in place of '%s'.",
+ "astRead", status, cval );
+ }
+
+/* Get the times describes by the time sub-phrase as MJD values. */
+ astMapGet0D( props, "MJDSTART", &start );
+ astMapGet0D( props, "MJDTIME", &time );
+ astMapGet0D( props, "MJDSTOP", &stop );
+
+/* Get the earliest time represented by the time sub-phrase. We use this
+ as the TimeOrigin for the TimeFrame, and also as the Epoch for all
+ frames. */
+ time_origin = start;
+ if( time_origin == AST__BAD ) time_origin = time;
+ if( time_origin == AST__BAD ) time_origin = stop;
+ epoch = time_origin;
+
+/* Store the TimeOrigin value in the TimeFrame, modifying the time values
+ accordingly. */
+ if( time_origin != AST__BAD ) {
+ astSetTimeOrigin( timefrm, time_origin );
+ if( start != AST__BAD ) start -= time_origin;
+ if( stop != AST__BAD ) stop -= time_origin;
+ if( time != AST__BAD ) time -= time_origin;
+ }
+
+/* Convert the epoch to TDB. */
+ if( epoch != AST__BAD && ts != AST__TDB ) {
+ tf1 = astCopy( timefrm );
+ astSetTimeScale( tf1, AST__TDB );
+ fs = astConvert( timefrm, tf1, "" );
+ astTran1( fs, 1, &epoch, 1, &epoch );
+ fs = astAnnul( fs );
+ tf1 = astAnnul( tf1 );
+ }
+
+/* Store the epoch value in the TimeFrame. */
+ if( epoch != AST__BAD ) astSetEpoch( timefrm, epoch );
+
+/* Create a suitable Region to describe the enclosure for the time coords */
+ if( start != AST__BAD || stop != AST__BAD ) {
+ time_enc = (AstRegion *) astInterval( timefrm, &start, &stop,
+ NULL, "", status );
+ use_enc = 1;
+ }
+
+/* Create a suitable Region to describe the time coords contained within
+ the above enclosure. */
+ if( time != AST__BAD ) {
+ time_co = (AstRegion *) SinglePointList( (AstFrame *) timefrm,
+ &time, NULL, status);
+ } else {
+ use_co = 0;
+ }
+
+/* If no enclosure Region was created for the time sub-phrase, use a
+ copy of any coordinate region. This is because each sub-phrase needs
+ to have an enclosure of some sort if they are to be combined in parallel
+ into an enclose for the whole CmpFrame. */
+ if( ! time_enc && time_co ) time_enc = astCopy( time_co );
+
+/* Set the filling factor. */
+ if( time_enc && astMapGet0D( props, "FILLFACTOR", &fill ) ) {
+ astSetFillFactor( time_enc, fill );
+ }
+
+/* Get the units in which the time error values are given, and get the
+ scaling factor that converts them into days. */
+ if( astMapGet0C( props, "UNIT", &cval ) ) {
+ if( !strcmp( cval, "s" ) ) {
+ scale = 1.0/86400.0;
+
+ } else if( !strcmp( cval, "d" ) ) {
+ scale = 1.0;
+
+ } else if( !strcmp( cval, "a" ) ) {
+ scale = 365.25;
+
+ } else if( !strcmp( cval, "yr" ) ) {
+ scale = 365.25;
+
+ } else if( !strcmp( cval, "cy" ) ) {
+ scale = 36525.0;
+
+ } else if( astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Unsupported "
+ "units (%s) for the time axis within an "
+ "STC-S description.", status, cval );
+ }
+
+ } else {
+ scale = 1.0/86400.0;
+ }
+
+/* Associate an uncertainty with the two Regions. */
+ if( astMapGet1D( props, "DERROR", 2, &nval, vals ) ) {
+ if( nval > 1 ) {
+ astAddWarning( this, 1, "An STC-S time sub-phrase contains an "
+ "Error range. AST does not support error ranges "
+ "so the mid value will be used as the error.",
+ "astRead", status );
+ vals[ 0 ] = 0.5*( vals[ 0 ] + vals[ 1 ] );
+ }
+
+ SetUnc( time_enc, time_co, (AstFrame *) timefrm, 0, scale, vals, 1,
+ status );
+ }
+
+/* Free resources */
+ props = astAnnul( props );
+ timefrm = astAnnul( timefrm );
+ }
+
+/* If the STC-S description contained a space sub-phrase, get the KeyMap
+ containing the proprties of the space sub-phrase, and then create AST
+ Regions describing the spatial position and its enclosing Region. */
+ if( astMapGet0A( full_props, "SPACE_PROPS", &obj ) ) {
+ props = (AstKeyMap *) obj;
+
+/* The class of Frame (SkyFrame or basic Frame) is determined by the
+ "FLAVOR". */
+ is_skyframe = 0;
+ if( astMapGet0C( props, "FLAVOUR", &cval ) ) {
+
+ if( astChrMatch( cval, "SPHER2" ) ) {
+ spacefrm = (AstFrame *) astSkyFrame( "", status );
+ is_skyframe = 1;
+
+ } else if( astChrMatch( cval, "CART1" ) ) {
+ spacefrm = astFrame( 1, "", status );
+
+ } else if( astChrMatch( cval, "CART2" ) ) {
+ spacefrm = astFrame( 2, "", status );
+
+ } else if( astChrMatch( cval, "CART3" ) ) {
+ spacefrm = astFrame( 3, "", status );
+
+ } else {
+ astError( AST__BADIN, "astRead(StcsChan): Unsupported "
+ "space 'Flavor' (%s) found in STC-S description.",
+ status, cval );
+ }
+
+ } else {
+ spacefrm = (AstFrame *) astSkyFrame( "", status );
+ is_skyframe = 1;
+ }
+
+/* Consider each supported space frame. Report an error for frames
+ not supported by AST. */
+ if( astMapGet0C( props, "FRAME", &cval ) ) {
+ if( astChrMatch( cval, "ICRS" ) ) {
+ sys = AST__ICRS;
+
+ } else if( astChrMatch( cval, "FK5" ) ) {
+ sys = AST__FK5;
+
+ } else if( astChrMatch( cval, "FK4" ) ) {
+ sys = AST__FK4;
+
+ } else if( astChrMatch( cval, "J2000" ) ) {
+ sys = AST__FK5;
+
+ } else if( astChrMatch( cval, "B1950" ) ) {
+ sys = AST__FK4;
+
+ } else if( astChrMatch( cval, "ECLIPTIC" ) ) {
+ sys = AST__ECLIPTIC;
+
+ } else if( astChrMatch( cval, "GALACTIC" ) ) {
+ sys = AST__GALACTIC;
+
+ } else if( astChrMatch( cval, "GALACTIC_II" ) ) {
+ sys = AST__GALACTIC;
+
+ } else if( astChrMatch( cval, "SUPER_GALACTIC" ) ) {
+ sys = AST__SUPERGALACTIC;
+
+ } else if( astChrMatch( cval, "UNKNOWNFrame" ) ) {
+ sys = AST__UNKNOWN;
+
+ } else {
+ sys = AST__UNKNOWN;
+ astAddWarning( this, 1, "'UNKNOWNFrame' being used in place of "
+ "unsupported frame '%s' in an STC-S description.",
+ "astRead", status, cval );
+ }
+
+ } else {
+ cval = "UNKNOWNFrame";
+ sys = AST__UNKNOWN;
+ astAddWarning( this, 1, "Space frame defaulting to 'UNKNOWNFrame' "
+ "in an STC-S description.", "astRead", status );
+ }
+
+/* We can set the System (only needed for SkyFrames). */
+ if( is_skyframe ) {
+ astSetSystem( spacefrm, sys );
+
+/* If we have a basic Frame, set the Domain equal to the STC-S frame value. */
+ } else {
+ astSetDomain( spacefrm, cval );
+ }
+
+/* Set the epoch of the space frame. */
+ if( epoch != AST__BAD ) astSetEpoch( spacefrm, epoch );
+
+/* The AST Frame and SkyFrame class has no reference position, so for
+ SkyFrames we consider "TOPOCENTER" and "UNKNOWN" acceptable and all
+ other unsupported. For other Frames we allow any reference position. */
+ if( !astMapGet0C( props, "REFPOS", &cval ) ) cval = "UNKNOWNRefPos";
+ if( is_skyframe && !astChrMatch( cval, "TOPOCENTER" ) ) {
+ astAddWarning( this, 1, "AST only supports topocentric sky frames, "
+ "so 'TOPOCENTER' will be used in place of '%s'.",
+ "astRead", status, cval );
+ }
+
+/* Get the number of spatial axes. */
+ naxes = astGetNaxes( spacefrm );
+
+/* Get the units strings. */
+ if( !astMapGet0C( props, "UNIT", &cval ) ) {
+ if( is_skyframe ) {
+ cval = "deg";
+ } else {
+ cval = "m";
+ }
+ }
+
+/* In AST, SkyFrames always use radians, so set up a scaling factor to
+ convert supplied axis values into radians. */
+ if( is_skyframe ) {
+
+ if( !strcmp( cval, "deg" ) || !strcmp( cval, "deg deg" ) ) {
+ scale = AST__DD2R;
+
+ } else if( !strcmp( cval, "arcmin" ) || !strcmp( cval, "arcmin arcmin" ) ) {
+ scale = AST__DD2R/60.0;
+
+ } else if( !strcmp( cval, "arcsec" ) || !strcmp( cval, "arcsec arcsec" ) ) {
+ scale = AST__DD2R/3600.0;
+
+ } else if( astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Unsupported "
+ "units (%s) for a spherical co-ordinate system "
+ "within an STC-S description: '%s'.", status,
+ cval, ContextFragment( &con, &fbuf, status ) );
+ }
+
+/* Basic Frames can use any of the allowed units, so use a scale factor of
+ 1.0. Also set the active unit flag in the space frame to enable intelligent
+ units conversion by astConvert etc. */
+ } else {
+ scale = 1.0;
+ astSetActiveUnit( spacefrm, 1 );
+
+/* Basic Frames can have different units on different axes. So split the
+ units property up into separate words. */
+ words = astChrSplit( cval, &nword );
+
+/* Set values for the Unit attributes of the Frame. Replicate the last
+ supplied unit string for any extra axes. */
+ for( iaxis = 0; iaxis < naxes; iaxis++ ) {
+ if( iaxis < nword ) {
+ astSetUnit( spacefrm, iaxis, words[ iaxis ] );
+ } else {
+ astSetUnit( spacefrm, iaxis, words[ nword - 1 ] );
+ }
+ }
+
+/* Free resources. */
+ for( iaxis = 0; iaxis < nword; iaxis++ ) {
+ words[ iaxis ] = astFree( words[ iaxis ] );
+ }
+ words = astFree( words );
+ }
+
+/* Create a suitable Region to enclose the space positions. This
+ includes scaling the supplied axis values to the units used by
+ the Frame. */
+ space_enc = MakeSpaceRegion( props, spacefrm, scale, status );
+ if( space_enc ) use_enc = 1;
+
+/* Create a suitable Region to describe the space coords contained within
+ the above enclosure. If any sub-phrase has no coordinate value, then
+ we cannot produce a PointList describing the complete coordinate set. */
+ if( astMapGet1D( props, "DPOSITION", naxes, &nval, vals ) ) {
+ for( iaxis = 0; iaxis < nval; iaxis++ ) vals[ iaxis ] *= scale;
+ space_co = (AstRegion *) SinglePointList( spacefrm, vals, NULL,
+ status);
+ } else {
+ use_co = 0;
+ }
+
+/* If no enclosure Region was created for the space sub-phrase, use a
+ copy of any coordinate region. This is because each sub-phrase needs
+ to have an enclosure of some sort if they are to be combined in parallel
+ into an enclose for the whole CmpFrame. */
+ if( ! space_enc && space_co ) space_enc = astCopy( space_co );
+
+/* Set the filling factor. */
+ if( space_enc && astMapGet0D( props, "FILLFACTOR", &fill ) ) {
+ astSetFillFactor( space_enc, fill );
+ }
+
+/* Associate an uncertainty with the two Regions. */
+ if( astMapGet1D( props, "DERROR", 2*naxes, &nval, vals ) ) {
+ if( nval > naxes ) {
+ astAddWarning( this, 1, "An STC-S space sub-phrase contains an "
+ "Error range. AST does not support error ranges "
+ "so the mid value will be used as the error.",
+ "astRead", status );
+ for( iaxis = 0; iaxis < naxes; iaxis++ ) {
+ vals[ iaxis ] = 0.5*( vals[ iaxis ] + vals[ iaxis + naxes ] );
+ }
+
+/* If insufficient error values have been supplied, replicate the last
+ one. */
+ } else {
+ for( iaxis = nval; iaxis < naxes; iaxis++ ) {
+ vals[ iaxis ] = vals[ nval - 1 ];
+ }
+ }
+
+/* Set the uncertainty in the two space regions. */
+ SetUnc( space_enc, space_co, (AstFrame *) spacefrm, is_skyframe,
+ scale, vals, naxes, status );
+ }
+
+/* Free resources */
+ props = astAnnul( props );
+ spacefrm = astAnnul( spacefrm );
+ }
+
+
+
+/* If the STC-S description contained a velocity sub-phrase, issue a
+ warning. */
+ if( astMapGet0A( full_props, "VELOCITY_PROPS", &obj ) ) {
+ astAddWarning( this, 1, "Ignoring a velocity sub-phrase found in "
+ "an STC-S description.", "astRead", status );
+ obj = astAnnul( obj );
+ }
+
+
+/* If the STC-S description contained a spectral sub-phrase, get the KeyMap
+ containing the proprties of the spectral sub-phrase, and then create AST
+ Regions describing the spectral coordinate value and its enclosing Region. */
+ if( astMapGet0A( full_props, "SPECTRAL_PROPS", &obj ) ) {
+ props = (AstKeyMap *) obj;
+
+/* Create the default SpecFrame */
+ specfrm = astSpecFrame( " ", status );
+
+/* Get the REFPOS property from the KeyMap, and identify the corresponding
+ AST StdOfRest. */
+ sor = AST__BADSOR;
+ if( astMapGet0C( props, "REFPOS", &cval ) ) {
+
+ if( astChrMatch( cval, "GEOCENTER" ) ) {
+ sor = AST__GESOR;
+
+ } else if( astChrMatch( cval, "BARYCENTER" ) ) {
+ sor = AST__BYSOR;
+
+ } else if( astChrMatch( cval, "HELIOCENTER" ) ) {
+ sor = AST__HLSOR;
+
+ } else if( astChrMatch( cval, "TOPOCENTER" ) ) {
+ sor = AST__TPSOR;
+
+ } else if( astChrMatch( cval, "LSR" ) ||
+ astChrMatch( cval, "LSRK" ) ) {
+ sor = AST__LKSOR;
+
+ } else if( astChrMatch( cval, "LSRD" ) ) {
+ sor = AST__LDSOR;
+
+ } else if( astChrMatch( cval, "GALACTIC_CENTER" ) ) {
+ sor = AST__GLSOR;
+
+ } else {
+ astAddWarning( this, 1, "Using 'HELIOCENTER' in place of "
+ "unsupported spectral reference position '%s' "
+ "found in an STC-S description.", "astRead",
+ status, cval );
+ }
+
+ } else {
+ astAddWarning( this, 2, "Spectral reference position defaulting to "
+ "'HELIOCENTER' in an STC-S description.", "astRead",
+ status );
+ }
+
+/* If we got a ref pos, set the StdOfRest attribute in the SpecFrame. */
+ if( sor != AST__BADSOR ) astSetStdOfRest( specfrm, sor );
+
+/* Get the units. */
+ if( !astMapGet0C( props, "UNIT", &cval ) ) cval = "Hz";
+
+
+/* Set the spectral system implied by the unit string. */
+ if( !cval || !strcmp( cval, "Hz" ) || !strcmp( cval, "MHz" ) ||
+ !strcmp( cval, "GHz" ) ) {
+ astSetSystem( specfrm, AST__FREQ );
+
+ } else if( !strcmp( cval, "m" ) || !strcmp( cval, "mm" ) ||
+ !strcmp( cval, "um" ) || !strcmp( cval, "nm" ) ||
+ !strcmp( cval, "Angstrom" ) ) {
+ astSetSystem( specfrm, AST__WAVELEN );
+
+ } else if( !strcmp( cval, "eV" ) || !strcmp( cval, "keV" ) ||
+ !strcmp( cval, "MeV" ) ) {
+ astSetSystem( specfrm, AST__ENERGY );
+
+ } else if( astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Unsupported spectral "
+ "units (%s) found within an STC-S description.",
+ status, cval );
+ }
+
+/* Set the units. */
+ astSetUnit( specfrm, 0, cval );
+
+/* Set the epoch */
+ if( epoch != AST__BAD ) astSetEpoch( specfrm, epoch );
+
+/* Create a suitable Region to describe the enclosure for the spectral
+ coords */
+ if( astMapGet0D( props, "LOLIMIT", &lolim ) ) {
+ astMapGet0D( props, "HILIMIT", &hilim );
+ spec_enc = (AstRegion *) astInterval( specfrm, &lolim, &hilim,
+ NULL, "", status );
+ use_enc = 1;
+ }
+
+/* Create a suitable Region to describe the spectral coords contained within
+ the above enclosure. If any sub-phrase has no coordinate value, then
+ we cannot produce a PointList describing the complete coordinate set. */
+ if( astMapGet0D( props, "SPECTRAL", &value ) ) {
+ spec_co = (AstRegion *) SinglePointList( (AstFrame *) specfrm,
+ &value, NULL, status);
+ } else {
+ use_co = 0;
+ }
+
+/* If no enclosure Region was created for the spectral sub-phrase, use a
+ copy of any coordinate region. This is because each sub-phrase needs
+ to have an enclosure of some sort if they are to be combined in parallel
+ into an enclose for the whole CmpFrame. */
+ if( ! spec_enc && spec_co ) spec_enc = astCopy( spec_co );
+
+/* Set the filling factor. */
+ if( spec_enc && astMapGet0D( props, "FILLFACTOR", &fill ) ) {
+ astSetFillFactor( spec_enc, fill );
+ }
+
+
+/* Associate an uncertainty with the two Regions. */
+ if( astMapGet1D( props, "DERROR", 2, &nval, vals ) ) {
+ if( nval > 1 ) {
+ astAddWarning( this, 1, "An STC-S spectral sub-phrase contains an "
+ "Error range. AST does not support error ranges "
+ "so the mid value will be used as the error.",
+ "astRead", status );
+ vals[ 0 ] = 0.5*( vals[ 0 ] + vals[ 1 ] );
+ }
+
+ SetUnc( spec_enc, spec_co, (AstFrame *) specfrm, 0, 1.0, vals, 1,
+ status );
+ }
+
+/* Free resources */
+ props = astAnnul( props );
+ specfrm = astAnnul( specfrm );
+ }
+
+
+
+
+/* If the STC-S description contained a redshift sub-phrase, get the KeyMap
+ containing the properties of the redshift sub-phrase, and then create AST
+ Regions describing the redshift coordinate value and its enclosing Region. */
+ if( astMapGet0A( full_props, "REDSHIFT_PROPS", &obj ) ) {
+ props = (AstKeyMap *) obj;
+
+/* Create the default SpecFrame */
+ redfrm = astSpecFrame( "Domain=REDSHIFT", status );
+
+/* Get the REFPOS property from the KeyMap, and identify the corresponding
+ AST StdOfRest. */
+ sor = AST__BADSOR;
+ if( astMapGet0C( props, "REFPOS", &cval ) ) {
+
+ if( astChrMatch( cval, "GEOCENTER" ) ) {
+ sor = AST__GESOR;
+
+ } else if( astChrMatch( cval, "BARYCENTER" ) ) {
+ sor = AST__BYSOR;
+
+ } else if( astChrMatch( cval, "HELIOCENTER" ) ) {
+ sor = AST__HLSOR;
+
+ } else if( astChrMatch( cval, "TOPOCENTER" ) ) {
+ sor = AST__TPSOR;
+
+ } else if( astChrMatch( cval, "LSR" ) ||
+ astChrMatch( cval, "LSRK" ) ) {
+ sor = AST__LKSOR;
+
+ } else if( astChrMatch( cval, "LSRD" ) ) {
+ sor = AST__LDSOR;
+
+ } else if( astChrMatch( cval, "GALACTIC_CENTER" ) ) {
+ sor = AST__GLSOR;
+
+ } else {
+ astAddWarning( this, 1, "Using 'HELIOCENTER' in place of "
+ "unsupported redshift reference position '%s' "
+ "found in an STC-S description.", "astRead",
+ status, cval );
+ }
+
+ } else {
+ astAddWarning( this, 2, "Redshift reference position defaulting to "
+ "'HELIOCENTER' in an STC-S description.", "astRead",
+ status );
+ }
+
+/* If we got a ref pos, set the StdOfRest attribute in the SpecFrame. */
+ if( sor != AST__BADSOR ) astSetStdOfRest( redfrm, sor );
+
+/* Get the redshift type. */
+ if( !astMapGet0C( props, "TYPE", &type ) ) type = "REDSHIFT";
+
+/* Now get the velocity definition, and set the equivalent SpecFrame
+ System value. AST only supports optical redshift, so report an error
+ or a warning for unsupported combinations. */
+ if( astMapGet0C( props, "DOPPLERDEF", &cval ) ){
+
+ if( astChrMatch( cval, "OPTICAL" ) ) {
+ if( astChrMatch( type, "VELOCITY" ) ){
+ astSetSystem( redfrm, AST__VOPTICAL );
+ } else {
+ astSetSystem( redfrm, AST__REDSHIFT );
+ }
+
+ } else if( astChrMatch( cval, "RADIO" ) ) {
+ if( astChrMatch( type, "VELOCITY" ) ){
+ astSetSystem( redfrm, AST__VRADIO );
+ } else {
+ astSetSystem( redfrm, AST__REDSHIFT );
+ astAddWarning( this, 1, "STC-S RADIO redshift not supported. "
+ "Assuming OPTICAL redshift instead.", "astRead",
+ status );
+ }
+
+ } else if( astChrMatch( cval, "RELATIVISTIC" ) ) {
+ if( astChrMatch( type, "VELOCITY" ) ){
+ astSetSystem( redfrm, AST__VREL );
+ } else {
+ astSetSystem( redfrm, AST__REDSHIFT );
+ astAddWarning( this, 1, "STC-S RELATIVISTIC redshift not supported. "
+ "Assuming OPTICAL redshift instead.", "astRead",
+ status );
+ }
+
+ } else {
+ if( astChrMatch( type, "VELOCITY" ) ){
+ astSetSystem( redfrm, AST__VOPTICAL );
+ astAddWarning( this, 1, "Doppler velocity definition defaulting"
+ " to 'OPTICAL' in an STC-S description.",
+ "astRead", status );
+
+ } else {
+ astSetSystem( redfrm, AST__REDSHIFT );
+ }
+ }
+ }
+
+/* Set the units. */
+ if( astChrMatch( type, "VELOCITY" ) ){
+ if( astMapGet0C( props, "UNIT", &cval ) ) {
+ astSetUnit( redfrm, 0, cval );
+ } else {
+ astSetUnit( redfrm, 0, "km/s" );
+ }
+
+ } else if( astMapGet0C( props, "UNIT", &cval ) ) {
+ astAddWarning( this, 1, "Ignoring units (%s) specified for REDSHIFT "
+ "in an STC-S description.", "astRead", status, cval );
+ }
+
+/* Set the epoch */
+ if( epoch != AST__BAD ) astSetEpoch( redfrm, epoch );
+
+/* Create a suitable Region to describe the enclosure for the redshift
+ coords */
+ if( astMapGet0D( props, "LOLIMIT", &lolim ) ) {
+ astMapGet0D( props, "HILIMIT", &hilim );
+ red_enc = (AstRegion *) astInterval( redfrm, &lolim, &hilim,
+ NULL, "", status );
+ use_enc = 1;
+ }
+
+/* Create a suitable Region to describe the redshift coords contained within
+ the above enclosure. If any sub-phrase has no coordinate value, then
+ we cannot produce a PointList describing the complete coordinate set. */
+ if( astMapGet0D( props, "REDSHIFT", &value ) ) {
+ red_co = (AstRegion *) SinglePointList( (AstFrame *) redfrm,
+ &value, NULL, status);
+ } else {
+ use_co = 0;
+ }
+
+/* If no enclosure Region was created for the redshift sub-phrase, use a
+ copy of any coordinate region. This is because each sub-phrase needs
+ to have an enclosure of some sort if they are to be combined in parallel
+ into an enclose for the whole CmpFrame. */
+ if( ! red_enc && red_co ) red_enc = astCopy( red_co );
+
+/* Set the filling factor. */
+ if( red_enc && astMapGet0D( props, "FILLFACTOR", &fill ) ) {
+ astSetFillFactor( red_enc, fill );
+ }
+
+/* Associate an uncertainty with the two Regions. */
+ if( astMapGet1D( props, "DERROR", 2, &nval, vals ) ) {
+ if( nval > 1 ) {
+ astAddWarning( this, 1, "An STC-S redshift sub-phrase contains an "
+ "Error range. AST does not support error ranges "
+ "so the mid value will be used as the error.",
+ "astRead", status );
+ vals[ 0 ] = 0.5*( vals[ 0 ] + vals[ 1 ] );
+ }
+
+ SetUnc( red_enc, red_co, (AstFrame *) redfrm, 0, 1.0, vals, 1,
+ status );
+ }
+
+/* Free resources */
+ props = astAnnul( props );
+ redfrm = astAnnul( redfrm );
+ }
+
+/* If a particular position was specified by the STC_S document, create the
+ full position from the individual sub-phrase position */
+ if( use_co ) {
+ new = time_co ? astClone( time_co ) : NULL;
+
+ if( space_co ) {
+ if( new ) {
+ tr = astPrism( new, space_co, "", status );
+ (void) astAnnul( new );
+ new = (AstObject *) tr;
+ } else {
+ new = astClone( space_co );
+ }
+ }
+
+ if( spec_co ) {
+ if( new ) {
+ tr = astPrism( new, spec_co, "", status );
+ (void) astAnnul( new );
+ new = (AstObject *) tr;
+ } else {
+ new = astClone( spec_co );
+ }
+ }
+
+ if( red_co ) {
+ if( new ) {
+ tr = astPrism( new, red_co, "", status );
+ (void) astAnnul( new );
+ new = (AstObject *) tr;
+ } else {
+ new = astClone( red_co );
+ }
+ }
+
+ if( new ) {
+ full_co = astSimplify( new );
+ new = astAnnul( new );
+ } else {
+ full_co = NULL;
+ }
+
+ } else {
+ full_co = NULL;
+ }
+
+/* If an enclosing volume was specified by the STC_S document, create the
+ full enclosure Region from the individual sub-phrase enclosure Regions. */
+ if( use_enc ) {
+ new = time_enc ? astClone( time_enc ) : NULL;
+
+ if( space_enc ) {
+ if( new ) {
+ tr = astPrism( new, space_enc, "", status );
+ (void) astAnnul( new );
+ new = (AstObject *) tr;
+ } else {
+ new = astClone( space_enc );
+ }
+ }
+
+ if( spec_enc ) {
+ if( new ) {
+ tr = astPrism( new, spec_enc, "", status );
+ (void) astAnnul( new );
+ new = (AstObject *) tr;
+ } else {
+ new = astClone( spec_enc );
+ }
+ }
+
+ if( red_enc ) {
+ if( new ) {
+ tr = astPrism( new, red_enc, "", status );
+ (void) astAnnul( new );
+ new = (AstObject *) tr;
+ } else {
+ new = astClone( red_enc );
+ }
+ }
+ full_enc = astSimplify( new );
+ new = astAnnul( new );
+
+ } else {
+ full_enc = NULL;
+ }
+
+/* See which, and how many, items are to be returned. */
+ nwant = 0;
+ if( ( want_enc = astGetStcsArea( this ) ) ) nwant++;
+ if( ( want_co = astGetStcsCoords( this ) ) ) nwant++;
+ if( ( want_props = astGetStcsProps( this ) ) ) nwant++;
+
+/* If one, and only one, of the three items is to be returned, return it. */
+ new = NULL;
+ if( nwant == 1 ) {
+ if( want_enc && full_enc ) {
+ new = astClone( full_enc );
+ } else if( want_co && full_co ) {
+ new = astClone( full_co );
+ } else if( want_props && full_props ){
+ new = astClone( full_props );
+ }
+
+/* If more than one item is to be returned, put them into a KeyMap and
+ return the KeyMap. */
+ } else if( nwant > 1 ) {
+ new = (AstObject *) astKeyMap( " ", status );
+ if( want_enc && full_enc ) astMapPut0A( new, "AREA", full_enc, NULL );
+ if( want_co && full_co ) astMapPut0A( new, "COORDS", full_co, NULL );
+ if( want_props && full_props ) astMapPut0A( new, "PROPS", full_props, NULL );
+
+/* Report an error if nothing is to be returned. */
+ } else if( astOK ){
+ astError( AST__ATTIN, "astRead(StcsChan): The StcsArea, StcsCoords "
+ "and StcsProps attributes indicate that nothing is to be "
+ "returned (possible programming error).", status );
+ }
+
+/* Free resources */
+ if( space_enc ) space_enc = astAnnul( space_enc );
+ if( spec_enc ) spec_enc = astAnnul( spec_enc );
+ if( time_enc ) time_enc = astAnnul( time_enc );
+ if( red_enc ) red_enc = astAnnul( red_enc );
+ if( space_co ) space_co = astAnnul( space_co );
+ if( spec_co ) spec_co = astAnnul( spec_co );
+ if( time_co ) time_co = astAnnul( time_co );
+ if( red_co ) red_co = astAnnul( red_co );
+ if( full_enc ) full_enc = astAnnul( full_enc );
+ if( full_co ) full_co = astAnnul( full_co );
+ if( full_props ) full_props = astAnnul( full_props );
+
+/* If an error occurred, clean up by deleting the new Object and
+ return a NULL pointer. */
+ if ( !astOK ) new = astDelete( new );
+
+/* Return the pointer to the new Object. */
+ return new;
+}
+
+static AstKeyMap *ReadProps( AstStcsChan *this, int *status ) {
+/*
+* Name:
+* ReadProps
+
+* Purpose:
+* Read STC-S properties from the source and store in a KeyMap.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* AstKeyMap *ReadProps( AstStcsChan *this, int *status )
+
+* Class Membership:
+* StcsChan member function
+
+* Description:
+* This function parses the list of space-separated words read from the
+* source function, identifies the purpose of each word within the STC-S
+* description, and stores the words in a returned KeyMap.
+
+* Parameters:
+* this
+* Pointer to the StcsChan.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new KeyMap. This will contain up to five entries
+* with any or all of the following keys: TIME_PROPS, SPACE_PROPS,
+* VELOCITY_PROPS, SPECTRAL_PROPS, REDSHIFT_PROPS. If an entry is absent,
+* it means the STC-S description did not contain the corresponding
+* sub-phrase. The value associated with each of these entries will be a
+* KeyMap. These will contain values for the sub-phrase proprties read
+* from the STC-S description. Properties that are not specified in
+* the STC-S description will not be present in the KeyMap. The values
+* stored in the KeyMap are the words read form the STC-S description
+* without any conversion or other processing.
+*/
+
+/* Local Constants: */
+#define MAXVAL 6
+
+/* Local Variables: */
+ AstKeyMap *props; /* KeyMap holding current sub-phrase properties */
+ AstKeyMap *result; /* Returned KeyMap holding all properties */
+ AstTimeFrame *timefrm; /* Used for unformatting ISO date-times */
+ WordContext con; /* Context for finding next source word */
+ char *fbuf; /* Pointer to buffer holding document fragment */
+ char *prop; /* String holding complete property value */
+ const char *subphrase; /* Name of current sub phrase */
+ const char *t; /* Temporary character string pointer */
+ const char *word; /* Pointer to next source word */
+ double val[ MAXVAL ]; /* Array of numerical property values */
+ double start; /* Start time (MJD) */
+ double stop; /* Stop time (MJD) */
+ double time; /* Time value (MJD) */
+ double value; /* Axis value */
+ int iaxis; /* Axis index */
+ int is_jd; /* Is time value a JD rather than an MJD? */
+ int nunit; /* Number of units strings supplied */
+ int nval; /* Number of numerical values read */
+ int naxes; /* No. of space Frame axes */
+ int nc; /* Number of characters written to string */
+ int new_word; /* Get a new word at the end of the pass? */
+ int redid; /* Redshift sub-phrase component identifier */
+ int spaceid; /* Space sub-phrase component identifier */
+ int specid; /* Spectral sub-phrase component identifier */
+ int timeid; /* Time sub-phrase component identifier */
+ int velid; /* Velocity sub-phrase component identifier */
+
+/* The stage reached in the parsing of the STC-S description is indicated
+ by the "look_for" variable. This variable is allowed the following
+ values, indicating the item that is to be checked for next. */
+ enum look_for_type {
+ ERROR,
+ FILL_FACTOR,
+ FLAVOUR,
+ FRAME,
+ LIMITS,
+ PIX_SIZE,
+ POSITION,
+ POSITION_INTERVAL,
+ REDSHIFT_IDENTIFIER,
+ RED_SPEC_LABEL,
+ RED_SPEC_VALUE,
+ REFPOS,
+ RESOLUTION,
+ SIZE,
+ SPACE_IDENTIFIER,
+ SPECTRAL_IDENTIFIER,
+ START,
+ STOP,
+ TIME,
+ TIME_IDENTIFIER,
+ TIME_LABEL,
+ TIME_SCALE,
+ TYPE_DOPPLER,
+ UNIT,
+ VELOCITY_IDENTIFIER,
+ VELOCITY
+ } look_for;
+
+/* Check the global error status. */
+ if ( !astOK ) return NULL;
+
+/* Create the returned KeyMap. */
+ result = astKeyMap( " ", status );
+
+/* Initialise the word search context. */
+ (void) GetNextWord( NULL, &con, status );
+
+/* Get a pointer to the first word in the STC-S description. */
+ word = GetNextWord( this, &con, status );
+
+/* Indicate we are currently looking for the time sub-phrase (the first
+ item in an STC-S description). */
+ look_for = TIME_IDENTIFIER;
+
+/* Initialise everything else. */
+ fbuf = NULL;
+ naxes = 0;
+ prop = NULL;
+ props = NULL;
+ redid = NULL_ID;
+ spaceid = NULL_ID;
+ specid = NULL_ID;
+ subphrase = NULL;
+ t = NULL;
+ timeid = NULL_ID;
+ velid = NULL_ID;
+ timefrm = NULL;
+
+/* Loop until all words in the STC-S description have been interpreted or
+ an error has occurred. */
+ while( word && astOK ) {
+
+/* Initialise a flag to indicate that we have interpreted the current word
+ sucesfully and so will need to get a new word before the next pass through
+ this loop. If it turns out that we cannot interpret the current word
+ in this pass, then this flag will be set to zero at some point, thus
+ preventing a new word from being acquired and causing another attempt to
+ re-interpret the current word in a different context. */
+ new_word = 1;
+
+/* If we are currently looking for the time sub-phrase, see if the current
+ word is any of the known time sub-phrase identifiers. Is so, move on
+ to read the associated sub-phrase component. */
+ if( look_for == TIME_IDENTIFIER ) {
+/* ------------------------------------------------------------------ */
+
+/* Assume that we will be moving on to read the fill factor (most time
+ sub-phrases start with the fill factor ). */
+ look_for = FILL_FACTOR;
+
+/* Now check the word to see if it a known time sub-phrase identifier. */
+ if( astChrMatch( word, "TimeInterval" ) ) {
+ timeid = TIME_INTERVAL_ID;
+
+ } else if( astChrMatch( word, "StartTime" ) ) {
+ timeid = START_TIME_ID;
+
+ } else if( astChrMatch( word, "StopTime" ) ) {
+ timeid = STOP_TIME_ID;
+
+ } else if( astChrMatch( word, "Time" ) ) {
+ look_for = TIME_SCALE; /* After "Time", we move on to find the
+ timeid = TIME_ID; time-scale, not the fill factor */
+
+/* If the word is not a known time sub-phrase identifier, indicate that we
+ should attempt to re-interpret the current word as a space sub-phrase
+ identifier, rather than getting a new word. */
+ } else {
+ look_for = SPACE_IDENTIFIER;
+ new_word = 0;
+ }
+
+/* If we have found a time sub-phrase identifier, create a KeyMap to hold
+ the properties of the time sub-phrase, and store the time sub-phrase
+ identifier in the new KeyMap. */
+ if( timeid != NULL_ID ) {
+ subphrase = "time";
+ props = astKeyMap( " ", status );
+ astMapPut0A( result, "TIME_PROPS", props, NULL );
+ astMapPut0C( props, "ID", word, NULL );
+ naxes = 1;
+ }
+
+
+
+/* If we are currently looking for the space sub-phrase, see if the current
+ word is any of the known space sub-phrase identifiers. Is so, move on
+ to read the associated sub-phrase component. */
+ } else if( look_for == SPACE_IDENTIFIER ) {
+/* ------------------------------------------------------------------ */
+
+/* Indicate we have finished any preceding time sub-phrase. */
+ timeid = NULL_ID;
+
+/* Now check the word to see if it a known space sub-phrase identifier. */
+ spaceid = SpaceId( word, status );
+
+/* Decide what to look for next. */
+ if( spaceid == POSITION_ID ) {
+ look_for = FRAME;
+
+ } else if( spaceid != NULL_ID ) {
+ look_for = FILL_FACTOR;
+
+/* If the word is not a known space sub-phrase identifier, move on to
+ re-interpret it as a Spectral sub-phrase identifier. */
+ } else {
+ look_for = SPECTRAL_IDENTIFIER;
+ new_word = 0;
+ }
+
+/* If we have found a space sub-phrase identifier, create a KeyMap to hold
+ the properties of the space sub-phrase, and store the space sub-phrase
+ identifier in the new KeyMap. */
+ if( spaceid != NULL_ID ) {
+ subphrase = "space";
+ if( props ) props = astAnnul( props );
+ props = astKeyMap( " ", status );
+ astMapPut0A( result, "SPACE_PROPS", props, NULL );
+ astMapPut0C( props, "ID", word, NULL );
+ }
+
+
+
+/* If we are currently looking for the velocity sub-phrase, see if the current
+ word is any of the known velocity sub-phrase identifiers. Is so, move on
+ to read the associated sub-phrase component. */
+ } else if( look_for == VELOCITY_IDENTIFIER ) {
+/* ------------------------------------------------------------------ */
+
+/* Indicate we have finished any preceding space sub-phrase. */
+ spaceid = NULL_ID;
+
+/* Now check the word to see if it a known velocity sub-phrase identifier. */
+ if( astChrMatch( word, "VelocityInterval" ) ) {
+ velid = VELOCITY_INTERVAL_ID;
+ look_for = FILL_FACTOR;
+
+ } else if( astChrMatch( word, "Velocity" ) ) {
+ velid = VELOCITY_ID;
+ look_for = VELOCITY;
+
+/* If the word is not a known velocity sub-phrase identifier, move on to
+ re-interpret it as a Spectral sub-phrase identifier. */
+ } else {
+ look_for = SPECTRAL_IDENTIFIER;
+ new_word = 0;
+ }
+
+/* If we have found a velocity sub-phrase identifier, create a KeyMap to
+ hold the properties of the velocity sub-phrase, and store the velocity
+ sub-phrase identifier in the new KeyMap. */
+ if( velid != NULL_ID ) {
+ subphrase = "velocity";
+ if( props ) props = astAnnul( props );
+ props = astKeyMap( " ", status );
+ astMapPut0A( result, "VELOCITY_PROPS", props, NULL );
+ astMapPut0C( props, "ID", word, NULL );
+ }
+
+
+
+/* If we are currently looking for the spectral sub-phrase, see if the
+ word is any of the known spectral sub-phrase identifiers. Is so, move
+ on to read the associated sub-phrase component. */
+ } else if( look_for == SPECTRAL_IDENTIFIER ) {
+/* ------------------------------------------------------------------ */
+
+/* Indicate we have finished any preceding velocity sub-phrase. */
+ velid = NULL_ID;
+
+/* Now check the word to see if it a known spectral sub-phrase identifier. */
+ if( astChrMatch( word, "SpectralInterval" ) ) {
+ look_for = FILL_FACTOR; /* Move on to find the fill factor */
+ specid = SPECTRAL_INTERVAL_ID;
+
+ } else if( astChrMatch( word, "Spectral" ) ) {
+ look_for = REFPOS; /* Move on to find the refpos */
+ specid = SPECTRAL_ID;
+
+/* If the word is not a known spectral sub-phrase identifier, move on to
+ look for the Redshift sub-phrase. */
+ } else {
+ look_for = REDSHIFT_IDENTIFIER;
+ new_word = 0;
+ }
+
+/* If we have found a spectral sub-phrase identifier, create a KeyMap to
+ hold the properties of the spectral sub-phrase, and store the spectral
+ sub-phrase identifier in the new KeyMap. */
+ if( specid != NULL_ID ) {
+ subphrase = "spectral";
+ if( props ) props = astAnnul( props );
+ props = astKeyMap( " ", status );
+ astMapPut0A( result, "SPECTRAL_PROPS", props, NULL );
+ astMapPut0C( props, "ID", word, NULL );
+ naxes = 1;
+ }
+
+
+
+/* If we are currently looking for the redshift sub-phrase, see if the
+ word is any of the known redshift sub-phrase identifiers. Is so, move
+ on to read the associated sub-phrase component. */
+ } else if( look_for == REDSHIFT_IDENTIFIER ) {
+/* ------------------------------------------------------------------ */
+
+/* Indicate we have finished any preceding spectral sub-phrase. */
+ specid = NULL_ID;
+
+/* Now check the word to see if it a known spectral sub-phrase identifier. */
+ if( astChrMatch( word, "RedshiftInterval" ) ) {
+ look_for = FILL_FACTOR; /* Move on to find the fill factor */
+ redid = REDSHIFT_INTERVAL_ID;
+
+ } else if( astChrMatch( word, "Redshift" ) ) {
+ look_for = REFPOS; /* Move on to find the refpos */
+ redid = REDSHIFT_ID;
+
+/* If the word is not a known redshift sub-phrase identifier, report a
+ warning. */
+ } else if( word[ 0 ] && astOK ) {
+ astError( AST__BADIN, "astRead(%s): Unsupported or irrelevant "
+ "word '%s' found in STC-S %s sub-phrase: '%s'.", status,
+ astGetClass( this ), word, subphrase,
+ ContextFragment( &con, &fbuf, status ) );
+ new_word = 0;
+ }
+
+/* If we have found a redshift sub-phrase identifier, create a KeyMap to
+ hold the properties of the redshift sub-phrase, and store the redshift
+ sub-phrase identifier in the new KeyMap. */
+ if( redid != NULL_ID ) {
+ subphrase = "redshift";
+ if( props ) props = astAnnul( props );
+ props = astKeyMap( " ", status );
+ astMapPut0A( result, "REDSHIFT_PROPS", props, NULL );
+ astMapPut0C( props, "ID", word, NULL );
+ naxes = 1;
+ }
+
+/* Indicate we can now end when we run out of input words. */
+ con.done = 1;
+
+
+
+/* If we are currently looking for a fill factor... */
+ } else if( look_for == FILL_FACTOR ) {
+/* ------------------------------------------------------------------ */
+
+/* If the current word is "fillfactor" attempt to read the numerical filling
+ factor from the next word. If this fails, or if the current word is
+ not "fillfactor", indicate that we will be re-interpreting the current
+ word in a new context and so do not need a new word. */
+ if( astChrMatch( word, "fillfactor" ) ) {
+ word = GetNextWord( this, &con, status );
+ if( astChr2Double( word ) == AST__BAD ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected a numerical "
+ "filling factor, but found '%s' in the %s "
+ "sub-phrase of STC-S description: '%s'.", status,
+ word, subphrase, ContextFragment( &con, &fbuf,
+ status ) );
+ new_word = 0;
+ }
+ } else {
+ new_word = 0;
+ }
+
+/* If we are reading a time sub-phrase, move on to read the timescale. */
+ if( timeid != NULL_ID ) {
+ look_for = TIME_SCALE;
+
+/* If we are reading a space sub-phrase, move on to read the frame. */
+ } else if( spaceid != NULL_ID ) {
+ look_for = FRAME;
+
+/* If we are reading a velocity sub-phrase, move on to read the limits. */
+ } else if( velid != NULL_ID ) {
+ look_for = LIMITS;
+
+/* Otherwise (i.e. for spectral and redshift sub-phrases) move on to read
+ the refpos. */
+ } else {
+ look_for = REFPOS;
+ }
+
+/* If the word was usable, record it as the fillfactor property. */
+ if( new_word ) astMapPut0C( props, "FILLFACTOR", word, NULL );
+
+
+
+/* If we are currently looking for a time scale... */
+ } else if( look_for == TIME_SCALE ) {
+/* ------------------------------------------------------------------ */
+
+/* If the current word is a recognised STC-S timescale, store it in the
+ props KeyMap. Otherwise, indicate that the word can be re-used in the
+ next context. */
+ if( astChrMatch( word, "TT" ) ||
+ astChrMatch( word, "TDT" ) ||
+ astChrMatch( word, "ET" ) ||
+ astChrMatch( word, "TAI" ) ||
+ astChrMatch( word, "IAT" ) ||
+ astChrMatch( word, "UTC" ) ||
+ astChrMatch( word, "TEB" ) ||
+ astChrMatch( word, "TDB" ) ||
+ astChrMatch( word, "TCG" ) ||
+ astChrMatch( word, "TCB" ) ||
+ astChrMatch( word, "LST" ) ||
+ astChrMatch( word, "nil" ) ) {
+
+ astMapPut0C( props, "TIMESCALE", word, NULL );
+
+ } else {
+ new_word = 0;
+ }
+
+/* Move on to look for a refpos */
+ look_for = REFPOS;
+
+
+
+/* If we are currently looking for a space frame... */
+ } else if( look_for == FRAME ) {
+/* ------------------------------------------------------------------ */
+
+/* If the current word is a recognised STC-S spatial frame, store it in
+ the props KeyMap. Otherwise, indicate that the word can be re-used. */
+ if( astChrMatch( word, "ICRS" ) ||
+ astChrMatch( word, "FK5" ) ||
+ astChrMatch( word, "FK4" ) ||
+ astChrMatch( word, "J2000" ) ||
+ astChrMatch( word, "B1950" ) ||
+ astChrMatch( word, "ECLIPTIC" ) ||
+ astChrMatch( word, "GALACTIC" ) ||
+ astChrMatch( word, "GALACTIC_II" ) ||
+ astChrMatch( word, "SUPER_GALACTIC" ) ||
+ astChrMatch( word, "GEO_C" ) ||
+ astChrMatch( word, "GEO_D" ) ||
+ astChrMatch( word, "UNKNOWNFrame" ) ) {
+
+ astMapPut0C( props, "FRAME", word, NULL );
+
+ } else {
+ new_word = 0;
+ }
+
+/* Move on to look for a refpos */
+ look_for = REFPOS;
+
+
+
+/* If we are currently looking for a refpos... */
+ } else if( look_for == REFPOS ) {
+/* ------------------------------------------------------------------ */
+
+/* If the current word is a recognised STC-S reference position, store it in
+ the props KeyMap. Otherwise, indicate that the word can be re-used. The
+ first group of reference positions apply to all sub-phrases. */
+ if( astChrMatch( word, "GEOCENTER" ) ||
+ astChrMatch( word, "BARYCENTER" ) ||
+ astChrMatch( word, "HELIOCENTER" ) ||
+ astChrMatch( word, "TOPOCENTER" ) ||
+ astChrMatch( word, "GALACTIC_CENTER" ) ||
+ astChrMatch( word, "EMBARYCENTER" ) ||
+ astChrMatch( word, "MOON" ) ||
+ astChrMatch( word, "MERCURY" ) ||
+ astChrMatch( word, "VENUS" ) ||
+ astChrMatch( word, "MARS" ) ||
+ astChrMatch( word, "JUPITER" ) ||
+ astChrMatch( word, "SATURN" ) ||
+ astChrMatch( word, "URANUS" ) ||
+ astChrMatch( word, "NEPTUNE" ) ||
+ astChrMatch( word, "PLUTO" ) ||
+ astChrMatch( word, "UNKNOWNRefPos" ) ) {
+
+ astMapPut0C( props, "REFPOS", word, NULL );
+
+/* This group of reference positions apply only to spectral and redshift
+ sub-phrases. */
+ } else if( astChrMatch( word, "LSR" ) ||
+ astChrMatch( word, "LSRK" ) ||
+ astChrMatch( word, "LSRD" ) ||
+ astChrMatch( word, "LOCAL_GROUP_CENTER" ) ) {
+
+ if( specid != NULL_ID || redid != NULL_ID ) {
+ astMapPut0C( props, "REFPOS", word, NULL );
+
+ } else if( astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Illegal reference "
+ "position '%s' found in the %s sub-phrase of "
+ "STC-S description: '%s'.", status, word,
+ subphrase, ContextFragment( &con, &fbuf, status ) );
+ new_word = 0;
+ }
+
+ } else {
+ new_word = 0;
+ }
+
+/* Choose what to look for next on the basis of the type of sub-phrase
+ currently being interpreted. */
+ if( timeid == TIME_INTERVAL_ID ){
+ look_for = START; /* Move on to find the start time */
+
+ } else if( timeid == START_TIME_ID ){
+ look_for = START; /* Move on to find the start time */
+
+ } else if( timeid == STOP_TIME_ID ){
+ look_for = STOP; /* Move on to find the stop time */
+
+ } else if( timeid == TIME_ID ){
+ look_for = TIME; /* Move on to find the time */
+
+ } else if( spaceid != NULL_ID ){
+ look_for = FLAVOUR; /* Move on to find the spatial flavour */
+
+ } else if( specid == SPECTRAL_INTERVAL_ID ) {
+ look_for = LIMITS; /* Move on to find the spectral limits */
+
+ } else if( specid == SPECTRAL_ID ) {
+ look_for = RED_SPEC_VALUE; /* Move on to find the spectral value */
+
+ } else if( redid == REDSHIFT_INTERVAL_ID ) {
+ look_for = TYPE_DOPPLER; /* Move on to find the redshift type */
+
+ } else if( redid == REDSHIFT_ID ) {
+ look_for = TYPE_DOPPLER; /* Move on to find the redshift type */
+
+ } else if( astOK ) { /* Should never happen */
+ astError( AST__INTER, "astRead(StcsChan): Sanity check 1 fails in "
+ "function ReadProps (AST internal programming error).",
+ status );
+ new_word = 0;
+ }
+
+
+
+
+
+/* If we are currently looking for a start time... */
+ } else if( look_for == START ) {
+/* ------------------------------------------------------------------ */
+
+/* Save the current word as the start of the START value. */
+ nc = 0;
+ prop = astAppendString( prop, &nc, word );
+
+/* If the current word is "JD" or "MJD", the following word should be
+ numerical. */
+ is_jd = astChrMatch( word, "JD" );
+ if( is_jd || astChrMatch( word, "MJD" ) ) {
+ word = GetNextWord( this, &con, status );
+ value = astChr2Double( word );
+ if( value == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected numerical "
+ "value in Start time, but found '%s %s' in STC-S "
+ "description: '%s'.", status, prop, word,
+ ContextFragment( &con, &fbuf, status ) );
+
+/* Append the second word to the first word. */
+ } else {
+ prop = astAppendString( prop, &nc, " " );
+ prop = astAppendString( prop, &nc, word );
+ }
+
+/* Convert JD to MJD if required. */
+ start = is_jd ? value - 2400000.5 : value;
+
+/* Otherwise, the current word should be an ISO date. Use a TimeFrame
+ to check the string. */
+ } else {
+ if( !timefrm ) timefrm = astTimeFrame( " ", status );
+ if( !astUnformat( timefrm, 0, word, &start ) && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected ISO date "
+ "string Start time, but found '%s' in an STC-S "
+ "description: '%s'.", status, word,
+ ContextFragment( &con, &fbuf, status ) );
+ }
+ }
+
+/* Record the START property. */
+ astMapPut0C( props, "START", prop, NULL );
+ astMapPut0D( props, "MJDSTART", start, NULL );
+
+/* Decide what to do next. */
+ if( timeid == TIME_INTERVAL_ID ){
+ look_for = STOP; /* Move on to find the stop time */
+
+ } else if( timeid == START_TIME_ID ){
+ look_for = TIME_LABEL; /* Move on to find the "coord" time */
+
+ }
+
+
+
+/* If we are currently looking for a stop time... */
+ } else if( look_for == STOP ) {
+/* ------------------------------------------------------------------ */
+
+/* Save the current word as the start of the STOP value. */
+ nc = 0;
+ prop = astAppendString( prop, &nc, word );
+
+/* If the current word is "JD" or "MJD", the following word should be
+ numerical. */
+ is_jd = astChrMatch( word, "JD" );
+ if( is_jd || astChrMatch( word, "MJD" ) ) {
+ word = GetNextWord( this, &con, status );
+ value = astChr2Double( word );
+ if( value == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected numerical "
+ "value in Stop time, but found '%s %s' in STC-S "
+ "description: '%s'.", status, prop, word,
+ ContextFragment( &con, &fbuf, status ) );
+
+/* Append the second word to the first word. */
+ } else {
+ prop = astAppendString( prop, &nc, " " );
+ prop = astAppendString( prop, &nc, word );
+ }
+
+/* Convert JD to MJD if required. */
+ stop = is_jd ? value - 2400000.5 : value;
+
+/* Otherwise, the current word should be an ISO date. Use a TimeFrame
+ to check the string. */
+ } else {
+ if( !timefrm ) timefrm = astTimeFrame( " ", status );
+ if( !astUnformat( timefrm, 0, word, &stop ) && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected ISO date "
+ "string Stop time, but found '%s' in an STC-S "
+ "description: '%s'.", status, word,
+ ContextFragment( &con, &fbuf, status ) );
+ }
+ }
+
+/* Record the STOP property. */
+ astMapPut0C( props, "STOP", prop, NULL );
+ astMapPut0D( props, "MJDSTOP", stop, NULL );
+
+/* Move on to find the "coord" time. */
+ look_for = TIME_LABEL;
+
+
+
+/* If we are currently looking for the label before a time coord value... */
+ } else if( look_for == TIME_LABEL ) {
+/* ------------------------------------------------------------------ */
+ if( astChrMatch( word, "Time" ) ) {
+ look_for = TIME;
+ } else {
+ new_word = 0;
+ look_for = UNIT;
+ }
+
+
+
+/* If we are currently looking for a time... */
+ } else if( look_for == TIME ) {
+/* ------------------------------------------------------------------ */
+
+/* Save the current word as the start of the TIME value. */
+ nc = 0;
+ prop = astAppendString( prop, &nc, word );
+
+/* If the current word is "JD" or "MJD", the following word should be
+ numerical. */
+ is_jd = astChrMatch( word, "JD" );
+ if( is_jd || astChrMatch( word, "MJD" ) ) {
+ word = GetNextWord( this, &con, status );
+ value = astChr2Double( word );
+ if( value == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected numerical "
+ "value in Time value, but found '%s %s' in STC-S "
+ "description: '%s'.", status, prop, word,
+ ContextFragment( &con, &fbuf, status ) );
+
+/* Append the second word to the first word. */
+ } else {
+ prop = astAppendString( prop, &nc, " " );
+ prop = astAppendString( prop, &nc, word );
+ }
+
+/* Convert JD to MJD if required. */
+ time = is_jd ? value - 2400000.5 : value;
+
+/* Otherwise, the current word should be an ISO date. Use a TimeFrame
+ to check the string. */
+ } else {
+ if( !timefrm ) timefrm = astTimeFrame( " ", status );
+ if( !astUnformat( timefrm, 0, word, &time ) && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected ISO date "
+ "string Time value, but found '%s' in an STC-S "
+ "description: '%s'.", status, word,
+ ContextFragment( &con, &fbuf, status ) );
+ }
+ }
+
+/* Record the TIME property. */
+ astMapPut0C( props, "TIME", prop, NULL );
+ astMapPut0D( props, "MJDTIME", time, NULL );
+
+/* Move on to look for the units. */
+ look_for = UNIT;
+
+
+
+/* If we are currently looking for a space "flavor"... */
+ } else if( look_for == FLAVOUR ) {
+/* ------------------------------------------------------------------ */
+
+/* If the current word is a recognised flavour value, note how many axis
+ values are required to specify a position. Otherwise, indicate that
+ the word can be re-used. */
+ if( astChrMatch( word, "SPHER2" ) ) {
+ naxes = 2;
+
+ } else if( astChrMatch( word, "UNITSPHER" ) ) {
+ naxes = 2;
+
+ } else if( astChrMatch( word, "CART1" ) ) {
+ naxes = 1;
+
+ } else if( astChrMatch( word, "CART2" ) ) {
+ naxes = 2;
+
+ } else if( astChrMatch( word, "CART3" ) ) {
+ naxes = 3;
+
+ } else if( astChrMatch( word, "SPHER3" ) ) {
+ naxes = 3;
+
+ } else {
+ naxes = 2;
+ new_word = 0;
+ }
+
+/* If the word was recognised as a flavour, store it in the porperties
+ KeyMap. */
+ if( new_word ) {
+ astMapPut0C( props, "FLAVOR", word, NULL );
+ astMapPut0C( props, "FLAVOUR", word, NULL );
+ }
+
+/* The next set of words to be read from the source function will specify
+ the arguments of the region enclosing the spatial positions. This may
+ contain nested regions, so use a recursive function to read the
+ arguments and store them in the properties KeyMap. */
+ if( new_word ) word = GetNextWord( this, &con, status );
+ word = ReadSpaceArgs( this, word, spaceid, naxes, &con, props,
+ status );
+ new_word = 0;
+
+/* Move on to the next look_for (following the region argument list read
+ by ReadSpaceArgs). */
+ if( spaceid == POSITION_ID ) {
+ look_for = UNIT;
+ } else {
+ look_for = POSITION;
+ }
+
+
+
+/* If we are currently looking for interval "lolimit"and "hilimit" ... */
+ } else if( look_for == LIMITS ) {
+/* ------------------------------------------------------------------ */
+ if( velid != NULL_ID ) {
+ t = "velocity";
+ look_for = VELOCITY;
+
+ } else if( specid != NULL_ID ) {
+ t = "spectral";
+ look_for = RED_SPEC_LABEL;
+
+ } else {
+ t = "redshift";
+ look_for = RED_SPEC_LABEL;
+ }
+
+/* The current word should be a numerical value (the low limit ). */
+ if( astChr2Double( word ) == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected a numerical "
+ "value for a %s lolimit, but found '%s' in an STC-S "
+ "description: '%s'.", status, t, word,
+ ContextFragment( &con, &fbuf, status ) );
+ } else {
+ astMapPut0C( props, "LOLIMIT", word, NULL );
+ }
+
+/* The next word should be a numerical value (the high limit ). */
+ word = GetNextWord( this, &con, status );
+ if( astChr2Double( word ) == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected a numerical "
+ "value for a %s hilimit, but found '%s' in an STC-S "
+ "description: '%s'.", status, t, word,
+ ContextFragment( &con, &fbuf, status ) );
+ } else {
+ astMapPut0C( props, "HILIMIT", word, NULL );
+ }
+
+
+
+/* If we are currently looking for the label before a spectral or redshift
+ value... */
+ } else if( look_for == RED_SPEC_LABEL ) {
+/* ------------------------------------------------------------------ */
+ if( specid != NULL_ID && astChrMatch( word, "Spectral" ) ) {
+ look_for = RED_SPEC_VALUE;
+
+ } else if( redid != NULL_ID && astChrMatch( word, "Redshift" ) ) {
+ look_for = RED_SPEC_VALUE;
+
+ } else {
+ new_word = 0;
+ look_for = UNIT;
+ }
+
+
+
+/* If we are currently looking for an spectral or redshift value. */
+ } else if( look_for == RED_SPEC_VALUE ) {
+/* ------------------------------------------------------------------ */
+
+ t = ( specid != NULL_ID ) ? "spectral" : "redshift";
+ if( astChr2Double( word ) == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected a numerical "
+ "%s value, but found '%s' in an STC-S "
+ "description: '%s'.", status, t, word,
+ ContextFragment( &con, &fbuf, status ) );
+ } else {
+ astMapPut0C( props, ( specid != NULL_ID ) ? "SPECTRAL" : "REDSHIFT",
+ word, NULL );
+ }
+
+/* Decide what to do next. */
+ look_for = UNIT;
+
+
+
+/* If we are currently looking for information needed to create a spatial
+ Position ... */
+ } else if( look_for == POSITION ) {
+/* ------------------------------------------------------------------ */
+
+/* Check the current word is "Position". If so, get the next word. */
+ if( astChrMatch( word, "Position" ) ) {
+ word = GetNextWord( this, &con, status );
+
+/* Get a value for every space axis. */
+ nc = 0;
+ for( iaxis = 0; iaxis < naxes; iaxis++ ) {
+ val[ iaxis ] = astChr2Double( word );
+ if( val[ iaxis ] == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected another "
+ "axis value for a space Position, but found "
+ "'%s' in an STC-S description: '%s'.", status,
+ word, ContextFragment( &con, &fbuf, status ) );
+ }
+ prop = astAppendString( prop, &nc, word );
+ prop = astAppendString( prop, &nc, " " );
+ word = GetNextWord( this, &con, status );
+ }
+
+/* Remove the trailing space, and store the property value in the KeyMap. */
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( props, "POSITION", prop, NULL );
+ astMapPut1D( props, "DPOSITION", naxes, val, NULL );
+ }
+
+/* Move on to read the "unit" item. */
+ new_word = 0;
+ look_for = UNIT;
+
+
+
+/* If we are currently looking for the redshift type and doppler
+ definition ... */
+ } else if( look_for == TYPE_DOPPLER ) {
+/* ------------------------------------------------------------------ */
+
+ if( astChrMatch( word, "VELOCITY" ) ||
+ astChrMatch( word, "REDSHIFT" ) ) {
+ astMapPut0C( props, "TYPE", word, NULL );
+ word = GetNextWord( this, &con, status );
+ }
+
+ if( astChrMatch( word, "OPTICAL" ) ||
+ astChrMatch( word, "RADIO" ) ||
+ astChrMatch( word, "RELATIVISTIC" ) ) {
+ astMapPut0C( props, "DOPPLERDEF", word, NULL );
+ } else {
+ new_word = 0;
+ }
+
+/* Decide what to do next. */
+ look_for = ( redid == REDSHIFT_INTERVAL_ID ) ? LIMITS : RED_SPEC_VALUE;
+
+
+
+/* If we are currently looking for a velocity label and value... */
+ } else if( look_for == VELOCITY ) {
+/* ------------------------------------------------------------------ */
+
+ if( astChrMatch( word, "Velocity" ) ) {
+ word = GetNextWord( this, &con, status );
+ if( astChr2Double( word ) == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected a "
+ "numerical Velocity value but found 'Velocity %s' "
+ "in an STC-S description: '%s'.", status, word,
+ ContextFragment( &con, &fbuf, status ) );
+ }
+
+ } else {
+ new_word = 0;
+ }
+
+ look_for = UNIT;
+
+
+
+/* If we are currently looking for a "unit" string... */
+ } else if( look_for == UNIT ) {
+/* ------------------------------------------------------------------ */
+
+/* See if the current word is "unit". If so, read the next word (which
+ will be the unit string itself). Otherwise, indicate the current word
+ can be re-used. */
+ if( astChrMatch( word, "unit" ) ) {
+ word = GetNextWord( this, &con, status );
+ } else {
+ new_word = 0;
+ }
+
+/* If we have a unit string... */
+ if( new_word ) {
+
+/* Check that the unit string is one of the allowed values (different
+ values are allowed for different sub-phrases). Space frames can have
+ multiple units strings (one for each axis) so loop round until a string
+ is found which is not a valid unit string. */
+ nc = 0;
+ nunit = 0;
+ while( ( timeid != NULL_ID && ( !strcmp( word, "s" ) ||
+ !strcmp( word, "d" ) ||
+ !strcmp( word, "a" ) ||
+ !strcmp( word, "yr" ) ||
+ !strcmp( word, "cy" ) ) ) ||
+
+ ( spaceid != NULL_ID && ( !strcmp( word, "deg" ) ||
+ !strcmp( word, "arcmin" ) ||
+ !strcmp( word, "arcsec" ) ||
+ !strcmp( word, "m" ) ||
+ !strcmp( word, "mm" ) ||
+ !strcmp( word, "m" ) ||
+ !strcmp( word, "km" ) ||
+ !strcmp( word, "AU" ) ||
+ !strcmp( word, "pc" ) ||
+ !strcmp( word, "kpc" ) ||
+ !strcmp( word, "Mpc" ) ) ) ||
+
+ ( velid != NULL_ID && ( !strcmp( word, "deg" ) ||
+ !strcmp( word, "arcmin" ) ||
+ !strcmp( word, "arcsec" ) ||
+ !strcmp( word, "m" ) ||
+ !strcmp( word, "mm" ) ||
+ !strcmp( word, "km" ) ||
+ !strcmp( word, "AU" ) ||
+ !strcmp( word, "pc" ) ||
+ !strcmp( word, "kpc" ) ||
+ !strcmp( word, "Mpc" ) ) ) ||
+
+ ( !strcmp( word, "Hz" ) ||
+ !strcmp( word, "MHz" ) ||
+ !strcmp( word, "GHz" ) ||
+ !strcmp( word, "m" ) ||
+ !strcmp( word, "mm" ) ||
+ !strcmp( word, "um" ) ||
+ !strcmp( word, "nm" ) ||
+ !strcmp( word, "Angstrom" ) ||
+ !strcmp( word, "eV" ) ||
+ !strcmp( word, "keV" ) ||
+ !strcmp( word, "MeV" ) ) ) {
+
+ prop = astAppendString( prop, &nc, word );
+ prop = astAppendString( prop, &nc, " " );
+ nunit++;
+ word = GetNextWord( this, &con, status );
+ }
+
+/* Report an error if an inappropriate number of valid unit strings was
+ found. */
+ if( nunit == 0 && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Unsupported "
+ "units (%s) for the %s sub-phrase within an "
+ "STC-S description: '%s'.", status, word, subphrase,
+ ContextFragment( &con, &fbuf, status ) );
+
+ } else if( nunit != 1 && nunit != naxes && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Incorrect number of "
+ "units string (%d) supplied for the %s sub-phrase within an "
+ "STC-S description: '%s'.", status, nunit, subphrase,
+ ContextFragment( &con, &fbuf, status ) );
+
+/* Otherwise, remove the trailing space, and store the property value in the
+ KeyMap. */
+ } else {
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( props, "UNIT", prop, NULL );
+ }
+
+/* The current word is the first word that was not a valid unit string,
+ and so can be re-used. */
+ new_word = 0;
+ }
+
+/* Move on to find the errors. */
+ look_for = ERROR;
+
+
+
+/* If we are currently looking for an "Error" string... */
+ } else if( look_for == ERROR ) {
+/* ------------------------------------------------------------------ */
+
+/* If the current word is "Error" read all subsequent words until the first
+ non-numerical value is encountered. */
+ if( astChrMatch( word, "Error" ) ) {
+ word = GetNextWord( this, &con, status );
+ value = astChr2Double( word );
+
+ nc = 0;
+ nval = 0;
+ while( value != AST__BAD ) {
+ if( nval < MAXVAL ) {
+ val[ nval++ ] = value;
+ prop = astAppendString( prop, &nc, word );
+ prop = astAppendString( prop, &nc, " " );
+ word = GetNextWord( this, &con, status );
+ value = astChr2Double( word );
+ } else {
+ astError( AST__BADIN, "astRead(StcsChan): Too many (more "
+ "than %d) numerical values found for the Error "
+ "property of the %s sub-phrase within an STC-S "
+ "description: '%s'.", status, MAXVAL, subphrase,
+ ContextFragment( &con, &fbuf, status ) );
+ break;
+ }
+ }
+
+/* Report an error if no numerical error values were found. */
+ if( nval == 0 && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected a "
+ "numerical error value but found 'Error %s' "
+ "for the %s sub-phrase within an "
+ "STC-S description: '%s'.", status, word, subphrase,
+ ContextFragment( &con, &fbuf, status ) );
+
+/* Otherwise, remove the trailing space and store the concatenated
+ string of formatted values in the properties KeyMap. Also store a
+ corresponding vector of floating point values in the KeyMap. */
+ } else {
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( props, "ERROR", prop, NULL );
+ astMapPut1D( props, "DERROR", nval, val, NULL );
+ }
+ }
+
+/* Indicate that we do not need to get a new word (we can re-use the last
+ one that turned out not to be a numerical value above). */
+ new_word = 0;
+
+/* Next look for Resolution */
+ look_for = RESOLUTION;
+
+
+
+/* If we are currently looking for a "Resolution" string... */
+ } else if( look_for == RESOLUTION ) {
+/* ------------------------------------------------------------------ */
+
+/* If the current word is "Resolution" read all subsequent words until the
+ first non-numerical value is encountered. */
+ if( astChrMatch( word, "Resolution" ) ) {
+ word = GetNextWord( this, &con, status );
+ value = astChr2Double( word );
+
+ nc = 0;
+ nval = 0;
+ while( value != AST__BAD ) {
+ if( nval < MAXVAL ) {
+ val[ nval++ ] = value;
+ prop = astAppendString( prop, &nc, word );
+ prop = astAppendString( prop, &nc, " " );
+ word = GetNextWord( this, &con, status );
+ value = astChr2Double( word );
+ } else {
+ astError( AST__BADIN, "astRead(StcsChan): Too many (more "
+ "than %d) numerical values found for the Resolution "
+ "property of the %s sub-phrase within an STC-S "
+ "description: '%s'.", status, MAXVAL, subphrase,
+ ContextFragment( &con, &fbuf, status ) );
+ break;
+ }
+ }
+
+/* Report an error if no numerical values were found. */
+ if( nval == 0 && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected a "
+ "numerical resolution value but found 'Resolution %s' "
+ "for the %s sub-phrase within an STC-S description:"
+ " '%s'.", status, word, subphrase,
+ ContextFragment( &con, &fbuf, status ) );
+
+/* Otherwise, remove the trailing space and store the concatenated
+ string of formatted values in the properties KeyMap. Also store a
+ corresponding vector of floating point values in the KeyMap. */
+ } else {
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( props, "RESOLUTION", prop, NULL );
+ astMapPut1D( props, "DRESOLUTION", nval, val, NULL );
+ }
+ }
+
+/* Indicate that we do not need to get a new word (we can re-use the last
+ one that turned out not to be a numerical value above). */
+ new_word = 0;
+
+/* Next look for Size. */
+ look_for = SIZE;
+
+
+
+/* If we are currently looking for a spatial "Size" string... */
+ } else if( look_for == SIZE ) {
+/* ------------------------------------------------------------------ */
+
+/* If the current word is "Size" read all subsequent words until the
+ first non-numerical value is encountered. */
+ if( astChrMatch( word, "Size" ) ) {
+ word = GetNextWord( this, &con, status );
+ value = astChr2Double( word );
+
+ nc = 0;
+ nval = 0;
+ while( value != AST__BAD ) {
+ if( nval < MAXVAL ) {
+ val[ nval++ ] = value;
+ prop = astAppendString( prop, &nc, word );
+ prop = astAppendString( prop, &nc, " " );
+ word = GetNextWord( this, &con, status );
+ value = astChr2Double( word );
+ } else {
+ astError( AST__BADIN, "astRead(StcsChan): Too many (more "
+ "than %d) numerical values found for the Size "
+ "property of the %s sub-phrase within an STC-S "
+ "description: '%s'.", status, MAXVAL, subphrase,
+ ContextFragment( &con, &fbuf, status ) );
+ break;
+ }
+ }
+
+/* Report an error if no numerical values were found. */
+ if( nval == 0 && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected a "
+ "numerical size value but found 'Size %s' "
+ "for the %s sub-phrase within an STC-S description:"
+ " '%s'.", status, word, subphrase,
+ ContextFragment( &con, &fbuf, status ) );
+
+/* Otherwise, remove the trailing space and store the concatenated
+ string of formatted values in the properties KeyMap. Also store a
+ corresponding vector of floating point values in the KeyMap. */
+ } else {
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( props, "SIZE", prop, NULL );
+ astMapPut1D( props, "DSIZE", nval, val, NULL );
+ }
+ }
+
+/* Indicate that we do not need to get a new word (we can re-use the last
+ one that turned out not to be a numerical value above). */
+ new_word = 0;
+
+/* Next look for PixSize. */
+ look_for = PIX_SIZE;
+
+
+
+/* If we are currently looking for a "PixSize" string... */
+ } else if( look_for == PIX_SIZE ) {
+/* ------------------------------------------------------------------ */
+
+/* If the current word is "PixSize" read all subsequent words until the
+ first non-numerical value is encountered. */
+ if( astChrMatch( word, "PixSize" ) ) {
+ word = GetNextWord( this, &con, status );
+ value = astChr2Double( word );
+
+ nc = 0;
+ nval = 0;
+ while( value != AST__BAD ) {
+ if( nval < MAXVAL ) {
+ val[ nval++ ] = value;
+ prop = astAppendString( prop, &nc, word );
+ prop = astAppendString( prop, &nc, " " );
+ word = GetNextWord( this, &con, status );
+ value = astChr2Double( word );
+ } else {
+ astError( AST__BADIN, "astRead(StcsChan): Too many (more "
+ "than %d) numerical values found for the PixSize "
+ "property of the %s sub-phrase within an STC-S "
+ "description: '%s'.", status, MAXVAL, subphrase,
+ ContextFragment( &con, &fbuf, status ) );
+ break;
+ }
+ }
+
+/* Report an error if no numerical values were found. */
+ if( nval == 0 && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected a "
+ "numerical pixel size but found 'PixSize %s' "
+ "for the %s sub-phrase within an STC-S description:"
+ " '%s'.", status, word, subphrase,
+ ContextFragment( &con, &fbuf, status ) );
+
+/* Otherwise, remove the trailing space and store the concatenated
+ string of formatted values in the properties KeyMap. Also store a
+ corresponding vector of floating point values in the KeyMap. */
+ } else {
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( props, "PIXSIZE", prop, NULL );
+ astMapPut1D( props, "DPIXSIZE", nval, val, NULL );
+ }
+ }
+
+/* Indicate that we do not need to get a new word (we can re-use the last
+ one that turned out not to be a numerical value above). */
+ new_word = 0;
+
+/* Next look for the next sub-phrase. */
+ if( timeid != NULL_ID ) {
+ look_for = SPACE_IDENTIFIER;
+
+ } else if( spaceid != NULL_ID ) {
+ look_for = VELOCITY_IDENTIFIER;
+
+ } else if( velid != NULL_ID ) {
+ look_for = SPECTRAL_IDENTIFIER;
+
+ } else if( specid != NULL_ID ) {
+ look_for = REDSHIFT_IDENTIFIER;
+
+ } else {
+ break;
+ }
+
+
+
+
+/* Report an error for any unknown look_for. */
+/* ------------------------------------------------------------------ */
+ } else if( astOK ) {
+ astError( AST__INTER, "astRead(StcsChan): Illegal look_for value "
+ "(%d) encountered (internal AST programming error).",
+ status, look_for );
+ }
+
+/* If required, get the next word in the STC-S description. */
+ if( new_word ) word = GetNextWord( this, &con, status );
+ }
+
+/* Free resources stored in the GetNextWord context structure. */
+ con.done = 1;
+ (void) GetNextWord( this, &con, status );
+ FreeContext( &con, status );
+
+/* Free other resources */
+ if( fbuf ) fbuf = astFree( fbuf );
+ if( prop ) prop = astFree( prop );
+ if( props ) props = astAnnul( props );
+ if( timefrm ) timefrm = astAnnul( timefrm );
+
+/* If an error occurred, clean up by deleting the new Object and
+ return a NULL pointer. */
+ if ( !astOK ) result = astDelete( result );
+
+/* Return the pointer to the properties KeyMap. */
+ return result;
+
+/* Undefine Local Constants: */
+#undef MAXVAL
+}
+
+static const char *ReadSpaceArgs( AstStcsChan *this, const char *word,
+ int spaceid, int naxes, WordContext *con,
+ AstKeyMap *props, int *status ){
+/*
+* Name:
+* ReadSpaceArgs
+
+* Purpose:
+* Read space region arguments from an STC-S description.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* const char *ReadSpaceArgs( AstStcsChan *this, const char *word,
+* int spaceid, int naxes, WordContext *con,
+* AstKeyMap *props, int *status )
+
+* Class Membership:
+* StcsChan member function
+
+* Description:
+* This function parses the list of space-separated words that form
+* the argument list of a spatial region. These words are read from the
+* source function, and stored in the supplied KeyMap using keys that
+* identify their purpose.
+*
+* This function calls itself recursively to handle compound regions.
+
+* Parameters:
+* this
+* Pointer to the StcsChan.
+* word
+* The first word of the argument list.
+* spaceid
+* An integer identifier for the type of spatial region for which
+* arguments are being read.
+* naxes
+* Number of axes in the space frame.
+* con
+* Pointer to a structure holding context for use with the
+* GetNextWord function. On exit, the next word returned by the
+* GetNextWord function will be the first word following the
+* argument list.
+* props
+* Pointer to the KeyMap in which the argument values should be
+* stored.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Pointer to the next wpord to be interpreted.
+
+*/
+
+
+/* Local Variables: */
+ AstKeyMap *new_props; /* KeyMap holding properties of an argument region */
+ char *fbuf; /* Pointer to buffer holding document fragment */
+ char *prop; /* String property value */
+ char key[ 20 ]; /* Key for argument region */
+ double *p; /* Pointer to next polygon vertex axis value */
+ double *temp; /* Array of polygon vertex axis values */
+ double val; /* Single numerical value */
+ double vals[ 6 ]; /* List of numerical values */
+ int iaxis; /* Axis index */
+ int nc; /* Used length of string */
+ int new_spaceid; /* Type of next argument region */
+ int nreg; /* Number of argument regions found */
+ int nvert; /* Number of vertices in polygon */
+
+/* Check inherited status */
+ if( !astOK ) return word;
+
+/* Initialise. */
+ fbuf = NULL;
+ prop = NULL;
+ nc = 0;
+
+/* If we are looking for information needed to create a spatial
+ Interval... */
+ if( spaceid == POSITION_INTERVAL_ID ) {
+
+/* Get a lolimit value for every space axis. */
+ for( iaxis = 0; iaxis < naxes; iaxis++ ) {
+ vals[ iaxis ] = astChr2Double( word );
+ if( vals[ iaxis ] == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected another "
+ "'lolimit' value for a PositionInterval, but found "
+ "'%s' in an STC-S description: '%s'.", status, word,
+ ContextFragment( con, &fbuf, status ) );
+ }
+ prop = astAppendString( prop, &nc, word );
+ prop = astAppendString( prop, &nc, " " );
+ word = GetNextWord( this, con, status );
+ }
+
+/* Remove the trailing space, and store the property value in the KeyMap. */
+ if( prop && nc > 0 ) {
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( props, "LOLIMIT", prop, NULL );
+ astMapPut1D( props, "DLOLIMIT", naxes, vals, NULL );
+ }
+
+/* Get a hilimit value for every space axis. */
+ nc = 0;
+ for( iaxis = 0; iaxis < naxes; iaxis++ ) {
+ vals[ iaxis ] = astChr2Double( word );
+ if( vals[ iaxis ] == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected another "
+ "'hilimit' value for a PositionInterval, but found "
+ "'%s' in an STC-S description: '%s'.", status, word,
+ ContextFragment( con, &fbuf, status ) );
+ }
+ prop = astAppendString( prop, &nc, word );
+ prop = astAppendString( prop, &nc, " " );
+ word = GetNextWord( this, con, status );
+ }
+
+/* Remove the trailing space, and store the property value in the KeyMap. */
+ if( prop && nc > 0 ) {
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( props, "HILIMIT", prop, NULL );
+ astMapPut1D( props, "DLOLIMIT", naxes, vals, NULL );
+ }
+
+/* If we are currently looking for information needed to create a spatial
+ AllSky ... */
+ } else if( spaceid == ALLSKY_ID ) {
+
+
+
+/* If we are currently looking for information needed to create a spatial
+ Circle ... */
+ } else if( spaceid == CIRCLE_ID ) {
+
+/* Get a centre value for every space axis. */
+ nc = 0;
+ for( iaxis = 0; iaxis < naxes; iaxis++ ) {
+ vals[ iaxis ] = astChr2Double( word );
+ if( vals[ iaxis ] == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected another "
+ "'centre' value for a Circle, but found "
+ "'%s' in an STC-S description: '%s'.", status, word,
+ ContextFragment( con, &fbuf, status ) );
+ }
+ prop = astAppendString( prop, &nc, word );
+ prop = astAppendString( prop, &nc, " " );
+ word = GetNextWord( this, con, status );
+ }
+
+/* Remove the trailing space, and store the property value in the KeyMap. */
+ if( prop && nc > 0 ) {
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( props, "CENTRE", prop, NULL );
+ astMapPut1D( props, "DCENTRE", naxes, vals, NULL );
+ }
+
+/* Get the radius value. */
+ val = astChr2Double( word );
+ if( val == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected a radius "
+ "value for a Circle, but found '%s' in an STC-S "
+ "description: '%s'.", status, word,
+ ContextFragment( con, &fbuf, status ) );
+ }
+
+/* Store the property value in the KeyMap. */
+ astMapPut0C( props, "RADIUS", word, NULL );
+
+/* Get the next word. */
+ word = GetNextWord( this, con, status );
+
+
+
+/* If we are currently looking for information needed to create a spatial
+ Ellipse ... */
+ } else if( spaceid == ELLIPSE_ID ) {
+
+/* Get a centre value for every space axis. */
+ nc = 0;
+ for( iaxis = 0; iaxis < naxes; iaxis++ ) {
+ vals[ iaxis ] = astChr2Double( word );
+ if( vals[ iaxis ] == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected another "
+ "centre value for an Ellipse, but found "
+ "'%s' in an STC-S description: '%s'.", status, word,
+ ContextFragment( con, &fbuf, status ) );
+ }
+ prop = astAppendString( prop, &nc, word );
+ prop = astAppendString( prop, &nc, " " );
+ word = GetNextWord( this, con, status );
+ }
+
+/* Remove the trailing space, and store the property value in the KeyMap. */
+ if( prop && nc > 0 ) {
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( props, "CENTRE", prop, NULL );
+ astMapPut1D( props, "DCENTRE", naxes, vals, NULL );
+ }
+
+/* Get the first radius value . */
+ val = astChr2Double( word );
+ if( val == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected the first "
+ "radius value for an Ellipse, but found "
+ "'%s' in an STC-S description: '%s'.", status, word,
+ ContextFragment( con, &fbuf, status ) );
+ }
+
+/* Store the property value in the KeyMap. */
+ astMapPut0C( props, "RADIUS1", word, NULL );
+
+/* Get the second radius value . */
+ word = GetNextWord( this, con, status );
+ val = astChr2Double( word );
+ if( val == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected the second "
+ "radius value for an Ellipse, but found "
+ "'%s' in an STC-S description: '%s'.", status, word,
+ ContextFragment( con, &fbuf, status ) );
+ }
+
+/* Store the property value in the KeyMap. */
+ astMapPut0C( props, "RADIUS2", word, NULL );
+
+/* Get the position angle value. */
+ word = GetNextWord( this, con, status );
+ val = astChr2Double( word );
+ if( val == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected the position "
+ "angle value for an Ellipse, but found "
+ "'%s' in an STC-S description: '%s'.", status, word,
+ ContextFragment( con, &fbuf, status ) );
+ }
+
+/* Store the property value in the KeyMap. */
+ astMapPut0C( props, "POSANGLE", word, NULL );
+
+/* Get the next word. */
+ word = GetNextWord( this, con, status );
+
+
+
+/* If we are currently looking for information needed to create a spatial
+ Box ... */
+ } else if( spaceid == BOX_ID ) {
+
+/* Get a centre value for every space axis. */
+ nc = 0;
+ for( iaxis = 0; iaxis < naxes; iaxis++ ) {
+ vals[ iaxis ] = astChr2Double( word );
+ if( vals[ iaxis ] == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected another "
+ "centre value for a Box, but found "
+ "'%s' in an STC-S description: '%s'.", status,
+ word, ContextFragment( con, &fbuf, status ) );
+ }
+ prop = astAppendString( prop, &nc, word );
+ prop = astAppendString( prop, &nc, " " );
+ word = GetNextWord( this, con, status );
+ }
+
+/* Remove the trailing space, and store the property value in the KeyMap. */
+ if( prop && nc > 0 ) {
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( props, "CENTRE", prop, NULL );
+ astMapPut1D( props, "DCENTRE", naxes, vals, NULL );
+ }
+
+/* Get bsize value for every space axis. */
+ nc = 0;
+ for( iaxis = 0; iaxis < naxes; iaxis++ ) {
+ vals[ iaxis ] = astChr2Double( word );
+ if( vals[ iaxis ] == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected another "
+ "'bsize' value for a Box, but found "
+ "'%s' in an STC-S description: '%s'.", status,
+ word, ContextFragment( con, &fbuf, status ) );
+ }
+ prop = astAppendString( prop, &nc, word );
+ prop = astAppendString( prop, &nc, " " );
+ word = GetNextWord( this, con, status );
+ }
+
+/* Remove the trailing space, and store the property value in the KeyMap. */
+ if( prop && nc > 0 ) {
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( props, "BSIZE", prop, NULL );
+ astMapPut1D( props, "DBSIZE", naxes, vals, NULL );
+ }
+
+
+/* If we are currently looking for information needed to create a spatial
+ Polygon ... */
+ } else if( spaceid == POLYGON_ID ) {
+
+/* Read the first vertex into a dynamically allocated array. */
+ temp = astMalloc( sizeof( *temp )*naxes );
+ if( temp ) {
+ nc = 0;
+ p = temp;
+ for( iaxis = 0; iaxis < naxes; iaxis++,p++ ) {
+ val = astChr2Double( word );
+ if( val == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected another "
+ "vertex value for a Polygon, but found "
+ "'%s' in an STC-S description: '%s'.", status,
+ word, ContextFragment( con, &fbuf, status ) );
+ } else {
+ *p = val;
+ }
+ prop = astAppendString( prop, &nc, word );
+ prop = astAppendString( prop, &nc, " " );
+ word = GetNextWord( this, con, status );
+ }
+
+/* Loop round reading remaining vertices, expanding the array as needed. */
+ nvert = 1;
+ val = astChr2Double( word );
+ while( val != AST__BAD && astOK ) {
+
+ temp = astGrow( temp, naxes*( nvert + 1 ), sizeof( *temp ) );
+ if( astOK ) {
+ p = temp + naxes*nvert;
+
+ for( iaxis = 0; iaxis < naxes; iaxis++, p++ ) {
+ if( val == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected "
+ "another vertex value for a Polygon, but "
+ "found '%s' in an STC-S description: '%s'.",
+ status, word, ContextFragment( con, &fbuf,
+ status ) );
+ } else {
+ *p = val;
+ }
+ prop = astAppendString( prop, &nc, word );
+ prop = astAppendString( prop, &nc, " " );
+ word = GetNextWord( this, con, status );
+ val = astChr2Double( word );
+ }
+ nvert++;
+ }
+ }
+
+/* Remove the trailing space, and store the property value in the KeyMap. */
+ if( prop && nc > 0 ) {
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( props, "VERTICES", prop, NULL );
+ astMapPut1D( props, "DVERTICES", naxes*nvert, temp, NULL );
+ }
+ temp = astFree( temp );
+ }
+
+
+
+/* If we are currently looking for information needed to create a spatial
+ Convex ... */
+ } else if( spaceid == CONVEX_ID ) {
+ astError( AST__BADIN, "astRead(StcsChan): A Convex was found "
+ "within an STC-S description ('Convex' regions "
+ "are not yet supported by AST): %s", status,
+ ContextFragment( con, &fbuf, status ) );
+
+
+
+/* If we are currently looking for information needed to create a spatial
+ Position ... */
+ } else if( spaceid == POSITION_ID ) {
+
+/* Get a value for every space axis. */
+ nc = 0;
+ for( iaxis = 0; iaxis < naxes; iaxis++ ) {
+ vals[ iaxis ] = astChr2Double( word );
+ if( vals[ iaxis ] == AST__BAD && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected another "
+ "axis value for a space Position, but found "
+ "'%s' in an STC-S description: '%s'.", status,
+ word, ContextFragment( con, &fbuf, status ) );
+ }
+ prop = astAppendString( prop, &nc, word );
+ prop = astAppendString( prop, &nc, " " );
+ word = GetNextWord( this, con, status );
+ }
+
+/* Remove the trailing space, and store the property value in the KeyMap. */
+ if( prop && nc > 0 ) {
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( props, "POSITION", prop, NULL );
+ astMapPut1D( props, "DPOSITION", naxes, vals, NULL );
+ }
+
+
+/* All remaining space id values require the argument list to be enclosed
+ in parentheses. Report an error if the current word does not start
+ with an opening parenthesis. */
+ } else if( *word != '(' && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected an opening "
+ "parenthesis but found '%s' in an STC-S description: '%s'.",
+ status, word, ContextFragment( con, &fbuf, status ) );
+
+/* Skip over the opening parenthesis. If the first word consists of just the
+ opening parenthesis, get the next word. */
+ } else {
+ if( *(++word) == 0 ) word = GetNextWord( this, con, status );
+
+/* Loop round all regions included in the compound region. */
+ nreg = 0;
+ while( astOK ) {
+
+/* If the next word starts with a closing parenthesis, we have reached
+ the end of the argument list. */
+ if( *word == ')' ) {
+
+/* Skip over the closing parenthesis. If the word consists of just the
+ closing parenthesis, get the next word. */
+ if( *(++word) == 0 ) word = GetNextWord( this, con, status );
+
+/* Leave the loop. */
+ break;
+ }
+
+/* Identify the region type from the current word. */
+ new_spaceid = SpaceId( word, status );
+ if( new_spaceid == NULL_ID && astOK ) {
+ astError( AST__BADIN, "astRead(StcsChan): Expected a "
+ "CoordinateArea or a closing parenthesis but found "
+ "'%s' in an STC-S description: '%s'.", status, word,
+ ContextFragment( con, &fbuf, status ) );
+ }
+
+/* Create a new KeyMap to store the properties of the new region. Store
+ this new KeyMap in the supplied KeyMap using a key of the form
+ "REGION<n>". */
+ new_props = astKeyMap( " ", status );
+ astMapPut0C( new_props, "ID", word, NULL );
+ sprintf( key, "REGION%d", ++nreg );
+ astMapPut0A( props, key, new_props, NULL );
+
+/* Get the next word (i.e. the first word of the argument list for the
+ region). */
+ word = GetNextWord( this, con, status );
+
+/* Call this function recursively to read the argument list. */
+ word = ReadSpaceArgs( this, word, new_spaceid, naxes, con,
+ new_props, status );
+
+/* Free resources. */
+ new_props = astAnnul( new_props );
+ }
+
+/* Store the number of regions in the supplied KeyMap. */
+ astMapPut0I( props, "NREG", nreg, NULL );
+
+/* Report an error if an in appropriate number of argument Regions were
+ supplied. */
+ if( spaceid == UNION_ID ) {
+ if( nreg < 2 && astOK ){
+ astError( AST__BADIN, "astRead(StcsChan): Less than two "
+ "CoordinateAreas found within a 'Union' element in an "
+ "STC-S description: '%s'.", status,
+ ContextFragment( con, &fbuf, status ) );
+ }
+
+ } else if( spaceid == INTERSECTION_ID ) {
+ if( nreg < 2 && astOK ){
+ astError( AST__BADIN, "astRead(StcsChan): Less than two "
+ "CoordinateAreas found within an 'Intersection' element "
+ "in an STC-S description: '%s'.", status,
+ ContextFragment( con, &fbuf, status ) );
+ }
+
+ } else if( spaceid == DIFFERENCE_ID ) {
+ if( nreg != 2 && astOK ){
+ astError( AST__BADIN, "astRead(StcsChan): %d CoordinateArea(s) "
+ "found within a 'Difference' element in an STC-S "
+ "description: '%s'.", status, nreg,
+ ContextFragment( con, &fbuf, status ) );
+ }
+
+
+ } else if( spaceid == NOT_ID ) {
+ if( nreg != 1 && astOK ){
+ astError( AST__BADIN, "astRead(StcsChan): %d CoordinateAreas "
+ "found within a 'Not' element in an STC-S description: "
+ "'%s'.", status, nreg,
+ ContextFragment( con, &fbuf, status ) );
+ }
+
+/* Report an error for unknown spaceid values */
+ } else if( astOK ) {
+ astError( AST__INTER, "astRead(StcsChan): Illegal 'spaceid' value "
+ "passed to function ReadSpaceArgs (internal AST "
+ "programming error).", status );
+ }
+ }
+
+/* Free resources */
+ if( prop ) prop = astFree( prop );
+
+/* Return a pointer to the next word to be interpreted. */
+ return word;
+}
+
+static void SetAttrib( AstObject *this_object, const char *setting, int *status ) {
+/*
+* Name:
+* SetAttrib
+
+* Purpose:
+* Set an attribute value for a StcsChan.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* void SetAttrib( AstObject *this, const char *setting )
+
+* Class Membership:
+* StcsChan member function (over-rides the astSetAttrib protected
+* method inherited from the Channel class).
+
+* Description:
+* This function assigns an attribute value for a StcsChan, the
+* attribute and its value being specified by means of a string of
+* the form:
+*
+* "attribute= value "
+*
+* Here, "attribute" specifies the attribute name and should be in
+* lower case with no white space present. The value to the right
+* of the "=" should be a suitable textual representation of the
+* value to be assigned and this will be interpreted according to
+* the attribute's data type. White space surrounding the value is
+* only significant for string attributes.
+
+* Parameters:
+* this
+* Pointer to the StcsChan.
+* setting
+* Pointer to a null terminated string specifying the new attribute
+* value.
+*/
+
+/* Local Variables: */
+ AstStcsChan *this; /* Pointer to the StcsChan structure */
+ int ival; /* Integer attribute value */
+ int len; /* Length of setting string */
+ int nc; /* Number of characters read by "astSscanf" */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the StcsChan structure. */
+ this = (AstStcsChan *) this_object;
+
+/* Obtain the length of the setting string. */
+ len = (int) strlen( setting );
+
+/* Test for each recognised attribute in turn, using "astSscanf" to parse
+ the setting string and extract the attribute value (or an offset to
+ it in the case of string values). In each case, use the value set
+ in "nc" to check that the entire string was matched. Once a value
+ has been obtained, use the appropriate method to set it. */
+
+/* StcsArea. */
+/* --------- */
+ if ( nc = 0,
+ ( 1 == astSscanf( setting, "stcsarea= %d %n", &ival, &nc ) )
+ && ( nc >= len ) ) {
+ astSetStcsArea( this, ival );
+
+/* StcsCoords. */
+/* ----------- */
+ } else if ( nc = 0,
+ ( 1 == astSscanf( setting, "stcscoords= %d %n", &ival, &nc ) )
+ && ( nc >= len ) ) {
+ astSetStcsCoords( this, ival );
+
+/* StcsProps. */
+/* ----------- */
+ } else if ( nc = 0,
+ ( 1 == astSscanf( setting, "stcsprops= %d %n", &ival, &nc ) )
+ && ( nc >= len ) ) {
+ astSetStcsProps( this, ival );
+
+/* StcsLength */
+/* ----------*/
+ } else if ( nc = 0,
+ ( 1 == astSscanf( setting, "stcslength= %d %n", &ival, &nc ) )
+ && ( nc >= len ) ) {
+ astSetStcsLength( this, ival );
+
+/* If the attribute is still not recognised, pass it on to the parent
+ method for further interpretation. */
+ } else {
+ (*parent_setattrib)( this_object, setting, status );
+ }
+}
+
+static void SetUnc( AstRegion *reg1, AstRegion *reg2, AstFrame *frm,
+ int is_skyframe, double scale, double *error, int nax,
+ int *status ){
+/*
+* Name:
+* SetUnc
+
+* Purpose:
+* Store an uncertainty Box with a supplied Region.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* void SetUnc( AstRegion *reg1, AstRegion *reg2, AstFrame *frm,
+* int is_skyframe, double scale, double *error, int nax,
+* int *status )
+
+* Class Membership:
+* StcsChan member function
+
+* Description:
+* This function creates a new Box with dimensions specified by the
+* values in the "error" array, centred on a representative position
+* within one of the supplied Regions, and then stores the Box as the
+* uncertainty Region within both the supplied Regions.
+
+* Parameters:
+* reg1
+* Pointer to a Region to which the error values relate.
+* reg2
+* Pointer to another Region to which the error values relate.
+* frm
+* Pointer to the Frame encapsulated by both Regions.
+* is_skyframe
+* Should be non-zero if "frm" is a SkyFrame.
+* scale
+* A scale factor to apply to the error values before using them.
+* error
+* Pointer to an array of RMS error values, one for each axis in
+* "frm". These are modified on exit. For a SkyFrame, both values
+* (including the longitude axis value) should be given as an
+* arc-distance. This function will convert the arc-distance to
+* a longitude increment using a representative latitude for the
+* region.
+* nax
+* The numner of axes in "frm".
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ AstBox *unc; /* Uncertainty box */
+ double dist; /* Diagonal length of Region bounding box */
+ double lbnd[ 6 ]; /* Lower bounds of Region bounding box */
+ double spos1[ 6 ]; /* A representative position in the Region */
+ double spos2[ 6 ]; /* A second position in the Region */
+ double ubnd[ 6 ]; /* Upper bounds of Region bounding box */
+ int i; /* Axis index */
+
+/* Check the global error status. Also check an error value was supplied,
+ and at least one of the Region pointers is not NULL. */
+ if ( !astOK || error[ 0 ] == AST__BAD || ( !reg1 && !reg2 ) ) return;
+
+/* We need a representative position within the region. First get the
+ coordinates at opposite corners of the region bounding box. */
+ astRegBaseBox( reg1 ? reg1 : reg2, lbnd, ubnd );
+
+/* Find the diagonal length of the bounding box. */
+ dist = astDistance( frm, lbnd, ubnd );
+
+/* Offset away from one corner towards the other by half the diagonal
+ length. The resulting position returned in spos1 is our representative
+ position for the region. */
+ astOffset( frm, lbnd, ubnd, dist/2, spos1 );
+
+/* Scale the error values */
+ for( i = 0; i < nax; i++ ) error[ i ] *= scale;
+
+/* If the region is defined within a SkyFrame, the supplied longitude
+ error value will be an arc-distance value. But we need a longitude
+ increment to create an uncertainty Region, so do the conversion. */
+ if( is_skyframe ) {
+
+/* Offset away from the representative position found above along the
+ first (i.e. longitude) axis by an arc-distance given by the Error
+ value. */
+ (void) astOffset2( frm, spos1, AST__DPIBY2, error[ 0 ], spos2 );
+
+/* Find the positive axis increment along the first axis. */
+ error[ 0 ] = astAxDistance( frm, 1, spos1[ 0 ], spos2[ 0 ] );
+ if( error[ 0 ] != AST__BAD ) error[ 0 ] = fabs( error[ 0 ] );
+ }
+
+/* The uncertainty Region will be a Box centred at the representative
+ position found above. Modify the "error" array to hold the corner
+ axis values. */
+ for( i = 0; i < nax; i++ ) error[ i ] += spos1[ i ];
+
+/* Create the box, and store it as the uncertainty Region in the supplied
+ Region. */
+ unc = astBox( frm, 0, spos1, error, NULL, " ", status );
+ if( reg1 ) astSetUnc( reg1, unc );
+ if( reg2 ) astSetUnc( reg2, unc );
+
+/* Free resources. */
+ unc = astAnnul( unc );
+}
+
+static AstPointList *SinglePointList( AstFrame *frm, double *pos,
+ AstRegion *unc, int *status){
+/*
+* Name:
+* SinglePointList
+
+* Purpose:
+* Create a PointList holding a single point.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* AstPointList *SinglePointList( AstFrame *frm, double *pos,
+* AstRegion *unc, int *status )
+
+* Class Membership:
+* StcsChan member function
+
+* Description:
+* This function creates a new PointList holding a single supplied
+* position.
+
+* Parameters:
+* frm
+* Pointer to the Frame in which the PointList is defined.
+* pos
+* Array holding the position. The length of this array must equal
+* the number of axes in "frm".
+* unc
+* Pointer to an uncertainty Region to associate with the new
+* PointList, or NULL.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new PointList. NULL is returned if an error has
+* already occurred, of if this function fails for any reason.
+*/
+
+/* Local Variables: */
+ AstPointList *result; /* Returned pointer. */
+ AstPointSet *pset; /* PointSet holding axis values */
+ double **ptr; /* Pointer to PointSet data arrays */
+ int i; /* Axis index */
+ int nax; /* Number of axes */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Get he number of axes. */
+ nax = astGetNaxes( frm );
+
+/* Create a PointSet to hold the supplied point, and get a pointer to its
+ data arrays. */
+ pset = astPointSet( 1, nax, "", status );
+ ptr = astGetPoints( pset );
+ if( astOK ) {
+
+/* Copy the supplied axis values into the PointSet data arrays. */
+ for( i = 0; i < nax; i++ ) ptr[ i ][ 0 ] = pos[ i ];
+
+/* Create the PointList. */
+ result = astPointList( frm, pset, unc, "", status );
+ }
+
+/* Free resources */
+ pset = astAnnul( pset );
+
+/* Return the result. */
+ return result;
+}
+
+static void SinkWrap( void (* sink)( const char * ), const char *line, int *status ) {
+/*
+* Name:
+* SinkWrap
+
+* Purpose:
+* Wrapper function to invoke a C StcsChan sink function.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* void SinkWrap( void (* sink)( const char * ), const char *line, int *status )
+
+* Class Membership:
+* StcsChan member function.
+
+* Description:
+* This function invokes the sink function whose pointer is
+* supplied in order to write an output line to an external data
+* store.
+
+* Parameters:
+* sink
+* Pointer to a sink function, whose single parameter is a
+* pointer to a const, null-terminated string containing the
+* text to be written, and which returns void. This is the form
+* of StcsChan sink function employed by the C language interface
+* to the AST library.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Invoke the sink function. */
+ ( *sink )( line );
+}
+
+static char *SourceWrap( const char *(* source)( void ), int *status ) {
+/*
+* Name:
+* SourceWrap
+
+* Purpose:
+* Wrapper function to invoke a C StcsChan source function.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* char *SourceWrap( const char *(* source)( void ), int *status )
+
+* Class Membership:
+* StcsChan member function.
+
+* Description:
+* This function invokes the source function whose pointer is
+* supplied in order to read the next input line from an external
+* data store. It then returns a pointer to a dynamic string
+* containing a copy of the text that was read.
+
+* Parameters:
+* source
+* Pointer to a source function, with no parameters, that
+* returns a pointer to a const, null-terminated string
+* containing the text that it read. This is the form of StcsChan
+* source function employed by the C language interface to the
+* AST library.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to a dynamically allocated, null terminated string
+* containing a copy of the text that was read. This string must be
+* freed by the caller (using astFree) when no longer required.
+*
+* A NULL pointer will be returned if there is no more input text
+* to read.
+
+* Notes:
+* - A NULL pointer value will be returned if this function is
+* invoked with the global error status set or if it should fail
+* for any reason.
+*/
+
+/* Local Variables: */
+ char *result; /* Pointer value to return */
+ const char *line; /* Pointer to input line */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Invoke the source function to read the next input line and return a
+ pointer to the resulting string. */
+ line = ( *source )();
+
+/* If a string was obtained, make a dynamic copy of it and save the
+ resulting pointer. */
+ if ( line ) result = astString( line, (int) strlen( line ) );
+
+/* Return the result. */
+ return result;
+}
+
+static int SpaceId( const char *word, int *status ){
+/*
+* Name:
+* SpaceId
+
+* Purpose:
+* Return the integer identifier for a given textual space identifier.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* int SpaceId( const char *word, int *status )
+
+* Class Membership:
+* StcsChan member function
+
+* Description:
+* This function returns an integer identifier for the given space
+* identifier.
+
+* Parameters:
+* word
+* The word holding the textual space identifier.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The integer space identifier, or NULL_ID if the supplied word was
+* not a known space identifier.
+
+*/
+
+
+/* Local Variables: */
+ int spaceid; /* Returned identifier */
+
+/* Check inherited status */
+ if( !astOK ) return NULL_ID;
+
+ if( astChrMatch( word, "PositionInterval" ) ) {
+ spaceid = POSITION_INTERVAL_ID;
+
+ } else if( astChrMatch( word, "AllSky" ) ) {
+ spaceid = ALLSKY_ID;
+
+ } else if( astChrMatch( word, "Circle" ) ) {
+ spaceid = CIRCLE_ID;
+
+ } else if( astChrMatch( word, "Ellipse" ) ) {
+ spaceid = ELLIPSE_ID;
+
+ } else if( astChrMatch( word, "Box" ) ) {
+ spaceid = BOX_ID;
+
+ } else if( astChrMatch( word, "Polygon" ) ) {
+ spaceid = POLYGON_ID;
+
+ } else if( astChrMatch( word, "Convex" ) ) {
+ spaceid = CONVEX_ID;
+
+ } else if( astChrMatch( word, "Union" ) ) {
+ spaceid = UNION_ID;
+
+ } else if( astChrMatch( word, "Intersection" ) ) {
+ spaceid = INTERSECTION_ID;
+
+ } else if( astChrMatch( word, "Difference" ) ) {
+ spaceid = DIFFERENCE_ID;
+
+ } else if( astChrMatch( word, "Not" ) ) {
+ spaceid = NOT_ID;
+
+ } else if( astChrMatch( word, "Position" ) ) {
+ spaceid = POSITION_ID;
+
+ } else {
+ spaceid = NULL_ID;
+ }
+
+/* Return the integer space identifier. */
+ return spaceid;
+}
+
+static void StoreTimeProp( AstKeyMap *props, AstTimeFrame *frm,
+ const char *key, double value, int *status ){
+/*
+* Name:
+* StoreTimeProp
+
+* Purpose:
+* Store a time value as an STC-S property, using the existing format.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* void StoreTimeProp( AstKeyMap *props, AstTimeFrame *frm,
+* const char *key, double value, int *status )
+
+* Class Membership:
+* StcsChan member function.
+
+* Description:
+* This function formats the supplied time value and stores it in
+* the "props" KeyMap, using the supplied key name. If the KeyMap
+* already contains an entry for the given key, the new value is
+* written using the same format. Otherwise, the new value is written
+* as an ISO date and time string.
+
+* Parameters:
+* props
+* Pointer to the KeyMap in which to store the time value.
+* frm
+* Pointer to a TimeFrame that can be used to format the time value.
+* key
+* Pointer to a string holding the property name associated with
+* the time value.
+* value
+* The time value, in the system described by "frm".
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ AstFrame *fmtfrm; /* Frame defining Format/System for formatted value */
+ AstFrame *fs; /* FrameSet connecting Frames */
+ const char *fmttxt; /* Formatted text */
+ const char *oldval; /* Pointer to old formatted time value */
+ const char *p; /* Pointer to next character in formatted value */
+ double fmtval; /* The time value in the formatting system */
+ int ndp; /* Number of decimal places in formatted value */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* We want a TimeFrame (fmtfrm) that describes how to format the time
+ value. If the Format attribute of the supplied TimeFrame has been
+ set, use it (and the current System). So just take a clone of the
+ supplied frame pointer. */
+ if( astTestFormat( frm, 0 ) ) {
+ fmtfrm = astClone( frm );
+
+/* If the Format attribute has not been set, we create a copy of the
+ supplied TimeFrame, and set its System and Format attributes to
+ produce the required format. */
+ } else {
+ fmtfrm = astCopy( frm );
+
+/* If the KeyMap contains an entry for the specified key, determine the
+ format of the time string it contains. */
+ if( astMapGet0C( props, key, &oldval ) && oldval ) {
+
+/* See how many digits there are after the decimal place */
+ p = strchr( oldval, '.' );
+ ndp = 0;
+ if( p ) {
+ while( *(++p) ) {
+ if( isdigit( *p ) ) {
+ ndp++;
+ } else {
+ break;
+ }
+ }
+ }
+
+/* If the string starts with "JD", the time is formatted as a numerical
+ Julian date. */
+ if( !strncmp( oldval, "JD", 2 ) ) {
+ astSetSystem( fmtfrm, AST__JD );
+ if( ndp > 0 ) {
+ astSet( fmtfrm, "Format=JD %%.%df", status, ndp );
+ } else {
+ astSetFormat( fmtfrm, 0, "JD %d" );
+ }
+
+/* If the string starts with "MJD", the time is formatted as a numerical
+ Modified Julian date. */
+ } else if( !strncmp( oldval, "MJD", 3 ) ) {
+ astSetSystem( fmtfrm, AST__MJD );
+ if( ndp > 0 ) {
+ astSet( fmtfrm, "Format=MJD %%.%df", status, ndp );
+ } else {
+ astSetFormat( fmtfrm, 0, "MJD %d" );
+ }
+
+/* Otherwise, the current word should be an ISO date. See how many
+ decimal paces in the seconds field there are (if any). */
+ } else {
+ astSet( fmtfrm, "Format=iso.%dT", status, ndp );
+ }
+
+/* If the KeyMap does not contain an entry for the specified key, an
+ ISO date/time string with 1 decimal place in the seconds field
+ is used. */
+ } else {
+ astSetFormat( fmtfrm, 0, "iso.1T" );
+ }
+ }
+
+/* Ensure the displayed value is an abolute value. */
+ astClearTimeOrigin( fmtfrm );
+
+/* Convert the supplied time value into the required system. */
+ fs = astConvert( frm, fmtfrm, "" );
+ astTran1( fs, 1, &value, 1, &fmtval );
+
+/* Format the value. */
+ fmttxt = astFormat( fmtfrm, 0, fmtval );
+
+/* Store it in the KeyMap. */
+ astMapPut0C( props, key, fmttxt, NULL );
+
+/* Free resources. */
+ fs = astAnnul( fs );
+ fmtfrm = astAnnul( fmtfrm );
+}
+
+static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* TestAttrib
+
+* Purpose:
+* Test if a specified attribute value is set for a StcsChan.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* int TestAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* StcsChan member function (over-rides the astTestAttrib protected
+* method inherited from the Object class).
+
+* Description:
+* This function returns a boolean result (0 or 1) to indicate whether
+* a value has been set for one of a StcsChan's attributes.
+
+* Parameters:
+* this
+* Pointer to the StcsChan.
+* attrib
+* Pointer to a null terminated string specifying the attribute
+* name. This should be in lower case with no surrounding white
+* space.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* One if a value has been set, otherwise zero.
+
+* Notes:
+* - A value of zero will be returned if this function is invoked
+* with the global status set, or if it should fail for any reason.
+*/
+
+/* Local Variables: */
+ AstStcsChan *this; /* Pointer to the StcsChan structure */
+ int result; /* Result value to return */
+
+/* Initialise. */
+ result = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Obtain a pointer to the StcsChan structure. */
+ this = (AstStcsChan *) this_object;
+
+/* Check the attribute name and test the appropriate attribute. */
+
+ if ( !strcmp( attrib, "stcsarea" ) ) {
+ result = astTestStcsArea( this );
+
+ } else if ( !strcmp( attrib, "stcscoords" ) ) {
+ result = astTestStcsCoords( this );
+
+ } else if ( !strcmp( attrib, "stcsprops" ) ) {
+ result = astTestStcsProps( this );
+
+ } else if ( !strcmp( attrib, "stcslength" ) ) {
+ result = astTestStcsLength( this );
+
+/* If the attribute is still not recognised, pass it on to the parent
+ method for further interpretation. */
+ } else {
+ result = (*parent_testattrib)( this_object, attrib, status );
+ }
+
+/* Return the result, */
+ return result;
+}
+
+static int Write( AstChannel *this_channel, AstObject *object, int *status ) {
+/*
+* Name:
+* Write
+
+* Purpose:
+* Write an Object to a StcsChan.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* int Write( AstChannel *this, AstObject *object, int *status )
+
+* Class Membership:
+* StcsChan member function (over-rides the astWrite method
+* inherited from the Channel class).
+
+* Description:
+* This function writes an Object to a StcsChan.
+
+* Parameters:
+* this
+* Pointer to the StcsChan.
+* object
+* Pointer to the Object which is to be written.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The number of Objects written to the StcsChan by this invocation of
+* astWrite.
+
+* Notes:
+* - A value of zero will be returned if this function is invoked
+* with the AST error status set, or if it should fail for any
+* reason.
+*/
+
+/* Local Variables: */
+ AstFrame *frm; /* AREA Frame */
+ AstFrameSet *fs; /* FrameSet connecting AREA and COORDS */
+ AstKeyMap *props; /* A KeyMap holding the STC-S properties list */
+ AstMapping *map; /* Mapping connecting AREA and COORDS */
+ AstObject *obj; /* A temporary Object pointer */
+ AstRegion *area; /* The Region representing the STC CoordArea */
+ AstRegion *coords; /* The Region representing the STC Coords */
+ AstRegion *new_coords; /* COORDS Region mapped into frame of AREA */
+ AstStcsChan *this; /* Pointer to the StcsChan structure */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+ const char *class; /* Pointer to string holding object class */
+ const char *errclass; /* Type of the failed entry */
+ const char *errname; /* Name of the failed entry */
+ const char *method; /* Pointer to string holding calling method */
+ const char *wantclass; /* The expected type */
+ int ret; /* Number of objects read */
+
+/* Initialise. */
+ ret = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return ret;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(this_channel);
+
+/* Obtain a pointer to the StcsChan structure. */
+ this = (AstStcsChan *) this_channel;
+
+/* Store the calling method, and object class. */
+ method = "astWrite";
+ class = astGetClass( this );
+
+/* Initialise */
+ area = NULL;
+ coords = NULL;
+ props = NULL;
+
+/* If the supplied Object is a Region, we will use it to define the AREA
+ properties. */
+ if( astIsARegion( object ) ) {
+ area = (AstRegion *) astClone( object );
+
+/* If the supplied Object is a KeyMap... */
+ } else if( astIsAKeyMap( object ) ) {
+ errname = NULL;
+ wantclass = NULL;
+ errclass = NULL;
+
+/* If the supplied KeyMap contains an entry with key "AREA", and if it is
+ a Region, use it to define the AREA properties. */
+ if( astMapGet0A( (AstKeyMap *) object, "AREA", &obj ) ) {
+ if( astIsARegion( obj ) ) {
+ area = (AstRegion *) obj;
+ } else {
+ wantclass = "Region";
+ errclass = astGetClass( obj );
+ errname = "AREA";
+ obj = astAnnul( obj );
+ }
+ }
+
+/* If the supplied KeyMap contains an entry with key "COORDS", and if it is
+ a Region, use it to define the COORDS properties. */
+ if( astMapGet0A( (AstKeyMap *) object, "COORDS", &obj ) ) {
+ if( astIsARegion( obj ) ) {
+ coords = (AstRegion *) obj;
+ } else {
+ wantclass = "Region";
+ errclass = astGetClass( obj );
+ errname = "COORDS";
+ obj = astAnnul( obj );
+ }
+ }
+
+/* If the supplied KeyMap contains an entry with key "PROPS", and if it is
+ a KeyMap, use it to define values for the properties that cannot be
+ determined from the supplied Regions (Resolution, PixSize, etc). */
+ if( astMapGet0A( (AstKeyMap *) object, "PROPS", &obj ) ) {
+ if( astIsAKeyMap( obj ) ) {
+ props = (AstKeyMap *) obj;
+ } else {
+ wantclass = "KeyMap";
+ errclass = astGetClass( obj );
+ errname = "PROPS";
+ obj = astAnnul( obj );
+ }
+ }
+
+/* If the supplied KeyMap contains an entry with any of the keys
+ "TIME_PROPS", "SPACE_PROPS", "SPECTRAL_PROPS" or "REDSHIFT_PROPS",
+ use the supplied KeyMap to define values for all properties. */
+ if( astMapGet0A( (AstKeyMap *) object, "TIME_PROPS", &obj ) ||
+ astMapGet0A( (AstKeyMap *) object, "SPACE_PROPS", &obj ) ||
+ astMapGet0A( (AstKeyMap *) object, "SPECTRAL_PROPS", &obj ) ||
+ astMapGet0A( (AstKeyMap *) object, "REDSHIFT_PROPS", &obj ) ) {
+ props = astClone( object );
+ }
+
+/* Report an error if the Object in the keymap has the wrong type. */
+ if( errname && astOK ) {
+ astAddWarning( this, 1, "The supplied KeyMap contains a %s "
+ "called '%s'. But '%s' should be a %s "
+ "(programming error).", method, status,
+ errclass, errname, errname, wantclass );
+ }
+
+/* Report an error if the keymap contains none of the above. */
+ if( !area && !coords && !props && astOK ) {
+ astAddWarning( this, 1, "The supplied KeyMap does not "
+ "contains anything that can be written out "
+ "through a %s.", method, status, class );
+ }
+
+/* If both COORDS and AREA were supplied, ensure they are in the same
+ Frame by mapping the COORDS Region into the Frame of the AREA Region. */
+ if( area && coords ) {
+ fs = astConvert( coords, area, " " );
+ if( fs ) {
+ map = astGetMapping( fs, AST__BASE, AST__CURRENT );
+ frm = astGetFrame( fs, AST__CURRENT );
+
+ new_coords = astMapRegion( coords, map, frm );
+
+ map = astAnnul( map );
+ frm = astAnnul( frm );
+ coords = astAnnul( coords );
+ fs = astAnnul( fs );
+
+ coords = new_coords;
+
+ } else if( astOK ){
+ astAddWarning( this, 1, "Cannot convert between the co-ordinate "
+ "frame of the COORDS Region and the co-ordinate "
+ "frame of the AREA Region.", method, status );
+ }
+ }
+
+/* Report an error if the supplied object is neither a KeyMap nor a
+ Region. */
+ } else if( astOK ) {
+ astAddWarning( this, 1, "Failed to write out a %s through a %s. "
+ "The %s class cannot be used to write out a %s.",
+ method, status, astGetClass( object ), class, class,
+ astGetClass( object ) );
+ }
+
+
+/* If we do not have a KeyMap in which to store the STC-S properties,
+ create one now. */
+ if( astOK ) {
+ if( ! props ) props = astKeyMap ( " ", status );
+
+/* Determine the set of STC-S properties that describe the COORDS Region,
+ and add them into the properties keymap, over-writing any values for the
+ same properties that are already in the props keymap. */
+ ret = coords ? WriteRegion( this, coords, props, status ) : 1;
+
+/* Determine the set of STC-S properties that describe the AREA Region,
+ and add them into the properties keymap, over-writing any values for the
+ same properties that are already in the props keymap. NB, we need to
+ do AREA after COORDS so that the sub-phrase identifier implied by the
+ AREA is used in preference to that implied by the COORDS. */
+ if( area && ret ) ret = WriteRegion( this, area, props, status );
+
+/* Convert the properties list into text and write it out through the
+ parent Channel's sink function. */
+ if( ret ) WriteProps( this, props, status );
+ }
+
+/* Free resources. */
+ if( area ) area = astAnnul( area );
+ if( coords ) coords = astAnnul( coords );
+ if( props ) props = astAnnul( props );
+
+/* If an error has occurred, return zero. */
+ if( !astOK ) ret = 0;
+
+/* Return the answer. */
+ return ret;
+}
+
+static void WriteProps( AstStcsChan *this, AstKeyMap *props, int *status ){
+/*
+* Name:
+* WriteProps
+
+* Purpose:
+* Write out a set of STC-S properties to the sink function.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* void WriteProps( AstStcsChan *this, AstKeyMap *props, int *status )
+
+* Class Membership:
+* StcsChan member function
+
+* Description:
+* This function converts the STC-S properties supplied in a KeyMap
+* into text, and writes the text out through the sink function associated
+* with the parent Channel.
+
+* Parameters:
+* this
+* Pointer to the StcsChan.
+* props
+* Pointer to the KeyMap holding the STC-S properties.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ AstKeyMap *spprops; /* Sub-phrase properties */
+ AstObject *obj; /* Generic Object pointer */
+ char *line; /* Dynamically allocated buffer for output text */
+ const char *id; /* Sub-phrase identifier */
+ const char *prefix; /* Prefix for property value */
+ int nc; /* Number of characters in "line" */
+ int pretty; /* Include new-lines and indentation in returned text? */
+ int crem; /* Character remaining on current output line */
+ int linelen; /* Line length */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Initialise things. */
+ nc = 0;
+ line = NULL;
+
+/* See if indentation and new-lines are to be added to the output text to
+ make it look pretty. */
+ pretty = astGetIndent( this );
+
+/* If so, get the line length to use, and initialise the number of
+ remaining characters in the current output line. */
+ if( pretty ) {
+ linelen = astGetStcsLength( this );
+ } else {
+ linelen = 0;
+ }
+ crem = linelen;
+
+/* Add each word in the time sub-phrase into the output buffer, in the
+ order defined by the STC-S standard. */
+ if( astMapGet0A( props, "TIME_PROPS", &obj ) ) {
+ spprops = (AstKeyMap *) obj;
+
+ line = AddItem( this, spprops, "ID", NULL, line, &nc, &crem, linelen, status );
+ astMapGet0C( spprops, "ID", &id );
+
+ line = AddItem( this, spprops, "FILLFACTOR", "fillfactor ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "TIMESCALE", NULL, line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "REFPOS", NULL, line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "START", NULL, line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "STOP", NULL, line, &nc, &crem, linelen, status );
+
+ prefix = !astChrMatch( id, "Time" ) ? "Time " : NULL;
+ line = AddItem( this, spprops, "TIME", prefix, line, &nc, &crem, linelen, status );
+
+ line = AddItem( this, spprops, "UNIT", "unit ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "ERROR", "Error ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "RESOLUTION", "Resolution ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "PIXSIZE", "PixSize ", line, &nc, &crem, linelen, status );
+
+ spprops = astAnnul( spprops );
+
+/* Write out the time sub-phrase text through the Channel sink function. */
+ if( pretty && astChrLen( line ) ) {
+ astPutNextText( this, line );
+ nc = 0;
+ crem = linelen;
+ }
+ }
+
+/* Add each word in the space sub-phrase into the output buffer, in the
+ order defined by the STC-S standard. */
+ if( astMapGet0A( props, "SPACE_PROPS", &obj ) ) {
+ spprops = (AstKeyMap *) obj;
+
+ line = AddItem( this, spprops, "ID", NULL, line, &nc, &crem, linelen, status );
+ astMapGet0C( spprops, "ID", &id );
+
+ line = AddItem( this, spprops, "FILLFACTOR", "fillfactor ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "FRAME", NULL, line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "REFPOS", NULL, line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "FLAVOUR", NULL, line, &nc, &crem, linelen, status );
+
+ line = PutRegionProps( this, spprops, id, (pretty ? 0 : -1), line, &nc,
+ &crem, linelen, status );
+
+ prefix = !astChrMatch( id, "Position" ) ? "Position " : NULL;
+ line = AddItem( this, spprops, "POSITION", prefix, line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "UNIT", "unit ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "ERROR", "Error ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "RESOLUTION", "Resolution ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "SIZE", "Size ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "PIXSIZE", "PixSize ", line, &nc, &crem, linelen, status );
+
+ spprops = astAnnul( spprops );
+
+/* Write out the spatial sub-phrase text through the Channel sink function. */
+ if( pretty && astChrLen( line ) ) {
+ astPutNextText( this, line );
+ nc = 0;
+ crem = linelen;
+ }
+ }
+
+/* Add each word in the spectral sub-phrase into the output buffer, in the
+ order defined by the STC-S standard. */
+ if( astMapGet0A( props, "SPECTRAL_PROPS", &obj ) ) {
+ spprops = (AstKeyMap *) obj;
+
+ line = AddItem( this, spprops, "ID", NULL, line, &nc, &crem, linelen, status );
+ astMapGet0C( spprops, "ID", &id );
+
+ line = AddItem( this, spprops, "FILLFACTOR", "fillfactor ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "REFPOS", NULL, line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "LOLIMIT", NULL, line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "HILIMIT", NULL, line, &nc, &crem, linelen, status );
+
+ prefix = !astChrMatch( id, "Spectral" ) ? "Spectral " : NULL;
+ line = AddItem( this, spprops, "SPECTRAL", prefix, line, &nc, &crem, linelen, status );
+
+ line = AddItem( this, spprops, "UNIT", "unit ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "ERROR", "Error ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "RESOLUTION", "Resolution ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "PIXSIZE", "PixSize ", line, &nc, &crem, linelen, status );
+
+ spprops = astAnnul( spprops );
+
+/* Write out the spectral sub-phrase text through the Channel sink function. */
+ if( pretty && astChrLen( line ) ) {
+ astPutNextText( this, line );
+ nc = 0;
+ crem = linelen;
+ }
+ }
+
+/* Add each word in the redshift sub-phrase into the output buffer, in the
+ order defined by the STC-S standard. */
+ if( astMapGet0A( props, "REDSHIFT_PROPS", &obj ) ) {
+ spprops = (AstKeyMap *) obj;
+
+ line = AddItem( this, spprops, "ID", NULL, line, &nc, &crem, linelen, status );
+ astMapGet0C( spprops, "ID", &id );
+
+ line = AddItem( this, spprops, "FILLFACTOR", "fillfactor ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "REFPOS", NULL, line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "TYPE", NULL, line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "DOPPLERDEF", NULL, line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "LOLIMIT", NULL, line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "HILIMIT", NULL, line, &nc, &crem, linelen, status );
+
+ prefix = !astChrMatch( id, "Redshift" ) ? "Redshift " : NULL;
+ line = AddItem( this, spprops, "REDSHIFT", prefix, line, &nc, &crem, linelen, status );
+
+ line = AddItem( this, spprops, "UNIT", "unit ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "ERROR", "Error ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "RESOLUTION", "Resolution ", line, &nc, &crem, linelen, status );
+ line = AddItem( this, spprops, "PIXSIZE", "PixSize ", line, &nc, &crem, linelen, status );
+
+ spprops = astAnnul( spprops );
+
+/* Write out the redshift sub-phrase text through the Channel sink function. */
+ if( pretty && astChrLen( line ) ) {
+ astPutNextText( this, line );
+ nc = 0;
+ crem = linelen;
+ }
+ }
+
+/* Write out any remaining text through the Channel sink function. */
+ if( nc && astChrLen( line ) ) astPutNextText( this, line );
+
+/* Free resources. */
+ line = astFree( line );
+
+}
+
+static int WriteRegion( AstStcsChan *this, AstRegion *reg, AstKeyMap *props,
+ int *status ){
+/*
+* Name:
+* WriteRegion
+
+* Purpose:
+* Convert a Region into a set of STC-S properties and store them in a
+* KeyMap.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* int WriteRegion( AstStcsChan *this, AstRegion *reg, AstKeyMap *props,
+* int *status )
+
+* Class Membership:
+* StcsChan member function
+
+* Description:
+* This function attempts to convert the supplied Region nto a set of
+* STC-S properties, and stores them in the supplied KeyMap.
+
+* Parameters:
+* this
+* Pointer to the StcsChan being used.
+* reg
+* Pointer to the region to be converted.
+* props
+* Pointer to the KeyMap in which to store the STC-S properties.
+* On exit, each STC-S sub-phrase has an entry in this KeyMap,
+* and each of these entries has a value that is another KeyMap
+* holding the properties for the sub-phrase.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A non-zero value is returned if the conversion was succesful, and
+* zero is returned otherwise.
+*/
+
+/* Local Variables: */
+ AstFrame *efrm; /* Pointer to encapsulated Frame */
+ AstFrame *pfrm; /* Pointer to primary Frame cntaining an axis */
+ AstFrame *spfrm; /* The sub-phrase Frame */
+ AstKeyMap *spprops; /* Sub-phrase properties */
+ AstMapping *map; /* Base->current Region Mapping */
+ AstMapping *sreg; /* Simplified Region */
+ AstObject *obj; /* Generic object pointer */
+ AstRegion *spreg; /* The sub-phrase Region */
+ AstRegion *treg; /* Temporary Region pointer */
+ AstRegion *unc; /* Uncertainty region */
+ AstRegion *unca; /* Adaptive uncertainty region */
+ AstStdOfRestType sor; /* StdOfRest attribute value */
+ AstSystemType sys; /* System attribute value */
+ char *prop; /* Formatted property string */
+ char *unit1; /* Pointer to string holding first axis unit */
+ char buf[ 100 ]; /* Buffer for formatted values */
+ char fmt[ 10 ]; /* Buffer for format specifier */
+ const char *class; /* Class name */
+ const char *dom; /* Domain name */
+ const char *dopdef; /* DopplerDef value */
+ const char *flavour; /* The STC-S flavour for the space frame */
+ const char *q; /* Pointer to next character */
+ const char *tfrm; /* STC-S string for Frame */
+ const char *tsor; /* STC-S string for RefPos */
+ const char *tts; /* Time scale label */
+ const char *type; /* Redshift Type value */
+ const char *unit; /* Unit string */
+ double *pcen; /* Pointer to Circle or ellipse centre */
+ double equinox; /* The required equinox value */
+ double error; /* Axis error value */
+ double fill; /* Fill factor */
+ double lbnd[ 3 ]; /* Region lower bounds */
+ double lim; /* Unlimited bounds value */
+ double p1[ 2 ]; /* End point of error line */
+ double scale; /* Factor for scaling Region values into required units */
+ double ubnd[ 3 ]; /* Region upper bounds */
+ int allthesame; /* Do all axes have the same units? */
+ int defdigs; /* Default number of digits */
+ int defs; /* Include default values in output STC-S? */
+ int i; /* Loop index */
+ int issky; /* Do the space axes form a SkyFrame? */
+ int nax; /* The number of axes */
+ int nc; /* Number of characters in "prop" string */
+ int nspace; /* Number of space axes */
+ int ok; /* Can the Region be written out? */
+ int pax; /* Index of axis in primary Frame */
+ int redax; /* The index of the redshift axis */
+ int retain_units; /* Retain the units/system in properties KeyMap? */
+ int spaceax[ 3 ]; /* Indicies of the space axes */
+ int spaceid; /* Code for space sub-phrase identifier */
+ int specax; /* The index of the spectral axis */
+ int timeax; /* Index of time axis */
+ int ts; /* Time scale identifier */
+
+/* Check the global error status. */
+ if ( !astOK ) return 0;
+
+/* Initialise things to avoid comiler warnings. */
+ sys = AST__BADSYSTEM;
+
+/* Assume we can do the conversion. */
+ ok = 1;
+
+/* See if default values are to be included in the output. */
+ defs = ( astGetFull( this ) > 0 );
+
+/* STC-S requires that the spatial shape (circle, box. etc) refers to
+ the coordinate system described by the STC-S. This is not quite like
+ AST, in that the AST class type (Circle, Box, etc) defines the
+ shape of the region in the base Frame, rather than the current Frame.
+ So we can only write the Region out using STC-S if the shape in the
+ current Frame is the same as the shape in the base Frame. This is the
+ case if the simplified Mapping connecting base and current Frames is
+ a UnitMap. Get the base->current Mapping from the Region. */
+ map = astRegMapping( reg );
+
+/* If it is not UnitMap, see if simplifying the whole Region results in
+ the base->current Mapping in the simplified Region being a UnitMap. */
+ if( !astIsAUnitMap( map ) ) {
+ map = astAnnul( map );
+ sreg = astSimplify( reg );
+ map = astRegMapping( sreg );
+
+/* If it is still not UnitMap, we cannot write out the region. */
+ if( !astIsAUnitMap( map ) ) {
+ astAddWarning( this, 1, "The supplied Region does not have a "
+ "supported shape within its current coordinate "
+ "system.", "astWrite", status );
+ ok = 0;
+ }
+
+ } else {
+ sreg = astClone( reg );
+ }
+ map = astAnnul( map );
+
+/* Store a safe value that can be used to test unbounded axes. */
+ lim = sqrt( DBL_MAX );
+
+/* First job is to identify the Time, Space, Spectral and Redshift axes
+ in the supplied Region.
+ ------------------------------------------------------------------- */
+
+/* Initialise things. */
+ timeax = -1;
+ nspace = 0;
+ issky = 0;
+ specax = -1;
+ redax = -1;
+ prop = NULL;
+
+/* Get a pointer to the Frame encapsulated by the Region. */
+ efrm = astRegFrame( sreg );
+
+/* Loop round all axes. */
+ nax = astGetNaxes( sreg );
+ for( i = 0; i < nax; i++ ) {
+
+/* Get the primary Frame that defines the current axis of the Region. */
+ astPrimaryFrame( efrm, i, &pfrm, &pax );
+
+/* Get its class and domain. */
+ class = astGetClass( pfrm );
+ dom = astGetDomain( pfrm );
+ if( astOK ) {
+
+/* The time axis is described by a TimeFrame with any domain. */
+ if( !strcmp( class, "TimeFrame" ) ) {
+ if( timeax == -1 ) {
+ timeax = i;
+ } else {
+ astAddWarning( this, 1, "More than one time axis found. "
+ "Extra axis (axis %d) will be ignored.",
+ "astWrite", status, i + 1 );
+ }
+
+/* The space axes are described by a SkyFrame or a basic Frame. If a
+ mixture of both types are found, report a warning and ignore the later
+ axes. */
+ } else if( !strcmp( class, "SkyFrame" ) ) {
+ if( issky || nspace == 0 ) {
+ if( nspace < 2 ) {
+ spaceax[ nspace++ ] = i;
+ issky = 1;
+ } else {
+ astAddWarning( this, 1, "More than two sky frame axes "
+ "found. Extra axis (axis %d) will be ignored.",
+ "astWrite", status, i + 1 );
+ }
+
+ } else {
+ astAddWarning( this, 1, "Mixture of basic and sky frame "
+ "axes found. Sky frame axis %d will be "
+ "ignored.", "astWrite", status, i + 1 );
+ }
+
+ } else if( !strcmp( class, "Frame" ) ) {
+ if( !issky ) {
+ if( nspace < 3 ) {
+ spaceax[ nspace++ ] = i;
+ } else {
+ astAddWarning( this, 1, "More than three basic space frame axes "
+ "found. Extra axis (axis %d) will be ignored.",
+ "astWrite", status, i + 1 );
+ }
+
+ } else {
+ astAddWarning( this, 1, "Mixture of basic and sky frame "
+ "axes found. Basic frame axis %d will be "
+ "ignored.", "astWrite", status, i + 1 );
+ }
+
+/* The spectral axis is described by a SpecFrame with domain SPECTRUM. */
+ } else if( !strcmp( class, "SpecFrame" ) &&
+ !strcmp( dom, "SPECTRUM" ) ) {
+ if( specax == -1 ) {
+ specax = i;
+ } else {
+ astAddWarning( this, 1, "More than one spectral axis found. "
+ "Extra axis (axis %d) will be ignored.",
+ "astWrite", status, i + 1 );
+ }
+
+/* The redshift axis is described by a SpecFrame with domain REDSHIFT. */
+ } else if( !strcmp( class, "SpecFrame" ) &&
+ !strcmp( dom, "REDSHIFT" ) ) {
+ if( redax == -1 ) {
+ redax = i;
+ } else {
+ astAddWarning( this, 1, "More than one redshift axis found. "
+ "Extra axis (axis %d) will be ignored.",
+ "astWrite", status, i + 1 );
+ }
+
+/* Warn about unused axes. */
+ } else {
+ astAddWarning( this, 1, "Could not classify axis %d (class=%s "
+ "domain=%s). It will be ignored.", "astWrite", status,
+ i + 1, class, dom );
+ }
+ }
+
+/* Free resources. */
+ pfrm = astAnnul( pfrm );
+ }
+ efrm = astAnnul( efrm );
+
+/* Set a flag indicating if there is anything to convert. */
+ ok = ok && ( timeax != -1 || nspace > 0 || specax != -1 || redax != -1 );
+
+
+/* Now we have identified the axes, we convert each available STC-S
+ sub-phrase, starting with the time sub-phrase.
+ ---------------------------------------------------------------- */
+ if( timeax != -1 ) {
+
+/* Create a Region by picking the time axis from the supplied Region. */
+ spreg = astPickAxes( sreg, 1, &timeax, NULL );
+
+/* Check it is a Region. If not, we cannot convert anything. */
+ if( !astIsARegion( spreg ) ) {
+ astAddWarning( this, 1, "Cannot determine the region covered by "
+ "the time axis.", "astWrite", status );
+ ok = 0;
+
+/* Otherwise we add a description of the time sub-phrase to the
+ properties keymap. */
+ } else {
+
+/* Get a pointer to the Region's time phrase property KeyMap, creating
+ one if necessary. */
+ if( astMapGet0A( props, "TIME_PROPS", &obj ) ) {
+ spprops = (AstKeyMap *) obj;
+ } else {
+ spprops = astKeyMap( " ", status );
+ astMapPut0A( props, "TIME_PROPS", spprops, NULL );
+ }
+
+/* Get the Region's fill factor. */
+ fill = astGetFillFactor( spreg );
+
+/* Ensure the TimeFrame represents MJD. If not, take a deep copy (to
+ avoid changing the supplied Region), and set its system to MJD. */
+ if( astGetSystem( spreg ) != AST__MJD ) {
+ treg = astCopy( spreg );
+ (void) astAnnul( spreg );
+ spreg = treg;
+ astSetAdaptive( spreg, 1 );
+ astSetSystem( spreg, AST__MJD );
+ }
+
+/* Get the bounds of the Region (i.e. the time axis coverage). */
+ astGetRegionBounds( spreg, lbnd, ubnd );
+
+/* Get a pointer to the time Region's encapsulated Frame. */
+ spfrm = astRegFrame( spreg );
+
+/* Report a warning if the sub-phrase Frame is not a TimeFrame */
+ if( !astIsATimeFrame( spfrm ) ) {
+ ok = 0;
+ astAddWarning( this, 1, "The time sub-phrase in the supplied "
+ "KeyMap is not described using an AST TimeFrame.",
+ "astWrite", status );
+
+/* Store properties that are specific to Time moments... */
+ } else if( lbnd[ 0 ] == ubnd[ 0 ] ) {
+ astMapPut0C( spprops, "ID", "Time", NULL );
+ StoreTimeProp( spprops, (AstTimeFrame *) spfrm, "TIME", lbnd[ 0 ], status );
+ fill = AST__BAD;
+
+/* Store properties that are specific to Time intervals... */
+ } else if( lbnd[ 0 ] > -lim && ubnd[ 0 ] < lim ) {
+ astMapPut0C( spprops, "ID", "TimeInterval", NULL );
+ StoreTimeProp( spprops, (AstTimeFrame *) spfrm, "START", lbnd[ 0 ], status );
+ StoreTimeProp( spprops, (AstTimeFrame *) spfrm, "STOP", ubnd[ 0 ], status );
+
+/* Store properties that are specific to Start times... */
+ } else if( lbnd[ 0 ] > -lim ) {
+ astMapPut0C( spprops, "ID", "StartTime", NULL );
+ StoreTimeProp( spprops, (AstTimeFrame *) spfrm, "START", lbnd[ 0 ], status );
+
+/* Store properties that are specific to Stop times... */
+ } else {
+ astMapPut0C( spprops, "ID", "StopTime", NULL );
+ StoreTimeProp( spprops, (AstTimeFrame *) spfrm, "STOP", ubnd[ 0 ], status );
+
+ }
+
+/* Store properties that are common to all time sub-phrase types. First the
+ fill factor. */
+ MapPut0D( spprops, "FILLFACTOR", fill, 1.0, defs, status );
+
+/* Now the time scale. */
+ ts = astGetTimeScale( spfrm );
+ if( ts == AST__TT ) {
+ tts = "TT";
+
+ } else if( ts == AST__TAI ) {
+ tts = "TAI";
+
+ } else if( ts == AST__UTC ) {
+ tts = "UTC";
+
+ } else if( ts == AST__TDB ) {
+ tts = "TDB";
+
+ } else if( ts == AST__TCG ) {
+ tts = "TCG";
+
+ } else if( ts == AST__TCB ) {
+ tts = "TCB";
+
+ } else if( ts == AST__LMST ) {
+ tts = "LST";
+
+ } else {
+ tts = "nil";
+ astAddWarning( this, 1, "Timescale '%s' is unsupported by "
+ "STC-S.", "astWrite", status,
+ astGetC( spfrm, "TimeScale" ) );
+ ok = 0;
+ }
+
+ MapPut0C( spprops, "TIMESCALE", tts, "nil", defs, status );
+
+/* RefPos. The AST TimeFrame class has no reference position, we leave
+ unchanged any refpos already in the keymap. If there is no refpos in the
+ keymap, we use "TOPOCENTER". */
+ if( !astMapHasKey( spprops, "REFPOS" ) ) {
+ astMapPut0C( spprops, "REFPOS", "TOPOCENTER", NULL );
+ }
+
+/* That's it for the time sub-phrase, unless the supplied Region has an
+ explicit (non-default) uncertainty. */
+ unc = astGetUnc( spreg, 0 );
+ if( unc ) {
+
+/* See if the supplied properties KeyMap contains any item that refers to
+ the Unit included in the STC-S description, but which is not updated by
+ this function. If it does, we need to retain any units specified
+ within the KeyMap. */
+ retain_units = ( astMapHasKey( spprops, "RESOLUTION" ) ||
+ astMapHasKey( spprops, "PIXSIZE" ) ||
+ astMapHasKey( spprops, "SIZE" ) );
+
+ if( retain_units ) {
+ if( !astMapGet0C( spprops, "UNIT", &unit ) ) unit = "s";
+ } else {
+ unit = "s";
+ }
+
+/* Store the units string */
+ MapPut0C( spprops, "UNIT", unit, "s", defs, status );
+
+/* If necessary, map the uncertainty region into the requied units. Take
+ a deep copy to avoid changing the supplied Region. */
+ if( strcmp( unit, astGetUnit( unc, 0 ) ) ) {
+ unca = astCopy( unc );
+ astSetAdaptive( unca, 0 );
+ astSetUnit( unca, 0, unit );
+ } else {
+ unca = astClone( unc );
+ }
+
+/* Get the bounds of the uncertainty. */
+ astGetRegionBounds( unca, lbnd, ubnd );
+
+/* The error is half the width of the bounding box. */
+ astMapPut0D( spprops, "ERROR", 0.5*( ubnd[ 0 ] - lbnd[ 0 ] ), NULL );
+
+/* Free resources. */
+ unca = astAnnul( unca );
+ unc = astAnnul( unc );
+ }
+
+/* Free resources. */
+ spfrm = astAnnul( spfrm );
+ spprops = astAnnul( spprops );
+ }
+
+/* Free resources. */
+ spreg = astAnnul( spreg );
+
+ }
+
+
+/* Now convert the space sub-phrase.
+ ---------------------------------------------------------------- */
+ if( nspace > 0 && ok ) {
+
+/* Create a Region by picking the space axes from the supplied Region. */
+ spreg = astPickAxes( sreg, nspace, spaceax, NULL );
+
+/* Check it is a Region. If not, we cannot convert anything. */
+ if( ! astIsARegion( spreg ) ) {
+ astAddWarning( this, 1, "Cannot determine the region covered by "
+ "the space axes.", "astWrite", status );
+ ok = 0;
+
+/* Otherwise we add a description of the space sub-phrase to the
+ properties keymap. */
+ } else {
+
+/* Get a pointer to the Region's space phrase property KeyMap, creating
+ one if necessary. */
+ if( astMapGet0A( props, "SPACE_PROPS", &obj ) ) {
+ spprops = (AstKeyMap *) obj;
+ } else {
+ spprops = astKeyMap( " ", status );
+ astMapPut0A( props, "SPACE_PROPS", spprops, NULL );
+ }
+
+/* If the space frame is a SkyFrame, ensure it refers to a coodinate
+ system that is supported by STC-S. Take a deep copy before changing
+ anything. */
+ if( issky ) {
+ sys = astGetSystem( spreg );
+ if( sys != AST__FK4 &&
+ sys != AST__FK5 &&
+ sys != AST__ICRS &&
+ sys != AST__ECLIPTIC &&
+ sys != AST__GALACTIC &&
+ sys != AST__SUPERGALACTIC &&
+ sys != AST__UNKNOWN ) {
+ treg = astCopy( spreg );
+ (void) astAnnul( spreg );
+ spreg = treg;
+ astSetAdaptive( spreg, 1 );
+ astSetSystem( spreg, AST__ICRS );
+ }
+ }
+
+/* Get a pointer to the Region's encapsulated Frame. */
+ spfrm = astRegFrame( spreg );
+
+/* If the supplied Region is defined in a SkyFrame, choose the units to
+ use when storing radius, error, etc in the KeyMap. If the props KeyMap
+ already contains a unit specification, we use it. Otherwise we use the
+ default (degrees). AST uses radians internally, so find the scaling
+ factor. */
+ if( issky ) {
+ if( astMapGet0C( spprops, "UNIT", &unit ) ) {
+ if( !strcmp( unit, "arcmin" ) ) {
+ scale = AST__DR2D*60.0;
+ } else if( !strcmp( unit, "arcsec" ) ) {
+ scale = AST__DR2D*3600.0;
+ } else {
+ unit = "deg";
+ scale = AST__DR2D;
+ }
+ } else {
+ unit = "deg";
+ scale = AST__DR2D;
+ }
+
+/* Store the units string */
+ MapPut0C( spprops, "UNIT", unit, "deg", defs, status );
+
+/* If the supplied Region is not defined in a SkyFrame, we will arrange
+ that the Region and the KeyMap use the same units, so set a scale
+ factor of 1.0. */
+ } else {
+ scale = 1.0;
+
+/* See if the supplied properties KeyMap contains any item that refers to
+ the Unit included in the STC-S description, but which is not updated by
+ this function. If it does, we need to retain any units specified
+ within the KeyMap. */
+ retain_units = ( astMapHasKey( spprops, "RESOLUTION" ) ||
+ astMapHasKey( spprops, "PIXSIZE" ) ||
+ astMapHasKey( spprops, "SIZE" ) );
+
+/* If so, and if the properties KeyMap already contains a Unit
+ specification, we convert the Region to the same units. Take a deep
+ copy of the Region first to avoid modifying the supplied Region. */
+ if( retain_units ) {
+ if( !astMapGet0C( spprops, "UNIT", &unit ) ) unit = "deg";
+
+ treg = astCopy( spreg );
+ (void) astAnnul( spreg );
+ spreg = treg;
+
+ for( i = 0; i < nspace; i++ ) {
+ astSetUnit( spreg, i, unit );
+
+/* Space frames can have different units on different axes. So look for
+ the start of the next word in the Unit propert. This will be the unit
+ for the next axis. If there are no more words in the Unit property,
+ re-use the last unit value. */
+ q = unit;
+ while( *q && !isspace( *q ) ) q++;
+ while( *q && isspace( *q ) ) q++;
+ if( *q ) unit = q;
+ }
+
+/* If we are not retaining the units specified in the properties KeyMap, we
+ retain the existing Region units instead, and store these units in the
+ properties KeyMap. We also check that these units are supported by
+ STC-S. */
+ } else {
+
+ nc = 0;
+ allthesame = 1;
+ unit1 = NULL;
+
+ for( i = 0; i < nspace; i++ ) {
+ unit = astGetUnit( spreg, i );
+
+ if( !unit1 ) {
+ unit1 = astStore( NULL, unit, strlen( unit ) + 1 );
+ } else {
+ if( strcmp( unit, unit1 ) ) allthesame = 0;
+ }
+
+ if( strcmp( unit, "deg" ) &&
+ strcmp( unit, "arcmin" ) &&
+ strcmp( unit, "arcsec" ) &&
+ strcmp( unit, "m" ) &&
+ strcmp( unit, "mm" ) &&
+ strcmp( unit, "km" ) &&
+ strcmp( unit, "AU" ) &&
+ strcmp( unit, "pc" ) &&
+ strcmp( unit, "kpc" ) &&
+ strcmp( unit, "Mpc" ) ) {
+ astAddWarning( this, 1, "Cannot use spatial units '%s'.",
+ "astWrite", status, unit );
+ ok = 0;
+ break;
+ }
+ prop = astAppendString( prop, &nc, unit );
+ prop = astAppendString( prop, &nc, " " );
+ }
+
+/* Remove the trailing space, and store the property value in the KeyMap. */
+ if( ! allthesame ) {
+ if( prop && nc > 0 ) {
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( spprops, "UNIT", prop, NULL );
+ }
+ } else {
+ astMapPut0C( spprops, "UNIT", unit1, NULL );
+ }
+
+ unit1 = astFree( unit1 );
+
+ }
+ }
+
+/* Get the fill factor. */
+ fill = astGetFillFactor( spreg );
+
+/* Get the default number of digits. This is only used if the supplied
+ properties KeyMap does not have a value for the item being stored. If
+ it does, the number of digits is inherited form the value int he KeyMap. */
+ defdigs = astGetDigits( spfrm );
+
+/* Store properties that are specific to the particular type of Region. */
+ spaceid = GetRegionProps( this, spreg, spprops, nspace, defdigs,
+ scale, issky, status );
+ if( spaceid == NULL_ID ) ok = 0;
+
+/* If the above went OK, store values for the properties that are common
+ to all types of space sub-phrase. */
+ if( ok ) {
+
+/* First the fill factor. */
+ if( spaceid != POSITION_ID ) {
+ MapPut0D( spprops, "FILLFACTOR", fill, 1.0, defs, status );
+ }
+
+/* Now the coordinate frame. */
+ tfrm = NULL;
+ sys = astGetSystem( spfrm );
+ if( issky ) {
+ if( sys == AST__FK4 ){
+ tfrm = "B1950";
+ equinox = 1950.0;
+
+ } else if( sys == AST__FK5 ){
+ tfrm = "J2000";
+ equinox = 2000.0;
+
+ } else if( sys == AST__ICRS ){
+ tfrm = "ICRS";
+ equinox = AST__BAD;
+
+ } else if( sys == AST__ECLIPTIC ){
+ tfrm = "ECLIPTIC";
+ equinox = 2000.0;
+
+ } else if( sys == AST__GALACTIC ){
+ tfrm = "GALACTIC";
+ equinox = AST__BAD;
+
+ } else if( sys == AST__SUPERGALACTIC ){
+ tfrm = "SUPER_GALACTIC";
+ equinox = AST__BAD;
+
+ } else if( sys == AST__UNKNOWN ){
+ tfrm = NULL;
+ equinox = AST__BAD;
+
+ } else {
+ tfrm = NULL;
+ astAddWarning( this, 1, "Sky system '%s' is "
+ "unsupported by STC-S.", "astWrite",
+ status, astGetC( spfrm, "System" ) );
+ ok = 0;
+ }
+
+ if( tfrm && equinox != AST__BAD ) {
+ if( astGetD( spfrm, "Equinox" ) != equinox ) {
+ astAddWarning( this, 1, "STC-S requires an equinox "
+ "of %g for the %s frame, but the "
+ "supplied %s equinox is %g.", "astWrite",
+ status, equinox, tfrm,
+ astGetClass( spfrm ),
+ astGetD( spfrm, "Equinox" ) );
+ ok = 0;
+ tfrm = NULL;
+ }
+ }
+ }
+
+/* If we do not yet have a Frame, use the Domain value if it is set (and
+ is a legal STC-S Frame). */
+ if( ! tfrm ) {
+ if( astTestDomain( spfrm ) ) {
+ tfrm = astGetDomain( spfrm );
+ if( strcmp( tfrm, "ICRS" ) &&
+ strcmp( tfrm, "FK5" ) &&
+ strcmp( tfrm, "FK4" ) &&
+ strcmp( tfrm, "J2000" ) &&
+ strcmp( tfrm, "B1950" ) &&
+ strcmp( tfrm, "ECLIPTIC" ) &&
+ strcmp( tfrm, "GALACTIC" ) &&
+ strcmp( tfrm, "GALACTIC_II" ) &&
+ strcmp( tfrm, "SUPER_GALACTIC" ) &&
+ strcmp( tfrm, "GEO_C" ) &&
+ strcmp( tfrm, "GEO_D" ) ){
+ astAddWarning( this, 1, "'UNKNOWNFrame' being used in "
+ "place of unsupported frame '%s'.",
+ "astWrite", status, tfrm );
+ tfrm = NULL;
+ }
+ }
+ }
+
+/* Store the Frame name in the props keymap. */
+ if( !tfrm ) tfrm = "UNKNOWNFrame";
+ astMapPut0C( spprops, "FRAME", tfrm, NULL );
+
+/* RefPos. The AST SkyFrame and Frame classes have no reference position, so
+ we leave unchanged any refpos already in the props keymap. If there is
+ no refpos in the keymap, we use "TOPOCENTER". */
+ if( !astMapHasKey( spprops, "REFPOS" ) ) {
+ astMapPut0C( spprops, "REFPOS", "TOPOCENTER", NULL );
+ }
+
+/* Flavour. */
+ if( issky ) {
+ flavour = "SPHER2";
+ } else if( nspace == 1 ){
+ flavour = "CART1";
+ } else if( nspace == 2 ){
+ flavour = "CART2";
+ } else {
+ flavour = "CART3";
+ }
+ MapPut0C( spprops, "FLAVOUR", flavour, "SPHER2", defs, status );
+
+/* That's it for the space sub-phrase, unless the supplied Region has an
+ explicit (non-default) uncertainty. */
+ unc = astGetUnc( spreg, 0 );
+ if( unc ) {
+
+/* Get the bounds of the uncertainty. */
+ astGetRegionBounds( unc, lbnd, ubnd );
+
+/* If its a sky frame, find the position of the centre of the uncertainty
+ region. */
+ pcen = issky ? astRegCentre( unc, NULL, NULL, 0,
+ AST__CURRENT ) : NULL;
+
+/* Find the half-width of the bounding box for each space axis, and
+ concatenate their formatted values into a string. If any bound is
+ undefined, quit the axis loop with nc=0. We need to convert longitude
+ axis values from lingitude increments to arc-distance. */
+ nc = 0;
+ defdigs = astGetDigits( unc );
+
+ for( i = 0; i < nspace; i++ ) {
+ if( ubnd[ i ] != AST__BAD && lbnd[ i ] != AST__BAD ){
+
+ if( ! issky ) {
+ error = 0.5*( ubnd[ i ] - lbnd[ i ] );
+ } else {
+ if( i == 0 ) {
+ p1[ 0 ] = ubnd[ 0 ];
+ p1[ 1 ] = pcen[ 1 ];
+ } else {
+ p1[ 0 ] = pcen[ 0 ];
+ p1[ 1 ] = ubnd[ 1 ];
+ }
+ error = astDistance( spfrm, pcen, p1 );
+ }
+
+ GetFmt( "ERROR", spprops, i, defdigs, fmt, status );
+ (void) sprintf( buf, fmt, scale*error );
+ prop = astAppendString( prop, &nc, buf );
+ prop = astAppendString( prop, &nc, " " );
+
+ } else {
+ nc = 0;
+ break;
+ }
+ }
+
+/* If the bounds were all good, store the string holding the formatted
+ error values in the properties KeyMap. */
+ if( prop && nc > 0 ) {
+ prop[ nc - 1 ] = 0;
+ astMapPut0C( spprops, "ERROR", prop, NULL );
+ }
+
+/* Free resources. */
+ pcen = astFree( pcen );
+ unc = astAnnul( unc );
+ }
+ }
+
+/* Free resources. */
+ spfrm = astAnnul( spfrm );
+ spprops = astAnnul( spprops );
+ }
+
+/* Free resources. */
+ spreg = astAnnul( spreg );
+
+ }
+
+
+
+/* Convert the spectral sub-phrase.
+ ---------------------------------------------------------------- */
+ if( specax != -1 ) {
+
+/* Create a Region by picking the spectral axis from the supplied Region. */
+ spreg = astPickAxes( sreg, 1, &specax, NULL );
+
+/* Check it is a Region. If not, we cannot convert anything. */
+ if( !astIsARegion( spreg ) ) {
+ astAddWarning( this, 1, "Cannot determine the region covered by "
+ "the spectral axis.", "astWrite", status );
+ ok = 0;
+
+/* Otherwise we add a description of the spectral sub-phrase to the
+ properties keymap. */
+ } else {
+
+/* Get a pointer to the Region's spectral phrase property KeyMap, creating
+ one if necessary. */
+ if( astMapGet0A( props, "SPECTRAL_PROPS", &obj ) ) {
+ spprops = (AstKeyMap *) obj;
+ } else {
+ spprops = astKeyMap( " ", status );
+ astMapPut0A( props, "SPECTRAL_PROPS", spprops, NULL );
+ }
+
+/* See if the supplied properties KeyMap contains any item that refers to
+ the Unit included in the STC-S description, but which is not updated by
+ this function. If it does, we need to retain any units specified
+ within the KeyMap. */
+ retain_units = ( astMapHasKey( spprops, "RESOLUTION" ) ||
+ astMapHasKey( spprops, "PIXSIZE" ) ||
+ astMapHasKey( spprops, "SIZE" ) );
+
+/* If so, and if the properties KeyMap already contains a Unit specification,
+ we convert the Region to the same units and system. Determine the
+ required system and units. */
+ if( retain_units ) {
+ if( !astMapGet0C( spprops, "UNIT", &unit ) ) unit = "Hz";
+
+ if( !strcmp( unit, "Hz" ) ||
+ !strcmp( unit, "MHz" ) ||
+ !strcmp( unit, "GHz" ) ) {
+ sys = AST__FREQ;
+
+ } else if( !strcmp( unit, "m" ) ||
+ !strcmp( unit, "mm" ) ||
+ !strcmp( unit, "um" ) ||
+ !strcmp( unit, "nm" ) ||
+ !strcmp( unit, "Angstrom" ) ) {
+ sys = AST__WAVELEN;
+
+ } else if( !strcmp( unit, "eV" ) ||
+ !strcmp( unit, "keV" ) ||
+ !strcmp( unit, "MeV" ) ) {
+ sys = AST__ENERGY;
+
+ } else {
+ astAddWarning( this, 1, "Illegal STC-S units '%s' found in "
+ "supplied KeyMap", "astWrite", status, unit );
+ ok = 0;
+ }
+
+/* If we do not need to retain the units implied by the supplied KeyMap,
+ use the Units and system in the supplied Region so long as they are
+ supported by STC-S. If not, use a related supported system instead. */
+ } else {
+ sys = astGetSystem( spreg );
+ unit = astGetUnit( spreg, 0 );
+
+ if( sys == AST__ENERGY ) {
+ sys = AST__ENERGY;
+ if( strcmp( unit, "eV" ) &&
+ strcmp( unit, "keV" ) &&
+ strcmp( unit, "MeV" ) ) unit = "eV";
+
+ } else if( sys == AST__WAVELEN || sys == AST__AIRWAVE ||
+ sys == AST__VOPTICAL || sys == AST__REDSHIFT ){
+ sys = AST__WAVELEN;
+ if( strcmp( unit, "m" ) &&
+ strcmp( unit, "mm" ) &&
+ strcmp( unit, "um" ) &&
+ strcmp( unit, "nm" ) &&
+ strcmp( unit, "Angstrom" ) ) unit = "m";
+
+ } else {
+ sys = AST__FREQ;
+ if( strcmp( unit, "Hz" ) &&
+ strcmp( unit, "MHz" ) &&
+ strcmp( unit, "GHz" ) ) unit = "Hz";
+
+ }
+ }
+
+/* Store the units string */
+ MapPut0C( spprops, "UNIT", unit, "Hz", defs, status );
+
+/* If either the System or Unit needs to be changed in the Region, take a
+ deep copy first in order to avoid changing the supplied Region. */
+ if( sys != astGetSystem( spreg ) ||
+ ( unit && strcmp( unit, astGetUnit( spreg, 0 ) ) ) ) {
+ treg = astCopy( spreg );
+ (void) astAnnul( spreg );
+ spreg = treg;
+ astSetAdaptive( spreg, 1 );
+ astSetSystem( spreg, sys );
+ astSetUnit( spreg, 0, unit );
+ }
+
+/* Get the Region's fill factor. */
+ fill = astGetFillFactor( spreg );
+
+/* Get the bounds of the Region (i.e. the spectral axis coverage). */
+ astGetRegionBounds( spreg, lbnd, ubnd );
+
+/* Get a pointer to the spectral Region's encapsulated Frame. */
+ spfrm = astRegFrame( spreg );
+
+/* Report a warning if the sub-phrase Frame is not a SpecFrame */
+ if( !astIsASpecFrame( spfrm ) ) {
+ ok = 0;
+ astAddWarning( this, 1, "The spectral sub-phrase in the supplied "
+ "KeyMap is not described using an AST SpecFrame.",
+ "astWrite", status );
+
+/* Store properties that are specific to spectral positions... */
+ } else if( lbnd[ 0 ] == ubnd[ 0 ] ) {
+ astMapPut0C( spprops, "ID", "Spectral", NULL );
+ astMapPut0D( spprops, "SPECTRAL", lbnd[ 0 ], NULL );
+ fill = AST__BAD;
+
+/* Store properties that are specific to Spectral intervals... */
+ } else if( lbnd[ 0 ] > -lim && ubnd[ 0 ] < lim ) {
+ astMapPut0C( spprops, "ID", "SpectralInterval", NULL );
+ astMapPut0D( spprops, "LOLIMIT", lbnd[ 0 ], NULL );
+ astMapPut0D( spprops, "HILIMIT", ubnd[ 0 ], NULL );
+
+ } else {
+ ok = 0;
+ astAddWarning( this, 1, "Cannot write out an unbounded "
+ "spectral interval.", "astWrite", status );
+ }
+
+/* Store properties that are common to all spectral sub-phrase types. First the
+ fill factor. */
+ MapPut0D( spprops, "FILLFACTOR", fill, 1.0, defs, status );
+
+/* Now the reference position. */
+ sor = astGetStdOfRest( spfrm );
+ if( sor == AST__GESOR ) {
+ tsor = "GEOCENTER";
+
+ } else if( sor == AST__BYSOR ) {
+ tsor = "BARYCENTER";
+
+ } else if( sor == AST__HLSOR ) {
+ tsor = "HELIOCENTER";
+
+ } else if( sor == AST__TPSOR ) {
+ tsor = "TOPOCENTER";
+
+ } else if( sor == AST__LKSOR ) {
+ tsor = "LSRK";
+
+ } else if( sor == AST__LDSOR ) {
+ tsor = "LSRD";
+
+ } else if( sor == AST__GLSOR ) {
+ tsor = "GALACTIC_CENTER";
+
+ } else {
+ tsor = NULL;
+ }
+
+ if( !tsor ) tsor = "UNKNOWNRefPos";
+ MapPut0C( spprops, "REFPOS", tsor, "UNKNOWNRefPos", defs,
+ status );
+
+/* Now the unit string. */
+ MapPut0C( spprops, "UNIT", unit, "Hz", defs, status );
+
+/* That's it for the spectral sub-phrase, unless the supplied Region has an
+ explicit (non-default) uncertainty. */
+ unc = astGetUnc( spreg, 0 );
+ if( unc ) {
+
+/* Get the bounds of the uncertainty. */
+ astGetRegionBounds( unc, lbnd, ubnd );
+
+/* The error is half the width of the bounding box. */
+ astMapPut0D( spprops, "ERROR", 0.5*( ubnd[ 0 ] - lbnd[ 0 ] ), NULL );
+
+/* Free resources. */
+ unc = astAnnul( unc );
+ }
+
+/* Free resources. */
+ spfrm = astAnnul( spfrm );
+ spprops = astAnnul( spprops );
+ }
+
+/* Free resources. */
+ spreg = astAnnul( spreg );
+
+ }
+
+
+
+/* Convert the redshift sub-phrase.
+ ---------------------------------------------------------------- */
+ if( redax != -1 ) {
+
+/* Create a Region by picking the redshift axis from the supplied Region. */
+ spreg = astPickAxes( sreg, 1, &redax, NULL );
+
+/* Check it is a Region. If not, we cannot convert anything. */
+ if( !astIsARegion( spreg ) ) {
+ astAddWarning( this, 1, "Cannot determine the region covered by "
+ "the redshift axis.", "astWrite", status );
+ ok = 0;
+
+/* Otherwise we add a description of the redshift sub-phrase to the
+ properties keymap. */
+ } else {
+
+/* Get a pointer to the Region's redshift phrase property KeyMap, creating
+ one if necessary. */
+ if( astMapGet0A( props, "REDSHIFT_PROPS", &obj ) ) {
+ spprops = (AstKeyMap *) obj;
+ } else {
+ spprops = astKeyMap( " ", status );
+ astMapPut0A( props, "REDSHIFT_PROPS", spprops, NULL );
+ }
+
+/* See if the supplied properties KeyMap contains any item that refers to
+ the system included in the STC-S description, but which is not updated by
+ this function. If it does, we need to retain any system specified
+ within the KeyMap. */
+ retain_units = ( astMapHasKey( spprops, "RESOLUTION" ) ||
+ astMapHasKey( spprops, "PIXSIZE" ) ||
+ astMapHasKey( spprops, "SIZE" ) );
+
+/* If so, and if the properties KeyMap already contains a DopplerDef or
+ Type specification, we convert the Region to the same system. */
+ if( retain_units ){
+ if( !astMapGet0C( spprops, "DOPPLERDEF", &dopdef ) ) dopdef = "OPTICAL";
+ if( !astMapGet0C( spprops, "TYPE", &type ) ) type = "VELOCITY";
+
+ if( astChrMatch( type, "VELOCITY" ) ) {
+ if( astChrMatch( dopdef, "OPTICAL" ) ) {
+ sys = AST__VOPTICAL;
+ } else if( astChrMatch( dopdef, "RADIO" ) ) {
+ sys = AST__VRADIO;
+ } else if( astChrMatch( dopdef, "RELATIVISTIC" ) ) {
+ sys = AST__VREL;
+ } else {
+ astAddWarning( this, 1, "Illegal STC-S DopplerDef '%s' "
+ "found in supplied KeyMap", "astWrite", status,
+ dopdef );
+ ok = 0;
+ }
+
+ } else if( astChrMatch( type, "REDSHIFT" ) ) {
+ if( astChrMatch( dopdef, "OPTICAL" ) ) {
+ sys = AST__REDSHIFT;
+ } else {
+ astAddWarning( this, 1, "Unsupported combination of "
+ "DopplerDef='%s' and Type='%s' found in "
+ "supplied KeyMap", "astWrite", status, dopdef,
+ type );
+ ok = 0;
+ }
+
+ } else {
+ astAddWarning( this, 1, "Illegal STC-S Redshift Type '%s' "
+ "found in supplied KeyMap", "astWrite", status,
+ type );
+ ok = 0;
+ }
+
+/* If the supplied KeyMap does not imply the required system, use the
+ system in the supplied Region. */
+ } else {
+ sys = astGetSystem( spreg );
+ }
+
+/* Choose the requied units. */
+ unit = ( sys == AST__REDSHIFT ) ? "": "km/s";
+
+/* Store the units string */
+ MapPut0C( spprops, "UNIT", unit, unit, defs, status );
+
+/* If either the System or Unit needs to be changed in the Region, take a
+ deep copy first in order to avoid changing the supplied Region. */
+ if( sys != astGetSystem( spreg ) ||
+ ( unit && strcmp( unit, astGetUnit( spreg, 0 ) ) ) ) {
+ treg = astCopy( spreg );
+ (void) astAnnul( spreg );
+ spreg = treg;
+ astSetAdaptive( spreg, 1 );
+ astSetSystem( spreg, sys );
+ astSetUnit( spreg, 0, unit );
+ }
+
+/* Get the Region's fill factor. */
+ fill = astGetFillFactor( spreg );
+
+/* Get the bounds of the Region (i.e. the redshift axis coverage). */
+ astGetRegionBounds( spreg, lbnd, ubnd );
+
+/* Get a pointer to the spectral Region's encapsulated Frame. */
+ spfrm = astRegFrame( spreg );
+
+/* Report a warning if the sub-phrase Frame is not a SpecFrame */
+ if( !astIsASpecFrame( spfrm ) ) {
+ ok = 0;
+ astAddWarning( this, 1, "The redshift sub-phrase in the supplied "
+ "KeyMap is not described using an AST SpecFrame.",
+ "astWrite", status );
+
+/* Store properties that are specific to redshift positions... */
+ } else if( lbnd[ 0 ] == ubnd[ 0 ] ) {
+ astMapPut0C( spprops, "ID", "Redshift", NULL );
+ astMapPut0D( spprops, "REDSHIFT", lbnd[ 0 ], NULL );
+ fill = AST__BAD;
+
+/* Store properties that are specific to Redshift intervals... */
+ } else if( lbnd[ 0 ] > -lim && ubnd[ 0 ] < lim ) {
+ astMapPut0C( spprops, "ID", "RedshiftInterval", NULL );
+ astMapPut0D( spprops, "LOLIMIT", lbnd[ 0 ], NULL );
+ astMapPut0D( spprops, "HILIMIT", ubnd[ 0 ], NULL );
+
+ } else {
+ ok = 0;
+ astAddWarning( this, 1, "Cannot write out an unbounded "
+ "redshift interval.", "astWrite", status );
+ }
+
+/* Store properties that are common to all redshift sub-phrase types. First the
+ fill factor. */
+ MapPut0D( spprops, "FILLFACTOR", fill, 1.0, defs, status );
+
+/* Now the reference position. */
+ sor = astGetStdOfRest( spfrm );
+
+ if( sor == AST__GESOR ) {
+ tsor = "GEOCENTER";
+
+ } else if( sor == AST__BYSOR ) {
+ tsor = "BARYCENTER";
+
+ } else if( sor == AST__HLSOR ) {
+ tsor = "HELIOCENTER";
+
+ } else if( sor == AST__TPSOR ) {
+ tsor = "TOPOCENTER";
+
+ } else if( sor == AST__LKSOR ) {
+ tsor = "LSRK";
+
+ } else if( sor == AST__LDSOR ) {
+ tsor = "LSRD";
+
+ } else if( sor == AST__GLSOR ) {
+ tsor = "GALACTIC_CENTER";
+
+ } else {
+ tsor = NULL;
+ }
+
+ if( !tsor ) tsor = "UNKNOWNRefPos";
+ MapPut0C( spprops, "REFPOS", tsor, "UNKNOWNRefPos", defs,
+ status );
+
+/* Type and DopplerDef. */
+ if( sys == AST__VOPTICAL ) {
+ type = "VELOCITY";
+ dopdef = "OPTICAL";
+
+ } else if( sys == AST__VRADIO ) {
+ type = "VELOCITY";
+ dopdef = "RADIO";
+
+ } else if( sys == AST__VREL ) {
+ type = "VELOCITY";
+ dopdef = "RELATIVISTIC";
+
+ } else {
+ type = "REDSHIFT";
+ dopdef = "OPTICAL";
+ }
+ astMapPut0C( spprops, "DOPPLERDEF", dopdef, NULL );
+ MapPut0C( spprops, "TYPE", type, "REDSHIFT", defs, status );
+
+/* Now the unit string. */
+ MapPut0C( spprops, "UNIT", unit, unit, defs, status );
+
+/* That's it for the redshift sub-phrase, unless the supplied Region has an
+ explicit (non-default) uncertainty. */
+ unc = astGetUnc( spreg, 0 );
+ if( unc ) {
+
+/* Get the bounds of the uncertainty. */
+ astGetRegionBounds( unc, lbnd, ubnd );
+
+/* The error is half the width of the bounding box. */
+ astMapPut0D( spprops, "ERROR", 0.5*( ubnd[ 0 ] - lbnd[ 0 ] ), NULL );
+
+/* Free resources. */
+ unc = astAnnul( unc );
+ }
+
+/* Free resources. */
+ spfrm = astAnnul( spfrm );
+ spprops = astAnnul( spprops );
+ }
+
+/* Free resources. */
+ spreg = astAnnul( spreg );
+
+ }
+
+/* Free resources */
+ if( sreg ) sreg = astAnnul( sreg );
+ if( prop ) prop = astFree( prop );
+
+/* Return the result. */
+ return ok;
+}
+
+/* Functions which access class attributes. */
+/* ---------------------------------------- */
+/* Implement member functions to access the attributes associated with
+ this class using the macros defined for this purpose in the
+ "object.h" file. For a description of each attribute, see the class
+ interface (in the associated .h file). */
+
+/*
+*att++
+* Name:
+* StcsArea
+
+* Purpose:
+* Return the CoordinateArea component when reading an STC-S document?
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Integer (boolean).
+
+* Description:
+* This is a boolean attribute which controls what is returned
+* by the
+c astRead
+f AST_READ
+* function when it is used to read from an StcsChan.
+* If StcsArea is set non-zero (the default), then a Region
+* representing the STC CoordinateArea will be returned by
+c astRead.
+f AST_READ.
+* If StcsArea is set to zero, then the STC CoordinateArea
+* will not be returned.
+
+* Notes:
+* - Other attributes such as StcsCoords and StcsProps can be used to
+* specify other Objects to be returned by
+c astRead.
+f AST_READ.
+* If more than one of these attributes is set non-zero, then the
+* actual Object returned by
+c astRead
+f AST_READ
+* will be a KeyMap, containing the requested Objects. In this
+* case, the Region representing the STC CoordinateArea will be
+* stored in the returned KeyMap using the key "AREA". If StcsArea
+* is the only attribute to be set non-zero, then the Object returned by
+c astRead
+f AST_READ
+* will be the CoordinateArea Region itself.
+* - The class of Region used to represent the CoordinateArea for each
+* STC-S sub-phrase is determined by the first word in the
+* sub-phrase (the "sub-phrase identifier"). The individual sub-phrase
+* Regions are combined into a single Prism, which is then simplified
+c using astSimplify
+f using AST_SIMPLIFY
+* to form the returned region.
+* - Sub-phrases that represent a single value ( that is, have
+* identifiers "Time", "Position", "Spectral" or "Redshift" ) are
+* considered to be be part of the STC CoordinateArea component.
+* - The TimeFrame used to represent a time STC-S sub-phrase will have
+* its TimeOrigin attribute set to the sub-phrase start time. If no
+* start time is specified by the sub-phrase, then the stop time will be
+* used instead. If no stop time is specified by the sub-phrase, then
+* the single time value specified in the sub-phrase will be used
+* instead. Subsequently clearing the TimeOrigin attribute (or setting
+* its value to zero) will cause the TimeFrame to reprsent absolute times.
+* - The Epoch attribute for the returned Region is set in the same
+* way as the TimeOrigin attribute (see above).
+
+* Applicability:
+* StcsChan
+* All StcsChans have this attribute.
+*att--
+*/
+
+/* This ia a boolean value (0 or 1) with a value of -INT_MAX when
+ undefined but yielding a default of 1. */
+astMAKE_CLEAR(StcsChan,StcsArea,stcsarea,-INT_MAX)
+astMAKE_GET(StcsChan,StcsArea,int,1,( this->stcsarea != -INT_MAX ? this->stcsarea : 1 ))
+astMAKE_SET(StcsChan,StcsArea,int,stcsarea,( value != 0 ))
+astMAKE_TEST(StcsChan,StcsArea,( this->stcsarea != -INT_MAX ))
+
+/*
+*att++
+* Name:
+* StcsCoords
+
+* Purpose:
+* Return the Coordinates component when reading an STC-S document?
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Integer (boolean).
+
+* Description:
+* This is a boolean attribute which controls what is returned
+* by the
+c astRead
+f AST_READ
+* function when it is used to read from an StcsChan.
+* If StcsCoords is set non-zero, then a PointList
+* representing the STC Coordinates will be returned by
+c astRead.
+f AST_READ.
+* If StcsCoords is set to zero (the default), then the STC
+* Coordinates will not be returned.
+
+* Notes:
+* - Other attributes such as StcsArea and StcsProps can be used to
+* specify other Objects to be returned by
+c astRead.
+f AST_READ.
+* If more than one of these attributes is set non-zero, then the
+* actual Object returned by
+c astRead
+f AST_READ
+* will be a KeyMap, containing the requested Objects. In this
+* case, the PointList representing the STC Coordinates will be
+* stored in the returned KeyMap using the key "COORDS". If StcsCoords
+* is the only attribute to be set non-zero, then the Object returned by
+c astRead
+f AST_READ
+* will be the Coordinates PointList itself.
+* - The Coordinates component is specified by the additional axis
+* values embedded within the body of each STC-S sub-phrase that
+* represents an extended area. Sub-phrases that represent a single
+* value ( that is, have identifiers "Time", "Position", "Spectral"
+* or "Redshift" ) are not considered to be be part of the STC
+* Coordinates component.
+* - If the STC-S documents does not contain a Coordinates component,
+* then a NULL object pointer
+f (AST__NULL)
+* will be returned by
+c astRead
+f AST_READ
+* if the Coordinates component is the only object being returned. If
+* other objects are also being returned (see attributes StcsProps and
+* StcsArea), then the returned KeyMap will contain a "COORDS" key
+* only if the Coordinates component is read succesfully.
+* - The TimeFrame used to represent a time STC-S sub-phrase will have
+* its TimeOrigin attribute set to the sub-phrase start time. If no
+* start time is specified by the sub-phrase, then the stop time will be
+* used instead. If no stop time is specified by the sub-phrase, then
+* the single time value specified in the sub-phrase will be used
+* instead. Subsequently clearing the TimeOrigin attribute (or setting
+* its value to zero) will cause the TimeFrame to reprsent absolute times.
+* - The Epoch attribute for the returned Region is set in the same
+* way as the TimeOrigin attribute (see above).
+
+* Applicability:
+* StcsChan
+* All StcsChans have this attribute.
+*att--
+*/
+
+/* This ia a boolean value (0 or 1) with a value of -INT_MAX when
+ undefined but yielding a default of zero. */
+astMAKE_CLEAR(StcsChan,StcsCoords,stcscoords,-INT_MAX)
+astMAKE_GET(StcsChan,StcsCoords,int,0,( this->stcscoords != -INT_MAX ? this->stcscoords : 0 ))
+astMAKE_SET(StcsChan,StcsCoords,int,stcscoords,( value != 0 ))
+astMAKE_TEST(StcsChan,StcsCoords,( this->stcscoords != -INT_MAX ))
+
+/*
+*att++
+* Name:
+* StcsProps
+
+* Purpose:
+* Return all properties when reading an STC-S document?
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Integer (boolean).
+
+* Description:
+* This is a boolean attribute which controls what is returned
+* by the
+c astRead
+f AST_READ
+* function when it is used to read from an StcsChan.
+* If StcsProps is set non-zero, then a KeyMap containing all the
+* properties read from the STC-S document will be returned by
+c astRead.
+f AST_READ.
+* If StcsProps is set to zero (the default), then the properties
+* will not be returned.
+
+* Notes:
+* - Other attributes such as StcsCoords and StcsArea can be used to
+* specify other Objects to be returned by
+c astRead.
+f AST_READ.
+* If more than one of these attributes is set non-zero, then the
+* actual Object returned by
+c astRead
+f AST_READ
+* will be a KeyMap containing the requested Objects. In this
+* case, the properties KeyMap will be stored in the returned KeyMap
+* using the key "PROPS". If StcsProps is the only attribute to be
+* set non-zero, then the Object returned by
+c astRead
+f AST_READ
+* will be the properties KeyMap itself.
+* - The KeyMap containing the properties will have entries for one or
+* more of the following keys: "TIME_PROPS", "SPACE_PROPS", "SPECTRAL_PROPS"
+* and "REDSHIFT_PROPS". Each of these entries will be another KeyMap
+* containing the properties of the corresponding STC-S sub-phrase.
+
+* Applicability:
+* StcsChan
+* All StcsChans have this attribute.
+*att--
+*/
+
+/* This ia a boolean value (0 or 1) with a value of -INT_MAX when
+ undefined but yielding a default of zero. */
+astMAKE_CLEAR(StcsChan,StcsProps,stcsprops,-INT_MAX)
+astMAKE_GET(StcsChan,StcsProps,int,0,( this->stcsprops != -INT_MAX ? this->stcsprops : 0 ))
+astMAKE_SET(StcsChan,StcsProps,int,stcsprops,( value != 0 ))
+astMAKE_TEST(StcsChan,StcsProps,( this->stcsprops != -INT_MAX ))
+
+/*
+*att++
+* Name:
+* StcsLength
+
+* Purpose:
+* Controls output line length.
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Integer.
+
+* Description:
+* This attribute specifies the maximum length to use when writing out
+* text through the sink function supplied when the StcsChan was created.
+* It is ignored if the Indent attribute is zero (in which case the text
+* supplied to the sink function can be of any length). The default value
+* is 70.
+*
+* The number of characters in each string written out through the sink
+* function will not usually be greater than the value of this attribute
+* (but may be less). However, if any single word in the STC-S
+* description exceeds the specified length, then the word will be
+* written out as a single line.
+*
+f Note, the default value of zero is unlikely to be appropriate when
+f an StcsChan is used within Fortran code. In this case, StcsLength
+f should usually be set to the size of the CHARACTER variable used to
+f receive the text returned by AST_GETLINE within the sink function.
+f In addition, the Indent attribute should be set non-zero. This
+f avoids the possibility of long lines being truncated invisibly
+f within AST_GETLINE.
+
+* Applicability:
+* StcsChan
+* All StcsChans have this attribute.
+*att--
+*/
+astMAKE_CLEAR(StcsChan,StcsLength,stcslength,-INT_MAX)
+astMAKE_GET(StcsChan,StcsLength,int,70,( ( this->stcslength != -INT_MAX ) ? this->stcslength : 70 ))
+astMAKE_SET(StcsChan,StcsLength,int,stcslength,(value<0?0:value))
+astMAKE_TEST(StcsChan,StcsLength,( this->stcslength != -INT_MAX ))
+
+/* Copy constructor. */
+/* ----------------- */
+
+/* Destructor. */
+/* ----------- */
+
+/* Dump function. */
+/* -------------- */
+
+static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
+/*
+* Name:
+* Dump
+
+* Purpose:
+* Dump function for StcsChan objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* void Dump( AstObject *this, AstChannel *channel, int *status )
+
+* Description:
+* This function implements the Dump function which writes out data
+* for the StcsChan class to an output Channel.
+
+* Parameters:
+* this
+* Pointer to the Object (an StcsChan) whose data are being written.
+* channel
+* Pointer to the Channel to which the data are being written.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ AstStcsChan *this; /* Pointer to the StcsChan structure */
+ int ival; /* Integer value */
+ int set; /* Attribute value set? */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the StcsChan structure. */
+ this = (AstStcsChan *) this_object;
+
+/* Write out values representing the instance variables for the
+ StcsChan class. Accompany these with appropriate comment strings,
+ possibly depending on the values being written.*/
+
+/* In the case of attributes, we first use the appropriate (private)
+ Test... member function to see if they are set. If so, we then use
+ the (private) Get... function to obtain the value to be written
+ out.
+
+ For attributes which are not set, we use the astGet... method to
+ obtain the value instead. This will supply a default value
+ (possibly provided by a derived class which over-rides this method)
+ which is more useful to a human reader as it corresponds to the
+ actual default attribute value. Since "set" will be zero, these
+ values are for information only and will not be read back. */
+
+/* StcsArea. */
+/* --------- */
+ set = TestStcsArea( this, status );
+ ival = set ? GetStcsArea( this, status ) : astGetStcsArea( this );
+ astWriteInt( channel, "StcsArea", set, 0, ival,
+ ival ? "Read the STC CoordinatesArea component" :
+ "Do not read the STC CoordinatesArea component" );
+
+/* StcsCoords. */
+/* ----------- */
+ set = TestStcsCoords( this, status );
+ ival = set ? GetStcsCoords( this, status ) : astGetStcsCoords( this );
+ astWriteInt( channel, "StcsCoords", set, 0, ival,
+ ival ? "Read the STC Coordinates component" :
+ "Do not read the STC Coordinates component" );
+
+/* StcsProps. */
+/* ---------- */
+ set = TestStcsProps( this, status );
+ ival = set ? GetStcsProps( this, status ) : astGetStcsProps( this );
+ astWriteInt( channel, "StcsProps", set, 0, ival,
+ ival ? "Read the STC-S properties" :
+ "Do not read the STC-S properties" );
+
+/* StcsLength */
+/* ---------- */
+ set = TestStcsLength( this, status );
+ ival = set ? GetStcsLength( this, status ) : astGetStcsLength( this );
+ astWriteInt( channel, "StcsLen", set, 0, ival, "STC-S buffer length" );
+
+}
+
+/* Standard class functions. */
+/* ========================= */
+/* Implement the astIsAStcsChan and astCheckStcsChan functions using the macros
+ defined for this purpose in the "object.h" header file. */
+astMAKE_ISA(StcsChan,Channel)
+astMAKE_CHECK(StcsChan)
+
+AstStcsChan *astStcsChan_( const char *(* source)( void ),
+ void (* sink)( const char * ),
+ const char *options, int *status, ...) {
+/*
+*++
+* Name:
+c astStcsChan
+f AST_STCSCHAN
+
+* Purpose:
+* Create an StcsChan.
+
+* Type:
+* Public function.
+
+* Synopsis:
+c #include "stcschan.h"
+c AstStcsChan *astStcsChan( const char *(* source)( void ),
+c void (* sink)( const char * ),
+c const char *options, ... )
+f RESULT = AST_STCSCHAN( SOURCE, SINK, OPTIONS, STATUS )
+
+* Class Membership:
+* StcsChan constructor.
+
+* Description:
+* This function creates a new StcsChan and optionally initialises
+* its attributes.
+*
+* A StcsChan is a specialised form of Channel which supports STC-S
+* I/O operations. Writing an Object to an StcsChan (using
+c astWrite) will, if the Object is suitable, generate an
+f AST_WRITE) will, if the Object is suitable, generate an
+* STC-S description of that Object, and reading from an StcsChan will
+* create a new Object from its STC-S description.
+*
+* Normally, when you use an StcsChan, you should provide "source"
+c and "sink" functions which connect it to an external data store
+c by reading and writing the resulting text. These functions
+f and "sink" routines which connect it to an external data store
+f by reading and writing the resulting text. These routines
+* should perform any conversions needed between external character
+c encodings and the internal ASCII encoding. If no such functions
+f encodings and the internal ASCII encoding. If no such routines
+* are supplied, a Channel will read from standard input and write
+* to standard output.
+*
+* Alternatively, an XmlChan can be told to read or write from
+* specific text files using the SinkFile and SourceFile attributes,
+* in which case no sink or source function need be supplied.
+
+* Parameters:
+c source
+f SOURCE = SUBROUTINE (Given)
+c Pointer to a source function that takes no arguments and
+c returns a pointer to a null-terminated string. If no value
+c has been set for the SourceFile attribute, this function
+c will be used by the StcsChan to obtain lines of input text. On
+c each invocation, it should return a pointer to the next input
+c line read from some external data store, and a NULL pointer
+c when there are no more lines to read.
+c
+c If "source" is NULL and no value has been set for the SourceFile
+c attribute, the StcsChan will read from standard input instead.
+f A source routine, which is a subroutine which takes a single
+f integer error status argument. If no value has been set
+f for the SourceFile attribute, this routine will be used by
+f the StcsChan to obtain lines of input text. On each
+f invocation, it should read the next input line from some
+f external data store, and then return the resulting text to
+f the AST library by calling AST_PUTLINE. It should supply a
+f negative line length when there are no more lines to read.
+f If an error occurs, it should set its own error status
+f argument to an error value before returning.
+f
+f If the null routine AST_NULL is suppied as the SOURCE value,
+f and no value has been set for the SourceFile attribute,
+f the StcsChan will read from standard input instead.
+c sink
+f SINK = SUBROUTINE (Given)
+c Pointer to a sink function that takes a pointer to a
+c null-terminated string as an argument and returns void.
+c If no value has been set for the SinkFile attribute, this
+c function will be used by the StcsChan to deliver lines of
+c output text. On each invocation, it should deliver the
+c contents of the string supplied to some external data store.
+c
+c If "sink" is NULL, and no value has been set for the SinkFile
+c attribute, the StcsChan will write to standard output instead.
+f A sink routine, which is a subroutine which takes a single
+f integer error status argument. If no value has been set
+f for the SinkFile attribute, this routine will be used by
+f the StcsChan to deliver lines of output text. On each
+f invocation, it should obtain the next output line from the
+f AST library by calling AST_GETLINE, and then deliver the
+f resulting text to some external data store. If an error
+f occurs, it should set its own error status argument to an
+f error value before returning.
+f
+f If the null routine AST_NULL is suppied as the SINK value,
+f and no value has been set for the SinkFile attribute,
+f the StcsChan will write to standard output instead.
+c options
+f OPTIONS = CHARACTER * ( * ) (Given)
+c Pointer to a null-terminated string containing an optional
+c comma-separated list of attribute assignments to be used for
+c initialising the new StcsChan. The syntax used is identical to
+c that for the astSet function and may include "printf" format
+c specifiers identified by "%" symbols in the normal way.
+f A character string containing an optional comma-separated
+f list of attribute assignments to be used for initialising the
+f new StcsChan. The syntax used is identical to that for the
+f AST_SET routine.
+c ...
+c If the "options" string contains "%" format specifiers, then
+c an optional list of additional arguments may follow it in
+c order to supply values to be substituted for these
+c specifiers. The rules for supplying these are identical to
+c those for the astSet function (and for the C "printf"
+c function).
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Returned Value:
+c astStcsChan()
+f AST_STCSCHAN = INTEGER
+* A pointer to the new StcsChan.
+
+* Notes:
+f - The names of the routines supplied for the SOURCE and SINK
+f arguments should appear in EXTERNAL statements in the Fortran
+f routine which invokes AST_STCSCHAN. However, this is not generally
+f necessary for the null routine AST_NULL (so long as the AST_PAR
+f include file has been used).
+* - If the external data source or sink uses a character encoding
+* other than ASCII, the supplied source and sink functions should
+* translate between the external character encoding and the internal
+* ASCII encoding used by AST.
+* - A null Object pointer (AST__NULL) will be returned if this
+* function is invoked with the AST error status set, or if it
+* should fail for any reason.
+f - Note that the null routine AST_NULL (one underscore) is
+f different to AST__NULL (two underscores), which is the null Object
+f pointer.
+*--
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstStcsChan *new; /* Pointer to new StcsChan */
+ va_list args; /* Variable argument list */
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* Initialise the StcsChan, allocating memory and initialising the
+ virtual function table as well if necessary. This interface is for
+ use by other C functions within AST, and uses the standard "wrapper"
+ functions included in this class. */
+ new = astInitStcsChan( NULL, sizeof( AstStcsChan ), !class_init,
+ &class_vtab, "StcsChan", source, SourceWrap,
+ sink, SinkWrap );
+
+/* If successful, note that the virtual function table has been
+ initialised. */
+ if ( astOK ) {
+ class_init = 1;
+
+/* Obtain the variable argument list and pass it along with the
+ options string to the astVSet method to initialise the new
+ StcsChan's attributes. */
+ va_start( args, status );
+ astVSet( new, options, NULL, args );
+ va_end( args );
+
+/* If an error occurred, clean up by deleting the new object. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return a pointer to the new StcsChan. */
+ return new;
+}
+
+AstStcsChan *astStcsChanId_( const char *(* source)( void ),
+ void (* sink)( const char * ),
+ const char *options, ... ) {
+/*
+* Name:
+* astStcsChanId_
+
+* Purpose:
+* Create an StcsChan.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "stcschan.h"
+* AstStcsChan *astStcsChanId_( const char *(* source)( void ),
+* void (* sink)( const char * ),
+* const char *options, ... )
+
+* Class Membership:
+* StcsChan constructor.
+
+* Description:
+* This function implements the external (public) C interface to the
+* astStcsChan constructor function. Another function (astStcsChanForId)
+* should be called to create an StcsChan for use within other languages.
+* Both functions return an ID value (instead of a true C pointer) to
+* external users, and must be provided because astStcsChan_ has a variable
+* argument list which cannot be encapsulated in a macro (where this conversion would otherwise
+* occur).
+*
+* The variable argument list also prevents this function from
+* invoking astStcsChan_ directly, so it must be a re-implementation
+* of it in all respects, except for the final conversion of the
+* result to an ID value.
+
+* Parameters:
+* As for astStcsChan_.
+
+* Returned Value:
+* The ID value associated with the new StcsChan.
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstStcsChan *new; /* Pointer to new StcsChan */
+ va_list args; /* Variable argument list */
+
+ int *status; /* Pointer to inherited status value */
+
+/* Get a pointer to the inherited status value. */
+ status = astGetStatusPtr;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* Initialise the StcsChan, allocating memory and initialising the
+ virtual function table as well if necessary. This interface is for
+ use by external C functions and uses the standard "wrapper"
+ functions included in this class. */
+ new = astInitStcsChan( NULL, sizeof( AstStcsChan ), !class_init,
+ &class_vtab, "StcsChan", source, SourceWrap,
+ sink, SinkWrap );
+
+/* If successful, note that the virtual function table has been
+ initialised. */
+ if ( astOK ) {
+ class_init = 1;
+
+/* Obtain the variable argument list and pass it along with the
+ options string to the astVSet method to initialise the new
+ StcsChan's attributes. */
+ va_start( args, options );
+ astVSet( new, options, NULL, args );
+ va_end( args );
+
+/* If an error occurred, clean up by deleting the new object. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return an ID value for the new StcsChan. */
+ return astMakeId( new );
+}
+
+AstStcsChan *astStcsChanForId_( const char *(* source)( void ),
+ char *(* source_wrap)( const char *(*)( void ), int * ),
+ void (* sink)( const char * ),
+ void (* sink_wrap)( void (*)( const char * ),
+ const char *, int * ),
+ const char *options, ... ) {
+/*
+*+
+* Name:
+* astStcsChanFor
+
+* Purpose:
+* Initialise an StcsChan from a foreign language interface.
+
+* Type:
+* Public function.
+
+* Synopsis:
+* #include "stcschan.h"
+* AstStcsChan *astStcsChanFor( const char *(* source)( void ),
+* char *(* source_wrap)( const char *(*)
+* ( void ), int * ),
+* void (* sink)( const char * ),
+* void (* sink_wrap)( void (*)( const char * ),
+* const char *, int * ),
+* const char *options, ... )
+
+* Class Membership:
+* StcsChan constructor.
+
+* Description:
+* This function creates a new StcsChan from a foreign language
+* interface and optionally initialises its attributes.
+*
+* A StcsChan is a specialised form of Channel which supports STC-S
+* I/O operations. Writing an Object to an StcsChan (using
+c astWrite) will, if the Object is suitable, generate an
+f AST_WRITE) will, if the Object is suitable, generate an
+* STC-S description of that Object, and reading from an StcsChan will
+* create a new Object from its STC-S description.
+*
+* Normally, when you use an StcsChan, you should provide "source"
+c and "sink" functions which connect it to an external data store
+c by reading and writing the resulting text. These functions
+f and "sink" routines which connect it to an external data store
+f by reading and writing the resulting text. These routines
+* should perform any conversions needed between external character
+c encodings and the internal ASCII encoding. If no such functions
+f encodings and the internal ASCII encoding. If no such routines
+* are supplied, a Channel will read from standard input and write
+* to standard output.
+
+* Parameters:
+* source
+* Pointer to a "source" function which will be used to obtain
+* lines of input text. Generally, this will be obtained by
+* casting a pointer to a source function which is compatible
+* with the "source_wrap" wrapper function (below). The pointer
+* should later be cast back to its original type by the
+* "source_wrap" function before the function is invoked.
+*
+* If "source" is NULL, the StcsChan will read from standard
+* input instead.
+* source_wrap
+* Pointer to a function which can be used to invoke the
+* "source" function supplied (above). This wrapper function is
+* necessary in order to hide variations in the nature of the
+* source function, such as may arise when it is supplied by a
+* foreign (non-C) language interface.
+*
+* The single parameter of the "source_wrap" function is a
+* pointer to the "source" function, and it should cast this
+* function pointer (as necessary) and invoke the function with
+* appropriate arguments to obtain the next line of input
+* text. The "source_wrap" function should then return a pointer
+* to a dynamically allocated, null terminated string containing
+* the text that was read. The string will be freed (using
+* astFree) when no longer required and the "source_wrap"
+* function need not concern itself with this. A NULL pointer
+* should be returned if there is no more input to read.
+*
+* If "source_wrap" is NULL, the StcsChan will read from standard
+* input instead.
+* sink
+* Pointer to a "sink" function which will be used to deliver
+* lines of output text. Generally, this will be obtained by
+* casting a pointer to a sink function which is compatible with
+* the "sink_wrap" wrapper function (below). The pointer should
+* later be cast back to its original type by the "sink_wrap"
+* function before the function is invoked.
+*
+* If "sink" is NULL, the StcsChan will write to standard output
+* instead.
+* sink_wrap
+* Pointer to a function which can be used to invoke the "sink"
+* function supplied (above). This wrapper function is necessary
+* in order to hide variations in the nature of the sink
+* function, such as may arise when it is supplied by a foreign
+* (non-C) language interface.
+*
+* The first parameter of the "sink_wrap" function is a pointer
+* to the "sink" function, and the second parameter is a pointer
+* to a const, null-terminated character string containing the
+* text to be written. The "sink_wrap" function should cast the
+* "sink" function pointer (as necessary) and invoke the
+* function with appropriate arguments to deliver the line of
+* output text. The "sink_wrap" function then returns void.
+*
+* If "sink_wrap" is NULL, the Channel will write to standard
+* output instead.
+* options
+* Pointer to a null-terminated string containing an optional
+* comma-separated list of attribute assignments to be used for
+* initialising the new StcsChan. The syntax used is identical to
+* that for the astSet function and may include "printf" format
+* specifiers identified by "%" symbols in the normal way.
+* ...
+* If the "options" string contains "%" format specifiers, then
+* an optional list of additional arguments may follow it in
+* order to supply values to be substituted for these
+* specifiers. The rules for supplying these are identical to
+* those for the astSet function (and for the C "printf"
+* function).
+
+* Returned Value:
+* astStcsChanFor()
+* A pointer to the new StcsChan.
+
+* Notes:
+* - A null Object pointer (AST__NULL) will be returned if this
+* function is invoked with the global error status set, or if it
+* should fail for any reason.
+* - This function is only available through the public interface
+* to the StcsChan class (not the protected interface) and is
+* intended solely for use in implementing foreign language
+* interfaces to this class.
+*-
+
+* Implememtation Notes:
+* - This function behaves exactly like astStcsChanId_, in that it
+* returns ID values and not true C pointers, but it has two
+* additional arguments. These are pointers to the "wrapper
+* functions" which are needed to accommodate foreign language
+* interfaces.
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstStcsChan *new; /* Pointer to new StcsChan */
+ va_list args; /* Variable argument list */
+ int *status; /* Pointer to inherited status value */
+
+/* Get a pointer to the inherited status value. */
+ status = astGetStatusPtr;
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Initialise the StcsChan, allocating memory and initialising the
+ virtual function table as well if necessary. */
+ new = astInitStcsChan( NULL, sizeof( AstStcsChan ), !class_init,
+ &class_vtab, "StcsChan", source, source_wrap,
+ sink, sink_wrap );
+
+/* If successful, note that the virtual function table has been
+ initialised. */
+ if ( astOK ) {
+ class_init = 1;
+
+/* Obtain the variable argument list and pass it along with the
+ options string to the astVSet method to initialise the new
+ StcsChan's attributes. */
+ va_start( args, options );
+ astVSet( new, options, NULL, args );
+ va_end( args );
+
+/* If an error occurred, clean up by deleting the new object. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return an ID value for the new StcsChan. */
+ return astMakeId( new );
+}
+
+AstStcsChan *astInitStcsChan_( void *mem, size_t size, int init,
+ AstStcsChanVtab *vtab, const char *name,
+ const char *(* source)( void ),
+ char *(* source_wrap)( const char *(*)( void ), int * ),
+ void (* sink)( const char * ),
+ void (* sink_wrap)( void (*)( const char * ),
+ const char *, int * ), int *status ) {
+/*
+*+
+* Name:
+* astInitStcsChan
+
+* Purpose:
+* Initialise an StcsChan.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "stcschan.h"
+* AstStcsChan *astInitStcsChan( void *mem, size_t size, int init,
+* AstStcsChanVtab *vtab, const char *name,
+* const char *(* source)( void ),
+* char *(* source_wrap)( const char *(*)( void ), int * ),
+* void (* sink)( const char * ),
+* void (* sink_wrap)( void (*)( const char * ),
+* const char *, int * ) )
+
+* Class Membership:
+* StcsChan initialiser.
+
+* Description:
+* This function is provided for use by class implementations to
+* initialise a new StcsChan object. It allocates memory (if
+* necessary) to accommodate the StcsChan plus any additional data
+* associated with the derived class. It then initialises a
+* StcsChan structure at the start of this memory. If the "init"
+* flag is set, it also initialises the contents of a virtual
+* function table for an StcsChan at the start of the memory passed
+* via the "vtab" parameter.
+
+* Parameters:
+* mem
+* A pointer to the memory in which the StcsChan is to be
+* initialised. This must be of sufficient size to accommodate
+* the StcsChan data (sizeof(StcsChan)) plus any data used by the
+* derived class. If a value of NULL is given, this function
+* will allocate the memory itself using the "size" parameter to
+* determine its size.
+* size
+* The amount of memory used by the StcsChan (plus derived class
+* data). This will be used to allocate memory if a value of
+* NULL is given for the "mem" parameter. This value is also
+* stored in the StcsChan structure, so a valid value must be
+* supplied even if not required for allocating memory.
+* init
+* A boolean flag indicating if the StcsChan's virtual function
+* table is to be initialised. If this value is non-zero, the
+* virtual function table will be initialised by this function.
+* vtab
+* Pointer to the start of the virtual function table to be
+* associated with the new StcsChan.
+* name
+* Pointer to a constant null-terminated character string which
+* contains the name of the class to which the new object
+* belongs (it is this pointer value that will subsequently be
+* returned by the astGetClass method).
+* source
+* Pointer to a "source" function which will be used to obtain
+* lines of text. Generally, this will be obtained by
+* casting a pointer to a source function which is compatible
+* with the "source_wrap" wrapper function (below). The pointer
+* should later be cast back to its original type by the
+* "source_wrap" function before the function is invoked.
+*
+* If "source" is NULL, the Channel will read from standard
+* input instead.
+* source_wrap
+* Pointer to a function which can be used to invoke the
+* "source" function supplied (above). This wrapper function is
+* necessary in order to hide variations in the nature of the
+* source function, such as may arise when it is supplied by a
+* foreign (non-C) language interface.
+*
+* The single parameter of the "source_wrap" function is a
+* pointer to the "source" function, and it should cast this
+* function pointer (as necessary) and invoke the function with
+* appropriate arguments to obtain the next line of input
+* text. The "source_wrap" function should then return a pointer
+* to a dynamically allocated, null terminated string containing
+* the text that was read. The string will be freed (using
+* astFree) when no longer required and the "source_wrap"
+* function need not concern itself with this. A NULL pointer
+* should be returned if there is no more input to read.
+*
+* If "source_wrap" is NULL, the Channel will read from standard
+* input instead.
+* sink
+* Pointer to a "sink" function which will be used to deliver
+* lines of text. Generally, this will be obtained by
+* casting a pointer to a sink function which is compatible with
+* the "sink_wrap" wrapper function (below). The pointer should
+* later be cast back to its original type by the "sink_wrap"
+* function before the function is invoked.
+*
+* If "sink" is NULL, the contents of the StcsChan will not be
+* written out before being deleted.
+* sink_wrap
+* Pointer to a function which can be used to invoke the "sink"
+* function supplied (above). This wrapper function is necessary
+* in order to hide variations in the nature of the sink
+* function, such as may arise when it is supplied by a foreign
+* (non-C) language interface.
+*
+* The first parameter of the "sink_wrap" function is a pointer
+* to the "sink" function, and the second parameter is a pointer
+* to a const, null-terminated character string containing the
+* text to be written. The "sink_wrap" function should cast the
+* "sink" function pointer (as necessary) and invoke the
+* function with appropriate arguments to deliver the line of
+* output text. The "sink_wrap" function then returns void.
+*
+* If "sink_wrap" is NULL, the Channel will write to standard
+* output instead.
+
+* Returned Value:
+* A pointer to the new StcsChan.
+
+* Notes:
+* - A null pointer will be returned if this function is invoked
+* with the global error status set, or if it should fail for any
+* reason.
+*-
+*/
+
+/* Local Variables: */
+ AstStcsChan *new; /* Pointer to new StcsChan */
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* If necessary, initialise the virtual function table. */
+ if ( init ) astInitStcsChanVtab( vtab, name );
+
+/* Initialise a Channel structure (the parent class) as the first
+ component within the StcsChan structure, allocating memory if
+ necessary. */
+ new = (AstStcsChan *) astInitChannel( mem, size, 0,
+ (AstChannelVtab *) vtab, name,
+ source, source_wrap, sink,
+ sink_wrap );
+
+ if ( astOK ) {
+
+/* Initialise the StcsChan data. */
+/* ---------------------------- */
+ new->stcsarea = -INT_MAX;
+ new->stcscoords = -INT_MAX;
+ new->stcsprops = -INT_MAX;
+ new->stcslength = -INT_MAX;
+
+/* If an error occurred, clean up by deleting the new object. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return a pointer to the new object. */
+ return new;
+}
+
+AstStcsChan *astLoadStcsChan_( void *mem, size_t size,
+ AstStcsChanVtab *vtab, const char *name,
+ AstChannel *channel, int *status ) {
+/*
+*+
+* Name:
+* astLoadStcsChan
+
+* Purpose:
+* Load an StcsChan.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "stcschan.h"
+* AstStcsChan *astLoadStcsChan( void *mem, size_t size,
+* AstStcsChanVtab *vtab, const char *name,
+* AstChannel *channel )
+
+* Class Membership:
+* StcsChan loader.
+
+* Description:
+* This function is provided to load a new StcsChan using data read
+* from a Channel. It first loads the data used by the parent class
+* (which allocates memory if necessary) and then initialises a
+* StcsChan structure in this memory, using data read from the input
+* Channel.
+*
+* If the "init" flag is set, it also initialises the contents of a
+* virtual function table for an StcsChan at the start of the memory
+* passed via the "vtab" parameter.
+
+* Parameters:
+* mem
+* A pointer to the memory into which the StcsChan is to be
+* loaded. This must be of sufficient size to accommodate the
+* StcsChan data (sizeof(StcsChan)) plus any data used by derived
+* classes. If a value of NULL is given, this function will
+* allocate the memory itself using the "size" parameter to
+* determine its size.
+* size
+* The amount of memory used by the StcsChan (plus derived class
+* data). This will be used to allocate memory if a value of
+* NULL is given for the "mem" parameter. This value is also
+* stored in the StcsChan structure, so a valid value must be
+* supplied even if not required for allocating memory.
+*
+* If the "vtab" parameter is NULL, the "size" value is ignored
+* and sizeof(AstStcsChan) is used instead.
+* vtab
+* Pointer to the start of the virtual function table to be
+* associated with the new StcsChan. If this is NULL, a pointer
+* to the (static) virtual function table for the StcsChan class
+* is used instead.
+* name
+* Pointer to a constant null-terminated character string which
+* contains the name of the class to which the new object
+* belongs (it is this pointer value that will subsequently be
+* returned by the astGetClass method).
+*
+* If the "vtab" parameter is NULL, the "name" value is ignored
+* and a pointer to the string "StcsChan" is used instead.
+
+* Returned Value:
+* A pointer to the new StcsChan.
+
+* Notes:
+* - A null pointer will be returned if this function is invoked
+* with the global error status set, or if it should fail for any
+* reason.
+*-
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstStcsChan *new; /* Pointer to the new StcsChan */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if( !astOK ) return new;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(channel);
+
+/* If a NULL virtual function table has been supplied, then this is
+ the first loader to be invoked for this StcsChan. In this case the
+ StcsChan belongs to this class, so supply appropriate values to be
+ passed to the parent class loader (and its parent, etc.). */
+ if ( !vtab ) {
+ size = sizeof( AstStcsChan );
+ vtab = &class_vtab;
+ name = "StcsChan";
+
+/* If required, initialise the virtual function table for this class. */
+ if ( !class_init ) {
+ astInitStcsChanVtab( vtab, name );
+ class_init = 1;
+ }
+ }
+
+/* Invoke the parent class loader to load data for all the ancestral
+ classes of the current one, returning a pointer to the resulting
+ partly-built StcsChan. */
+ new = astLoadChannel( mem, size, (AstChannelVtab *) vtab, name,
+ channel );
+
+ if ( astOK ) {
+
+/* Read input data. */
+/* ================ */
+/* Request the input Channel to read all the input data appropriate to
+ this class into the internal "values list". */
+ astReadClassData( channel, "StcsChan" );
+
+/* Now read each individual data item from this list and use it to
+ initialise the appropriate instance variable(s) for this class. */
+
+/* In the case of attributes, we first read the "raw" input value,
+ supplying the "unset" value as the default. If a "set" value is
+ obtained, we then use the appropriate (private) Set... member
+ function to validate and set the value properly. */
+
+/* StcsArea. */
+/* --------- */
+ new->stcsarea = astReadInt( channel, "stcsarea", -INT_MAX );
+ if ( TestStcsArea( new, status ) ) SetStcsArea( new, new->stcsarea, status );
+
+/* StcsCoords. */
+/* ----------- */
+ new->stcscoords = astReadInt( channel, "stcscoords", -INT_MAX );
+ if ( TestStcsCoords( new, status ) ) SetStcsCoords( new, new->stcscoords, status );
+
+/* StcsProps. */
+/* ---------- */
+ new->stcsprops = astReadInt( channel, "stcsprops", -INT_MAX );
+ if ( TestStcsProps( new, status ) ) SetStcsProps( new, new->stcsprops, status );
+
+/* StcsLength */
+/* ---------- */
+ new->stcslength = astReadInt( channel, "stcslen", -INT_MAX );
+
+ }
+
+/* If an error occurred, clean up by deleting the new StcsChan. */
+ if ( !astOK ) new = astDelete( new );
+
+/* Return the new StcsChan pointer. */
+ return new;
+}
+
+/* Virtual function interfaces. */
+/* ============================ */
+/* These provide the external interface to the virtual functions defined by
+ this class. Each simply checks the global error status and then locates and
+ executes the appropriate member function, using the function pointer stored
+ in the object's virtual function table (this pointer is located using the
+ astMEMBER macro defined in "object.h").
+
+ Note that the member function may not be the one defined here, as it may
+ have been over-ridden by a derived class. However, it should still have the
+ same interface. */
+
+
+
+
+
+
+
+