diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2019-05-10 16:18:58 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2019-05-10 16:18:58 (GMT) |
commit | 5492ad5105428df25cca70ab260229f757427278 (patch) | |
tree | e2bc900ba8c297d483518d1e86405e2e0f86f0ea /ast/stcschan.c | |
parent | 9646e8d50bc1481de77459d59738826f9c256ad6 (diff) | |
download | blt-5492ad5105428df25cca70ab260229f757427278.zip blt-5492ad5105428df25cca70ab260229f757427278.tar.gz blt-5492ad5105428df25cca70ab260229f757427278.tar.bz2 |
upgrade ast 8.7.1
Diffstat (limited to 'ast/stcschan.c')
-rw-r--r-- | ast/stcschan.c | 8732 |
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, ®_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, ®_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, ®_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. */ + + + + + + + + |