summaryrefslogtreecommitdiffstats
path: root/ast/xmlchan.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2018-09-21 17:04:03 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2018-09-21 17:04:03 (GMT)
commit28518ab5eb4726fe91ee0c916b2322f8c47eb3ad (patch)
tree2653f0ce7e9c70973f27fe6c0c38ec31e21ce5cf /ast/xmlchan.c
parentbd7d67f66c53df36bb50e3423bfc91eae8618201 (diff)
downloadblt-28518ab5eb4726fe91ee0c916b2322f8c47eb3ad.zip
blt-28518ab5eb4726fe91ee0c916b2322f8c47eb3ad.tar.gz
blt-28518ab5eb4726fe91ee0c916b2322f8c47eb3ad.tar.bz2
update ast 8.6.3
Diffstat (limited to 'ast/xmlchan.c')
-rw-r--r--ast/xmlchan.c14120
1 files changed, 14120 insertions, 0 deletions
diff --git a/ast/xmlchan.c b/ast/xmlchan.c
new file mode 100644
index 0000000..bf7aad9
--- /dev/null
+++ b/ast/xmlchan.c
@@ -0,0 +1,14120 @@
+/*
+*class++
+* Name:
+* XmlChan
+
+* Purpose:
+* I/O Channel using XML to represent Objects.
+
+* Constructor Function:
+c astXmlChan
+f AST_XMLCHAN
+
+* Description:
+* A XmlChan is a specialised form of Channel which supports XML I/O
+* operations. Writing an Object to an XmlChan (using
+c astWrite) will, if the Object is suitable, generate an
+f AST_WRITE) will, if the Object is suitable, generate an
+* XML description of that Object, and reading from an XmlChan will
+* create a new Object from its XML description.
+*
+* Normally, when you use an XmlChan, you should provide "source"
+c and "sink" functions which connect it to an external data store
+c by reading and writing the resulting XML text. These functions
+f and "sink" routines which connect it to an external data store
+f by reading and writing the resulting XML 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.
+
+* Inheritance:
+* The XmlChan class inherits from the Channel class.
+
+* Attributes:
+* In addition to those attributes common to all Channels, every
+* XmlChan also has the following attributes:
+*
+* - XmlFormat: System for formatting Objects as XML
+* - XmlLength: Controls output buffer length
+* - XmlPrefix: The namespace prefix to use when writing
+
+* Functions:
+c The XmlChan class does not define any new functions beyond those
+f The XmlChan class does not define any new routines beyond those
+* which are applicable to all Mappings.
+
+* Copyright:
+* Copyright (C) 1997-2006 Council for the Central Laboratory of the
+* Research Councils
+* 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:
+* 10-OCT-2003 (DSB):
+* Original version.
+* 6-FEB-2004 (DSB):
+* Added XmlPrefix and XmlFormat attributes.
+* 10-FEB-2004 (DSB):
+* - Added debug conditional code to keep track of memory leaks.
+* - Fixed bug which prevented more than 1 object being read from
+* an XmlChan.
+* 7-DEC-2005 (DSB):
+* Free memory allocated by calls to astReadString.
+* 12-FEB-2010 (DSB):
+* Represent AST__BAD externally using the string "<bad>".
+*class--
+
+* Further STC work:
+* - Speed up general STC processing (a lot of time seems to be spent
+* simplifying things)
+* - Document (including a complete description of what is and is not
+* supported in the reference docs for the XmlFormat attribute).
+* - Produce a schema describing the format which can in fact be read by
+* AST.
+* - Look at Jonathan McDowell's mini-STC schema (also STC stuff in
+* spectral data model)
+* - Web services. Read only: test STCs for overlap, test points for
+* inclusion/exclusion, plot a mask over an image, verification (can AST
+* read it & does it generate warnings?). Read/Write: convert FITS to STC,
+* transform STC into a new coord system.
+* - Add support for writing as well as reading
+* - Modify Stc... constructors to check that the supplied Frame is suitable.
+* - What about multiple AstroCoordFrames and AstroCoordAreas in a STC?
+* - Add support for generic CoordFrames
+* - What should be done with pixel coords info within STC?
+* - Extend coverage (e.g. to 3D space frames, etc)
+
+*/
+
+/* 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 XmlChan
+
+/* The XML element name used to store an AST attribute setting */
+#define ATTR "_attribute"
+
+/* The XML element name used for an AST "isa" element */
+#define ISA "_isa"
+
+/* The XML attribute name which holds the name of the AST class which
+ defines the item contained in the element. */
+#define DEFINEDBY "definedby"
+
+/* The XML attribute name which holds the name of the AST attribute */
+#define NAME "name"
+
+/* The XML attribute name which holds the value of the AST attribute */
+#define VALUE "value"
+
+/* The XML attribute name which indicates if the AST attribute value is a
+ default value. */
+#define DEFAULT "default"
+
+/* The XML attribute name which indicates if the AST attribute value was
+ originally a string value. */
+#define QUOTED "quoted"
+
+/* The XML attribute name which holds a description of the AST attribute. */
+#define DESC "desc"
+
+/* The XML attribute name which holds the label associated with an AST
+ Object (if any). */
+#define LABEL "label"
+
+/* A string used to indicate atrue attribute value */
+#define TRUE "true"
+
+/* Format identifiers and strings */
+#define UNKNOWN_FORMAT -1
+#define NATIVE_FORMAT 0
+#define QUOTED_FORMAT 1
+#define IVOA_FORMAT 2
+#define MAX_FORMAT 2
+#define UNKNOWN_STRING "UNKNOWN"
+#define NATIVE_STRING "NATIVE"
+#define QUOTED_STRING "QUOTED"
+#define IVOA_STRING "IVOA"
+
+/* Values representing message severities. */
+#define WARNING 0
+#define FAILURE 1
+#define RESET 2
+
+/* Known IVOA namespaces. When a new name is added, update the FindIVOAClass
+ function. */
+#define STC_URI "urn:nvo-stc"
+
+/* Known IVOA Classes and attributes. When a new name is added, it may be
+ necessary to update the FindIVOAClass function. */
+#define STC_RESOURCE_PROFILE "STCResourceProfile"
+#define SEARCH_LOCATION "SearchLocation"
+#define OBSERVATION_LOCATION "ObservationLocation"
+#define OBSERVATORY_LOCATION "ObservatoryLocation"
+#define CATALOG_ENTRY_LOCATION "CatalogEntryLocation"
+#define OBS_DATA_LOCATION "ObsDataLocation"
+#define ASTRO_COORD_SYSTEM "AstroCoordSystem"
+#define ASTRO_COORD_AREA "AstroCoordArea"
+#define ASTRO_COORDS "AstroCoords"
+#define TIME_FRAME "TimeFrame"
+#define SPACE_FRAME "SpaceFrame"
+#define SPECTRAL_FRAME "SpectralFrame"
+#define REDSHIFT_FRAME "RedshiftFrame"
+#define DOPPLER_DEFINITION "DopplerDefinition"
+
+/* Returns string "an" or "a" depending on whether the first character of
+ the supplied string is a vowel or not. */
+#define ANA(t) (t?(strchr("AaEeIiOoUu",t[0])?"an":"a"):"")
+
+/* String used to represent AST__BAD externally. */
+#define BAD_STRING "<bad>"
+
+/* Include files. */
+/* ============== */
+/* Interface definitions. */
+/* ---------------------- */
+
+#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 "frame.h" /* Coordinate Frames */
+#include "timeframe.h" /* Time coordinate Frames */
+#include "cmpframe.h" /* Coordinate Frames */
+#include "skyframe.h" /* Celestial coordinate Frames */
+#include "specframe.h" /* Spectral coordinate Frames */
+#include "region.h" /* Regions within coordinate Frames */
+#include "ellipse.h" /* Ellipses within coordinate Frames */
+#include "pointlist.h" /* Points within coordinate Frames */
+#include "polygon.h" /* Polygons within coordinate Frames */
+#include "circle.h" /* Circles within coordinate Frames */
+#include "keymap.h" /* Mapping of keys to values */
+#include "channel.h" /* Interface for parent class */
+#include "xmlchan.h" /* Interface definition for this class */
+#include "loader.h" /* Interface to the global loader */
+#include "object.h" /* Base Object class */
+#include "wcsmap.h" /* Angular conversion constants */
+#include "xml.h" /* AST XML facilities */
+#include "erfa.h" /* ERFA functions */
+#include "stcresourceprofile.h" /* IVOA StcResourceProfile class */
+#include "stcsearchlocation.h" /* IVOA SearchLocation class */
+#include "stccatalogentrylocation.h"/* IVOA CatalogEntryLocation class */
+#include "stcobsdatalocation.h" /* IVOA ObsDataLocation class */
+#include "nullregion.h" /* Null regions */
+#include "interval.h" /* Axis intervals */
+#include "box.h" /* Box regions */
+#include "cmpregion.h" /* Compound regions */
+#include "prism.h" /* Prism regions */
+#include "unitmap.h" /* Unit Mappings */
+#include "unit.h" /* Unit handling utilities */
+#include "pal.h" /* slalib functions */
+#include "globals.h" /* Thread-safe global data access */
+
+/* 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>
+
+/* Type Definitions */
+/* ================ */
+
+/* A type for functions which read an IVOA element and return a
+ corresponding AST Object. */
+typedef AstObject *(*IVOAReader)( AstXmlChan *, AstXmlElement *, int * );
+
+/* A structure to hold the result of scanning the content of an IVOA
+ element.*/
+typedef struct IVOAScan {
+ int n; /* Number of element names described by this structure */
+ int *count; /* Array holding number of each element name found */
+ AstXmlElement ***el; /* Array holding pointers to each element found */
+} IVOAScan;
+
+/* Module Variables. */
+/* ================= */
+
+/* Address of this static variable is used as a unique identifier for
+ member of this class. */
+static int class_check;
+
+/* 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_getfull)( AstChannel *, int * );
+static int (* parent_getcomment)( AstChannel *, int * );
+static int (* parent_getindent)( AstChannel *, int * );
+
+/* Text values used to represent XmlFormat values externally. These
+ should be in the order defined by the associated constants above. */
+static const char *xformat[3] = { NATIVE_STRING, QUOTED_STRING, IVOA_STRING };
+
+/* 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->Class_Init = 0; \
+ globals->IsUsable_This = NULL; \
+ globals->GetAttrib_Buff[ 0 ] = 0; \
+ globals->GetNextChar_C = NULL; \
+ globals->GetNextChar_Buf = NULL;
+
+/* Create the function that initialises global data for this module. */
+astMAKE_INITGLOBALS(XmlChan)
+
+/* Define macros for accessing each item of thread specific global data. */
+#define class_init astGLOBAL(XmlChan,Class_Init)
+#define class_vtab astGLOBAL(XmlChan,Class_Vtab)
+#define isusable_this astGLOBAL(XmlChan,IsUsable_This)
+#define getattrib_buff astGLOBAL(XmlChan,GetAttrib_Buff)
+#define getnextchar_c astGLOBAL(XmlChan,GetNextChar_C)
+#define getnextchar_buf astGLOBAL(XmlChan,GetNextChar_Buf)
+
+
+
+/* If thread safety is not needed, declare and initialise globals at static
+ variables. */
+#else
+
+/* An XmlChan pointer use to communicate with the IsUsable function. */
+static AstXmlChan *isusable_this = NULL;
+
+/* Buffer returned by GetAttrib. */
+static char getattrib_buff[ 51 ];
+
+/* Variables used in GetNextChar */
+static char *getnextchar_c = NULL; /* Pointer to next character to read */
+static char *getnextchar_buf = NULL; /* Pointer to previously read text */
+
+
+/* Define the class virtual function table and its initialisation flag
+ as static variables. */
+static AstXmlChanVtab class_vtab; /* Virtual function table */
+static int class_init = 0; /* Virtual function table initialised? */
+
+#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. */
+AstXmlChan *astXmlChanForId_( const char *(*)( void ),
+ char *(*)( const char *(*)( void ), int * ),
+ void (*)( const char * ),
+ void (*)( void (*)( const char * ), const char *, int * ),
+ const char *, ... );
+AstXmlChan *astXmlChanId_( const char *(* source)( void ),
+ void (* sink)( const char * ),
+ const char *options, ... );
+
+/* Prototypes for Private Member Functions. */
+/* ======================================== */
+static AstObject *AstroCoordSystemReader( AstXmlChan *, AstXmlElement *, int * );
+static AstObject *MakeAstFromXml( AstXmlChan *, AstXmlElement *, int * );
+static AstObject *ObsDataLocationReader( AstXmlChan *, AstXmlElement *, int * );
+static AstObject *Read( AstChannel *, int * );
+static AstObject *ReadObject( AstChannel *, const char *, AstObject *, int * );
+static AstObject *RedshiftFrameReader( AstXmlChan *, AstXmlElement *, int * );
+static AstObject *SpaceFrameReader( AstXmlChan *, AstXmlElement *, int * );
+static AstObject *SpectralFrameReader( AstXmlChan *, AstXmlElement *, int * );
+static AstObject *StcMetadataReader( AstXmlChan *, AstXmlElement *, int * );
+static AstObject *TimeFrameReader( AstXmlChan *, AstXmlElement *, int * );
+static AstPointList *ObservatoryLocationReader( AstXmlChan *, AstXmlElement *, AstStcObsDataLocation *, int * );
+static AstRegion *AllSkyReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * );
+static AstRegion *AstroCoordAreaReader( AstXmlChan *, AstXmlElement *, AstFrame *, AstRegion *[4], int, AstKeyMap **, int * );
+static AstRegion *BoxReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * );
+static AstRegion *CircleReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * );
+static AstRegion *ConstraintReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * );
+static AstRegion *ConvexReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * );
+static AstRegion *Coord2VecIntervalReader( AstXmlChan *, AstXmlElement *, const char *, AstFrame *, int * );
+static AstRegion *Coord3VecIntervalReader( AstXmlChan *, AstXmlElement *, const char *, AstFrame *, int * );
+static AstRegion *CoordScalarIntervalReader( AstXmlChan *, AstXmlElement *, const char *, AstFrame *, int * );
+static AstRegion *EllipseReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * );
+static AstRegion *IntersectionReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * );
+static AstRegion *NegationReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * );
+static AstRegion *PolygonReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * );
+static AstRegion *Position2DReader( AstXmlChan *, AstXmlElement *, AstFrame *, double *, AstKeyMap **, int * );
+static AstRegion *PositionIntervalReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * );
+static AstRegion *RedshiftIntervalReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * );
+static AstRegion *RedshiftReader( AstXmlChan *, AstXmlElement *, AstFrame *, AstKeyMap **, int * );
+static AstRegion *StcRegionReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * );
+static AstRegion *RegionReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * );
+static AstRegion *SpectralIntervalReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * );
+static AstRegion *SpectralReader( AstXmlChan *, AstXmlElement *, AstFrame *, double *, AstKeyMap **, int * );
+static AstRegion *SphereReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * );
+static AstRegion *TimeIntervalReader( AstXmlChan *, AstXmlElement *, AstTimeFrame *, int * );
+static AstRegion *TimeReader( AstXmlChan *, AstXmlElement *, AstTimeFrame *, double *, AstKeyMap **, int * );
+static AstRegion *UnionReader( AstXmlChan *, AstXmlElement *, AstFrame *, int * );
+static AstSystemType RedshiftSys( AstXmlChan *, AstXmlElement *, char **, int, int * );
+static AstSystemType SpecSys( AstXmlChan *, AstXmlElement *, const char *, int, int * );
+static AstXmlElement *FindAttribute( AstXmlChan *, const char *, int * );
+static AstXmlElement *FindElement( AstXmlChan *, AstXmlElement *, const char *, int * );
+static AstXmlElement *FindObject( AstXmlChan *, const char *, int * );
+static AstXmlElement *MakePos2D( AstXmlChan *, AstXmlElement *, int * );
+static AstXmlElement *ReadXmlText( AstXmlChan *, int * );
+static AstXmlElement *Remove( AstXmlChan *, AstXmlElement *, int * );
+static IVOAReader FindIVOAClass( AstXmlElement *, int *, int * );
+static IVOAScan *FreeIVOAScan( IVOAScan *, int * );
+static IVOAScan *ScanIVOAElement( AstXmlChan *, AstXmlElement *, int, const char *[], int[], int[], int * );
+static char *ReadString( AstChannel *, const char *, const char *, int * );
+static char *SourceWrap( const char *(*)( void ), int * );
+static char GetNextChar( void *, int * );
+static const char *FindNextIsA( AstXmlElement *, int, int * );
+static const char *GetAttrib( AstObject *, const char *, int * );
+static const char *GetTag( AstXmlObject *, int, int * );
+static double AstronTimeReader( AstXmlChan *, AstXmlElement *, AstTimeFrame *, int * );
+static double AttrValueD( AstXmlChan *, AstXmlElement *, const char *, double, int * );
+static double ElemValueD( AstXmlChan *, AstXmlElement *, double, int * );
+static double Error2PAReader( AstXmlChan *, AstXmlElement *, double *, int * );
+static double MakeMJD( AstTimeFrame *, double, int * );
+static double PosAngleReader( AstXmlChan *, AstXmlElement *, int * );
+static double ReadDouble( AstChannel *, const char *, double, int * );
+static int AstroCoordsReader( AstXmlChan *, AstXmlElement *, AstFrame *, AstRegion *[4], AstKeyMap **, int * );
+static int AttrValueB( AstXmlChan *, AstXmlElement *, const char *, int, int * );
+static int AttrValueI( AstXmlChan *, AstXmlElement *, const char *, int, int * );
+static int ElemListD( AstXmlChan *, AstXmlElement *, int, double *, int * );
+static int FindString( int, const char *[], const char *, const char *, const char *, const char *, int * );
+static int GetComment( AstChannel *, int * );
+static int GetFull( AstChannel *, int * );
+static int GetIndent( AstChannel *, int * );
+static int IsUsable( AstXmlElement *, int * );
+static int ReadInt( AstChannel *, const char *, int, int * );
+static int TestAttrib( AstObject *, const char *, int * );
+static int Use( AstXmlChan *, int, int, int * );
+static int Ustrcmp( const char *, const char *, int * );
+static int Ustrncmp( const char *, const char *, size_t, int * );
+static int VertexReader( AstXmlChan *, AstXmlElement *, double *, double *, int * );
+static void ClearAttrib( AstObject *, const char *, int * );
+static void Copy( const AstObject *, AstObject *, int * );
+static void Delete( AstObject *, int * );
+static void Dump( AstObject *, AstChannel *, int * );
+static void FillAndLims( AstXmlChan *, AstXmlElement *, AstRegion *, int * );
+static void OutputText( AstXmlChan *, const char *, int, int * );
+static void ReCentreAnc( AstRegion *, int, AstKeyMap **, int * );
+static void ReadClassData( AstChannel *, const char *, int * );
+static void Report( AstXmlChan *, AstXmlElement *, int, const char *, int * );
+static void SetAttrib( AstObject *, const char *, int * );
+static void SinkWrap( void (*)( const char * ), const char *, int * );
+static void WriteBegin( AstChannel *, const char *, const char *, int * );
+static void WriteDouble( AstChannel *, const char *, int, int, double, const char *, int * );
+static void WriteEnd( AstChannel *, const char *, int * );
+static void WriteInt( AstChannel *, const char *, int, int, int, const char *, int * );
+static void WriteIsA( AstChannel *, const char *, const char *, int * );
+static void WriteObject( AstChannel *, const char *, int, int, AstObject *, const char *, int * );
+static void WriteString( AstChannel *, const char *, int, int, const char *, const char *, int * );
+static AstTimeScaleType TimeScaleReader( AstXmlChan *, AstXmlElement *, int * );
+
+static int TestXmlLength( AstXmlChan *, int * );
+static void ClearXmlLength( AstXmlChan *, int * );
+static void SetXmlLength( AstXmlChan *, int, int * );
+static int GetXmlLength( AstXmlChan *, int * );
+
+static int TestXmlFormat( AstXmlChan *, int * );
+static void ClearXmlFormat( AstXmlChan *, int * );
+static void SetXmlFormat( AstXmlChan *, int, int * );
+static int GetXmlFormat( AstXmlChan *, int * );
+
+static int TestXmlPrefix( AstXmlChan *, int * );
+static void ClearXmlPrefix( AstXmlChan *, int * );
+static void SetXmlPrefix( AstXmlChan *, const char *, int * );
+static const char * GetXmlPrefix( AstXmlChan *, int * );
+
+/* Member functions. */
+/* ================= */
+
+static AstRegion *AllSkyReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, int *status ){
+/*
+* Name:
+* AllSkyReader
+
+* Purpose:
+* Make an AST Region from an IVOA AllSky element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *AllSkyReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* AllSky element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA AllSky element.
+* frm
+* Pointer to the 2D Frame in which the returned Region should be
+* defined. If the Unit attribute is not set, this function will
+* set it to the value supplied in "unit" before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstRegion *new; /* Pointer to returned Region */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Create a negated NullRegion (this is a boundless Region which includes
+ all points in the Frame). */
+ new = (AstRegion *) astNullRegion( frm, NULL, "negated=1", status );
+
+/* Get any fill factor from the element and assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static AstRegion *AstroCoordAreaReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, AstRegion *uncs[4],
+ int nanc, AstKeyMap **ancs, int *status ) {
+/*
+* Name:
+* AstroCoordAreaReader
+
+* Purpose:
+* Make an AST Region from an IVOA AstroCoordArea element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *AstroCoordAreaReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, AstRegion *uncs[4],
+* int nanc, AstKeyMap **ancs, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* AstroCoordArea element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA AstroCoordArea element. May be NULL, in
+* which case a NullRegion is returned.
+* frm
+* The Frame in which the returned Region is to be defined. If
+* Units or reference values (Epoch, RestFreq, RefRA, etc) are not set
+* for any axes, then they will be set by this function if possible.
+* uncs
+* Array holding pointers to the uncertainty Regions to be associated
+* with each of the four STC domains (space, time, spectral, redshift).
+* NULL should be suppied in any element for which no uncertainty is
+* available.
+* nanc
+* Number of KeyMap pointers stored in "ancs"
+* ancs
+* Pointer to an array of "nanc" elements, each being a pointer to
+* a KeyMap. Each one describes the ancilary information in an
+* AstroCoords element associated with the AstroCoordsArea decribed
+* by "region". Each KeyMap has elements with keys AST__STCERROR,
+* AST__STCRES, AST__STCSIZE, AST__STCPIXSZ, AST__STCVALUE each of
+* which holds a pointer to a Region.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+*/
+
+/* Local Variables: */
+ AstRegion *r;
+ AstFrame *cfrm;
+ AstFrame *fr;
+ AstFrame *pfrm;
+ AstFrame *red_frame;
+ AstFrame *space_frame;
+ AstFrame *spec_frame;
+ AstFrameSet *fs;
+ AstMapping *map;
+ AstObject *o;
+ AstRegion **red_list;
+ AstRegion **spec_list;
+ AstRegion **space_list;
+ AstRegion **time_list;
+ AstRegion *new;
+ AstRegion *reg;
+ AstRegion *rred;
+ AstRegion *rspec;
+ AstRegion *rspace;
+ AstRegion *rtime;
+ AstRegion *sum;
+ AstRegion *tmp;
+ AstTimeFrame *time_frame;
+ IVOAScan *scan;
+ char *decset;
+ char *raset;
+ char buff[ AST__DBL_WIDTH + 30 ];
+ char setting[ 100 ];
+ const char *dom;
+ const char *id;
+ const char *names[4];
+ const char *name;
+ const char *old_units;
+ const char *text;
+ double decref;
+ double lbnd[2];
+ double raref;
+ double space_val[2];
+ double spec_val;
+ double time_val;
+ double ubnd[2];
+ int i;
+ int ianc;
+ int ired;
+ int ispace;
+ int ispec;
+ int itime;
+ int k;
+ int l;
+ int max[4];
+ int min[4];
+ int nax;
+ int nred;
+ int nspace;
+ int nspec;
+ int ntime;
+ int paxis;
+
+ static const char *key[ 5 ] = { AST__STCERROR,
+ AST__STCRES,
+ AST__STCSIZE,
+ AST__STCPIXSZ,
+ AST__STCVALUE };
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* If null AstroCoordArea element has been supplied, return a NullRegion. */
+ if( !elem ) {
+ new = (AstRegion *) astNullRegion( frm, NULL, "", status );
+
+/* Otherwise, create a Region of suitable class. */
+ } else {
+
+/* First identify the individual Frames within the supplied Frame. Current
+ implementation for spatial axes is limited to celestial longitude and
+ latitude. */
+ space_frame = NULL;
+ spec_frame = NULL;
+ red_frame = NULL;
+ time_frame = NULL;
+
+ nax = astGetNaxes( frm );
+ for( i = 0; i < nax; i++ ) {
+ astPrimaryFrame( frm, i, &pfrm, &paxis );
+ dom = astGetDomain( pfrm );
+ if( !strcmp( dom, "SKY" ) ) {
+ if( !space_frame ) {
+ space_frame = astClone( pfrm );
+ } else if( pfrm != space_frame) {
+ Report( this, elem, FAILURE, "contains more than 2 spatial axes", status );
+ }
+
+ } else if( !strcmp( dom, "TIME" ) ) {
+ if( !time_frame ) {
+ if( astIsATimeFrame( pfrm ) ) {
+ time_frame = (AstTimeFrame *) astClone( pfrm );
+ } else if( astOK ) {
+ astError( AST__INTER, "AstroCoordAreaReader(XmlChan): %s "
+ "supplied where TimeFrame expected (internal "
+ "AST programming error).", status, astGetClass( pfrm ) );
+ }
+ } else {
+ Report( this, elem, FAILURE, "contains more than 1 time axis", status );
+ }
+
+ } else if( !strcmp( dom, "SPECTRUM" ) ) {
+ if( !spec_frame ) {
+ spec_frame = astClone( pfrm );
+ } else {
+ Report( this, elem, FAILURE, "contains more than 1 spectral axis", status );
+ }
+
+ } else if( !strcmp( dom, "REDSHIFT" ) ) {
+ if( !red_frame ) {
+ red_frame = astClone( pfrm );
+ } else {
+ Report( this, elem, FAILURE, "contains more than 1 redshift axis", status );
+ }
+
+ } else {
+ Report( this, elem, FAILURE, "contains axes for an unsupported domain", status );
+ }
+ pfrm = astAnnul( pfrm );
+ }
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "Sphere|PositionInterval|Region";
+ names[ 1 ] = "TimeInterval";
+ names[ 2 ] = "SpectralInterval";
+ names[ 3 ] = "RedshiftInterval";
+ min[ 0 ] = 0;
+ min[ 1 ] = 0;
+ min[ 2 ] = 0;
+ min[ 3 ] = 0;
+ max[ 0 ] = INT_MAX;
+ max[ 1 ] = INT_MAX;
+ max[ 2 ] = INT_MAX;
+ max[ 3 ] = INT_MAX;
+ scan = ScanIVOAElement( this, elem, 4, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Create Regions for all the SpatialIntervals found in the supplied element. */
+ space_val[ 0 ] = AST__BAD;
+ space_val[ 1 ] = AST__BAD;
+ nspace = scan->count[ 0 ];
+ space_list = astMalloc( sizeof(AstRegion *)*(size_t)nspace );
+ if( space_list ) {
+ for( ispace = 0; ispace < nspace; ispace++ ) {
+ name = astXmlGetName( scan->el[ 0 ][ ispace ] );
+ if( !strcmp( name, "Sphere" ) ) {
+ space_list[ ispace ] = SphereReader( this,
+ scan->el[ 0 ][ ispace ],
+ space_frame, status );
+ } else if( !strcmp( name, "PositionInterval" ) ) {
+ space_list[ ispace ] = PositionIntervalReader( this,
+ scan->el[ 0 ][ ispace ],
+ space_frame, status );
+ } else if( !strcmp( name, "Region" ) ) {
+ space_list[ ispace ] = StcRegionReader( this,
+ scan->el[ 0 ][ ispace ],
+ space_frame, status );
+ } else if( astOK ) {
+ astError( AST__INTER, "AstroCoordAreaReader(XmlChan): "
+ "SpatialInterval type %s not yet supported "
+ "(AST internal programming error).", status, name );
+ break;
+ }
+
+/* Store any uncertainty region.*/
+ if( uncs[ 0 ] ) astSetUnc( space_list[ ispace ], uncs[ 0 ] );
+
+ }
+
+/* If the spatial region is a single point we will use the point as the
+ reference position for any SpecFrames which are created. If there is
+ just one spatial interval, and if it is bounded. and if the bounds are
+ equal on both axes, note the mean position. */
+ if( nspace == 1 ){
+ if( astGetBounded( space_list[ 0 ] ) ) {
+ astGetRegionBounds( space_list[ 0 ], lbnd, ubnd );
+ if( astEQUAL( lbnd[ 0 ], ubnd[ 0 ] ) &&
+ astEQUAL( lbnd[ 1 ], ubnd[ 1 ] ) ) {
+ space_val[ 0 ] = 0.5*( lbnd[ 0 ] + ubnd[ 0 ] );
+ space_val[ 1 ] = 0.5*( lbnd[ 1 ] + ubnd[ 1 ] );
+ }
+ }
+ }
+ }
+
+/* Create Regions for all the TimeIntervals found in the supplied element. */
+ time_val = AST__BAD;
+ ntime = scan->count[ 1 ];
+ time_list = astMalloc( sizeof(AstRegion *)*(size_t)ntime );
+ if( time_list ) {
+ for( itime = 0; itime < ntime; itime++ ) {
+ time_list[ itime ] = TimeIntervalReader( this,
+ scan->el[ 1 ][ itime ],
+ time_frame, status );
+
+/* Store any uncertainty region. Transfer the System and TimeOrigin
+ values from the time region to the time uncertainty, if set. */
+ if( uncs[ 1 ] ) {
+
+ if( astTestSystem( time_frame ) &&
+ astTestTimeOrigin( time_frame ) ) {
+
+ sprintf( setting, "System=%s",
+ astGetC( time_frame, "System" ) );
+ astRegSetAttrib( uncs[ 1 ], setting, NULL );
+
+
+ if( astTestUnit( time_frame, 0 ) ) {
+ old_units = astGetUnit( time_frame, 0 );
+ old_units = astStore( NULL, old_units,
+ strlen( old_units ) + 1 );
+ } else {
+ old_units = NULL;
+ }
+
+ astSetUnit( time_frame, 0, astGetUnit( uncs[ 1 ], 0 ) );
+
+ sprintf( setting, "TimeOrigin=%s",
+ astGetC( time_frame, "TimeOrigin" ) );
+ astRegSetAttrib( uncs[ 1 ], setting, NULL );
+
+ if( old_units ) {
+ astSetUnit( time_frame, 0, old_units );
+ old_units = astFree( (void *) old_units );
+ } else {
+ astClearUnit( time_frame, 0 );
+ }
+
+ }
+
+ astSetUnc( time_list[ itime ], uncs[ 1 ] );
+ }
+ }
+
+/* Use the mid point as the Epoch for all Frames which are created. If
+ either limit is not specified, use the specified limit. */
+ if( ntime > 0 ){
+ astGetRegionBounds( time_list[ 0 ], lbnd, ubnd );
+ if( fabs( lbnd[ 0 ] ) != DBL_MAX && lbnd[ 0 ] != AST__BAD ){
+ if( fabs( ubnd[ 0 ] ) != DBL_MAX && ubnd[ 0 ] != AST__BAD ){
+ time_val = 0.5*( lbnd[ 0 ] + ubnd[ 0 ] );
+ } else {
+ time_val = lbnd[ 0 ];
+ }
+ } else if( fabs( ubnd[ 0 ] ) != DBL_MAX && ubnd[ 0 ] != AST__BAD ){
+ time_val = ubnd[ 0 ];
+ }
+ }
+ }
+
+/* Create Regions for all the SpectralIntervals found in the supplied element. */
+ spec_val = AST__BAD;
+ nspec = scan->count[ 2 ];
+ spec_list = astMalloc( sizeof(AstRegion *)*(size_t)nspec );
+ if( spec_list ) {
+ for( ispec = 0; ispec < nspec; ispec++ ) {
+ spec_list[ ispec ] = SpectralIntervalReader( this,
+ scan->el[ 2 ][ ispec ],
+ spec_frame, status );
+/* Store any uncertainty region.*/
+ if( uncs[ 2 ] ) astSetUnc( spec_list[ ispec ], uncs[ 2 ] );
+ }
+
+/* If the spectral region is a single point we will use the point as the
+ rest frequency for all RedShift Frames which are created. If there is just
+ one spectral interval, and if it is bounded. and if the bounds are equal,
+ note the mean spectral value. */
+ if( nspec == 1 ){
+ if( astGetBounded( spec_list[ 0 ] ) ) {
+ astGetRegionBounds( spec_list[ 0 ], lbnd, ubnd );
+ if( astEQUAL( lbnd[ 0 ], ubnd[ 0 ] ) ) {
+ spec_val = 0.5*( lbnd[ 0 ] + ubnd[ 0 ] );
+ }
+ }
+ }
+ }
+
+/* Create Regions for all the RedshiftIntervals found in the supplied element. */
+ nred = scan->count[ 3 ];
+ red_list = astMalloc( sizeof(AstRegion *)*(size_t)nred );
+ if( red_list ) {
+ for( ired = 0; ired < nred; ired++ ) {
+ red_list[ ired ] = RedshiftIntervalReader( this,
+ scan->el[ 3 ][ ired ],
+ red_frame, status );
+/* Store any uncertainty region.*/
+ if( uncs[ 3 ] ) astSetUnc( red_list[ ired ], uncs[ 3 ] );
+ }
+ }
+
+/* Free the can result structure.*/
+ scan = FreeIVOAScan( scan, status );
+
+/* If the spatial regions cover only a single point, convert it to FK5
+ J2000 and use it as the reference position for any SpecFrames (spectral or
+ redshift) unless values were inherited from the supplied Frame. If the
+ supplied Frame did not contain set values for these attributes, set them
+ now. Use astRegSetAttrib which applies the attribute setting to both
+ base and current Frame of the Region's FrameSet, and avoids re-mapping
+ the current Frame. */
+ if( astOK ) {
+ if( space_val[ 0 ] != AST__BAD && space_val[ 1 ] != AST__BAD ) {
+
+/* First need to convert to FK5 J2000 and format into a string for use with
+ astRegSetAttrib. Need to ensure that the Format and Digits attributes
+ are set to values which will result in no loss of precision in the
+ formatting and unformatting steps. */
+ fr = astCopy( space_frame );
+ astClear( fr, "Format(1),Format(2),Digits(1),Digits(2)" );
+ astSet( fr, "digits=%d,system=FK5,equinox=J2000", status, AST__DBL_DIG);
+ fs = astConvert( space_frame, fr, "" );
+ fr = astAnnul( fr );
+ if( fs ) {
+ astTran2( fs, 1, space_val, space_val + 1, 1, &raref, &decref );
+
+ text = astFormat( fs, raref, 0 );
+ l = text ? strlen( text ) : 0;
+ raset = astMalloc( l + 10 );
+ if( raset ) sprintf( raset, "refra=%s", text );
+
+ text = astFormat( fs, decref, 1 );
+ l = text ? strlen( text ) : 0;
+ decset = astMalloc( l + 10 );
+ if( decset ) sprintf( decset, "refdec=%s", text );
+
+ fs = astAnnul( fs );
+
+/* Now set the FK5 J2000 values in the required Frames and Regions. */
+ if( !spec_frame || !astTestRefRA( spec_frame ) ||
+ !astTestRefDec( spec_frame ) ) {
+ for( ispec = 0; ispec < nspec; ispec++ ) {
+ astRegSetAttrib( spec_list[ ispec ], raset, NULL );
+ astRegSetAttrib( spec_list[ ispec ], decset, NULL );
+ }
+
+ if( spec_frame ) {
+ astSetRefRA( (AstSpecFrame *) spec_frame, raref );
+ astSetRefDec( (AstSpecFrame *) spec_frame, decref );
+ }
+ }
+
+ if( !red_frame || !astTestRefRA( red_frame ) ||
+ !astTestRefDec( red_frame ) ) {
+ for( ired = 0; ired < nred; ired++ ) {
+ astRegSetAttrib( red_list[ ired ], raset, NULL );
+ astRegSetAttrib( red_list[ ired ], decset, NULL );
+ }
+
+ if( red_frame ) {
+ astSetRefRA( (AstSpecFrame *) red_frame, raref );
+ astSetRefDec( (AstSpecFrame *) red_frame, decref );
+ }
+ }
+
+ for( ianc = 0; ianc < nanc; ianc++ ) {
+ for( k = 0; k < 5; k++ ) {
+ if( astMapGet0A( ancs[ ianc ], key[ k ], &o ) ) {
+ r = (AstRegion *) o;
+ astRegSetAttrib( r, raset, NULL );
+ astRegSetAttrib( r, decset, NULL );
+ r = astAnnul( r );
+ }
+ }
+ }
+
+/* Free resources. */
+ if( raset ) raset = astFree( raset );
+ if( decset ) decset = astFree( decset );
+
+ } else if( astOK ) {
+ astError( AST__INTER, "AstroCoordAreaReader(XmlChan):"
+ " Cannot convert spatial position to FK5 J2000" , status);
+ }
+ }
+
+/* If a time region was specified, use a typical value as the epoch for
+ all Frames. Call MakeMJD to convert "time_val" from the system of the
+ TimeFrame to an MJD (as required by the Frame Epoch attribute). Set
+ the value in both the returned Region and the supplied Frame. */
+ if( time_val != AST__BAD ) {
+ fr = astRegFrame( time_list[ 0 ] );
+ if( astIsATimeFrame( fr ) ) {
+ time_val = MakeMJD( (AstTimeFrame *) fr, time_val, status );
+ } else if( astOK ) {
+ astError( AST__INTER, "AstroCoordAreaReader(XmlChan): %s "
+ "supplied where TimeFrame expected (internal "
+ "AST programming error).", status, astGetClass( fr ) );
+ }
+ fr = astAnnul( fr );
+
+ sprintf( buff, "epoch= MJD %.*g", AST__DBL_DIG, time_val );
+
+ if( !space_frame || !astTestEpoch( space_frame ) ) {
+ for( ispace = 0; ispace < nspace; ispace++ ) {
+ astRegSetAttrib( space_list[ ispace ], buff, NULL );
+ }
+ if( space_frame ) astSetEpoch( space_frame, time_val );
+ }
+
+ if( !spec_frame || !astTestEpoch( spec_frame ) ) {
+ for( ispec = 0; ispec < nspec; ispec++ ) {
+ astRegSetAttrib( spec_list[ ispec ], buff, NULL );
+ }
+ if( spec_frame ) astSetEpoch( spec_frame, time_val );
+ }
+
+ if( !red_frame || !astTestEpoch( red_frame ) ) {
+ for( ired = 0; ired < nred; ired++ ) {
+ astRegSetAttrib( red_list[ ired ], buff, NULL );
+ }
+ if( red_frame ) astSetEpoch( red_frame, time_val );
+ }
+
+ for( ianc = 0; ianc < nanc; ianc++ ) {
+ for( k = 0; k < 5; k++ ) {
+ if( astMapGet0A( ancs[ ianc ], key[ k ], &o ) ) {
+ r = (AstRegion *) o;
+ astRegSetAttrib( r, buff, NULL );
+ r = astAnnul( r );
+ }
+ }
+ }
+
+ }
+
+/* If the spectral regions cover only a single point, format it with its
+ units so that the astSetAttrib function can convert it to Hz and use
+ it as the rest frequency for any redshift Frames. */
+ if( spec_val != AST__BAD && nred > 0 ) {
+
+ text = astGetUnit( spec_frame, 0 );
+ if( text ) sprintf( buff, "restfreq= %.*g %s", AST__DBL_DIG,
+ spec_val, text );
+
+ if( !red_frame || !astTestRestFreq( red_frame ) ) {
+ for( ired = 0; ired < nred; ired++ ) {
+ astRegSetAttrib( red_list[ ired ], buff, NULL );
+ }
+ if( red_frame ) astSetAttrib( red_frame, buff );
+ }
+
+ for( ianc = 0; ianc < nanc; ianc++ ) {
+ for( k = 0; k < 5; k++ ) {
+ if( astMapGet0A( ancs[ ianc ], key[ k ], &o ) ) {
+ r = (AstRegion *) o;
+ astRegSetAttrib( r, buff, NULL );
+ r = astAnnul( r );
+ }
+ }
+ }
+ }
+
+/* Create Regions corresponding to every possible combination of interval
+ on each axis type, and assemble the union of these into a CmpRegion (if
+ there is more than one). */
+ sum = NULL;
+
+/* Initialise indices of the sub-Frame intervals to use. */
+ ispace = 0;
+ itime = 0;
+ ispec = 0;
+ ired = 0;
+
+/* Loop over all possible combinations of time+space+spec+red intervals. */
+ while( 1 ) {
+ rspace = ( ispace < nspace ) ? space_list[ ispace ] : NULL;
+ rtime = ( itime < ntime ) ? time_list[ itime ] : NULL;
+ rspec = ( ispec < nspec ) ? spec_list[ ispec ] : NULL;
+ rred = ( ired < nred ) ? red_list[ ired ] : NULL;
+
+/* Prism Regions extrude a Region into higher dimensions, and the
+ extrusion is defined by an Interval. Spatial Regions are not
+ restricted to Intervals and so any spatial Region must be the first
+ Region to be included in the Prism (all the other axis types *are*
+ restricted to Intervals and so can be used to extrude the spatial
+ region). */
+ reg = rspace ? astClone( rspace ) : NULL;
+
+/* Now extrude this region (if any) into the time axis. */
+ if( rtime ) {
+ if( reg ) {
+ tmp = (AstRegion *) astPrism( reg, rtime, "", status );
+ (void) astAnnul( reg );
+ reg = tmp;
+ } else {
+ reg = astClone( rtime );
+ }
+ }
+
+/* Now extrude this region (if any) into the spectral axis. */
+ if( rspec ) {
+ if( reg ) {
+ tmp = (AstRegion *) astPrism( reg, rspec, "", status );
+ (void) astAnnul( reg );
+ reg = tmp;
+ } else {
+ reg = astClone( rspec );
+ }
+ }
+
+/* Now extrude this region (if any) into the redshift axis. */
+ if( rred ) {
+ if( reg ) {
+ tmp = (AstRegion *) astPrism( reg, rred, "", status );
+ (void) astAnnul( reg );
+ reg = tmp;
+ } else {
+ reg = astClone( rred );
+ }
+ }
+
+
+/* If a Prism was created, add it into the CmpRegion which holds the
+ running sum of the union of all Prisms created so far. */
+ if( reg ) {
+ if( !sum ) {
+ sum = astClone( reg );
+ } else {
+ tmp = (AstRegion *) astCmpRegion( sum, reg, AST__OR, "", status );
+ (void) astAnnul( sum );
+ sum = tmp;
+ }
+ reg = astAnnul( reg );
+ }
+
+/* Increment the indices of the next set of sub-Frame Intervals to use.
+ Leave the while loop when all combinations have been done. */
+ if( ++ired >= nred ) {
+ ired = 0;
+ if( ++ispec >= nspec ) {
+ ispec = 0;
+ if( ++itime >= ntime ) {
+ itime = 0;
+ if( ++ispace >= nspace ) break;
+ }
+ }
+ }
+ }
+
+/* Simplify the total sum Region. */
+ tmp = astSimplify( sum );
+ (void) astAnnul( sum );
+ sum = tmp;
+
+/* The axes in this sum Region may not be in the correct order or units (i.e
+ in the order and units specified in the supplied Frame). So use
+ astConvert to get a Mapping from the Frame represented by the sum
+ Region to the supplied Frame. */
+ fs = astConvert( sum, frm, "" );
+ if( fs ) {
+
+/* Unless the Mapping is a UnitMap, remap the sum Region into the
+ supplied Frame using this Mapping. */
+ map = astGetMapping( fs, AST__BASE, AST__CURRENT );
+ if( !astIsAUnitMap( map ) ) {
+ new = astMapRegion( sum, map, frm );
+ } else {
+ new = astClone( sum );
+ }
+
+ map = astAnnul( map );
+ fs = astAnnul( fs );
+
+ } else if( astOK ) {
+ astError( AST__INTER, "AstroCoordAreaReader(%s): Cannot "
+ "convert from supplied Frame to internal Frame (AST "
+ "internal programming error).", status, astGetClass( this ) );
+ }
+
+/* Transfer selected properties from the supplied Frame to the current Frame
+ of the returned Region. */
+ cfrm = astRegFrame( new );
+ if( astTestIdent( frm ) ) astSetIdent( cfrm, astGetIdent( frm ) );
+ if( astTestTitle( frm ) ) astSetTitle( cfrm, astGetTitle( frm ) );
+
+/* Ensure the Epoch is set correctly in the Region */
+ if( time_val != AST__BAD ) {
+ sprintf( buff, "epoch= MJD %.*g", AST__DBL_DIG, time_val );
+ astRegSetAttrib( new, buff, NULL );
+ }
+
+/* Free resources. */
+ cfrm = astAnnul( cfrm );
+ sum = astAnnul( sum );
+ }
+
+ if( space_list ) {
+ for( i = 0; i < nspace; i++ ) space_list[ i ] = astAnnul( space_list[ i ] );
+ space_list = astFree( space_list );
+ }
+
+ if( time_list ) {
+ for( i = 0; i < ntime; i++ ) time_list[ i ] = astAnnul( time_list[ i ] );
+ time_list = astFree( time_list );
+ }
+
+ if( spec_list ) {
+ for( i = 0; i < nspec; i++ ) spec_list[ i ] = astAnnul( spec_list[ i ] );
+ spec_list = astFree( spec_list );
+ }
+
+ if( red_list ) {
+ for( i = 0; i < nred; i++ ) red_list[ i ] = astAnnul( red_list[ i ] );
+ red_list = astFree( red_list );
+ }
+
+ }
+
+ if( space_frame ) space_frame = astAnnul( space_frame );
+ if( time_frame ) time_frame = astAnnul( time_frame );
+ if( spec_frame ) spec_frame = astAnnul( spec_frame );
+ if( red_frame ) red_frame = astAnnul( red_frame );
+
+/* Get the ID attribute from the AstroCoordArea element and store in the
+ returned Region. */
+ id = astXmlGetAttributeValue( elem, "ID" );
+ if( id ) astSetIdent( new, id );
+
+ }
+
+/* If an error has occurred,annul the returned pointer. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static int AstroCoordsReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, AstRegion *uncs[4],
+ AstKeyMap **anc, int *status ) {
+/*
+* Name:
+* AstroCoordsReader
+
+* Purpose:
+* Modify a Frame to take account of an IVOA AstroCoords element, and
+* return an coordinate uncertainties.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* int AstroCoordsReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, AstRegion *uncs[4],
+* AstKeyMap **anc, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function modifies the supplied Frame object to incorporate the
+* effects of the supplied AstroCoords element. It may also return
+* Regions representing the bounds of the uncertainties in the four
+* component coordinate Frames, depending on the contents of the
+* AstroCoords element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA AstroCoords element.
+* frm
+* The Frame object to modify.
+* uncs
+* Array in which to return pointers to the uncertainty Regions to
+* be associated with each of the four STC domains (space, time,
+* spectral, redshift). NULL is returned in any element for which
+* no uncertainty is specified within the supplied AstroCoords element.
+* anc
+* Address of a location at which to store the pointer to a newly
+* created KeyMap holding ancillary information describing the
+* AstroCoords element in the form required by constructors of AST
+* Stc objects. A NULL pointer is returned if no usable ancillary
+* information is found in the AstroCoords.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Non-zero if any non-NULL values have been returned in the "uncs"
+* array. Zero otherwise.
+
+*/
+
+/* Local Variables: */
+ AstFrame *afrm; /* Pointer to axis Frame */
+ AstFrame *gfrm; /* Pointer to generic Frame */
+ AstFrame *pfrm; /* Pointer to position Frame */
+ AstFrame *rfrm; /* Pointer to redshift Frame */
+ AstFrame *sfrm; /* Pointer to spectral Frame */
+ AstTimeFrame *tfrm; /* Pointer to time Frame */
+ AstKeyMap *panc; /* KeyMap holding spatial ancillary data */
+ AstKeyMap *ranc; /* KeyMap holding redshift ancillary data */
+ AstKeyMap *sanc; /* KeyMap holding spectral ancillary data */
+ AstKeyMap *tanc; /* KeyMap holding temporal ancillary data */
+ AstObject *o; /* Pointer to object retrieved from KeyMap */
+ AstRegion *r; /* Individual ancillary Region */
+ AstRegion *t; /* Total extruded ancillary Region */
+ AstRegion *tt; /* Temporary Region pointer */
+ AstXmlElement *el; /* Pointer to Position2D element */
+ IVOAScan *scan; /* Structure holding scan results */
+ char **anames; /* Pointer to list of ancillary name pointers */
+ const char *dom; /* Pointer to Domain attribute value */
+ const char *nam; /* Pointer to ancillary Name string */
+ const char *names[4]; /* Names of the subelements to be searched for */
+ char buff[100]; /* Message buffer */
+ double epoch; /* Epoch */
+ double hi; /* High limit for zero-width interval */
+ double lo; /* Low limit for zero-width interval */
+ double pos[2]; /* Reference spatial position */
+ double rf; /* Rest frequency */
+ int axes[2]; /* Indices of position axes */
+ int axis; /* Index of next axis to use */
+ int empty; /* Is returned KeyMap empty? */
+ int i; /* Loop count */
+ int isearth; /* Does the SkyFrame represent terrestrial lon/lat? */
+ int junk; /* Unused integer value */
+ int max[4]; /* Max allowed occurrences of each name */
+ int min[4]; /* Min allowed occurrences of each name */
+ int nax; /* Number of axes in supplied Frame */
+ int unc; /* Any uncertainty Regions found? */
+ int use; /* Use ancillary information? */
+
+ static const char *key[ 5 ] = { AST__STCERROR,
+ AST__STCRES,
+ AST__STCSIZE,
+ AST__STCPIXSZ,
+ AST__STCVALUE };
+/* Initialise */
+ unc = 0;
+ uncs[ 0 ] = NULL;
+ uncs[ 1 ] = NULL;
+ uncs[ 2 ] = NULL;
+ uncs[ 3 ] = NULL;
+ *anc = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return unc;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "Position2D|Position3D";
+ names[ 1 ] = "Time";
+ names[ 2 ] = "Spectral";
+ names[ 3 ] = "Redshift";
+ min[ 0 ] = 0;
+ min[ 1 ] = 0;
+ min[ 2 ] = 0;
+ min[ 3 ] = 0;
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ max[ 2 ] = 1;
+ max[ 3 ] = 1;
+ scan = ScanIVOAElement( this, elem, 4, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Initialise pointers to component Frames */
+ pfrm = NULL;
+ tfrm = NULL;
+ sfrm = NULL;
+ rfrm = NULL;
+
+/* Initialise pointers to KeyMaps holding ancillary data. */
+ panc = NULL;
+ tanc = NULL;
+ sanc = NULL;
+ ranc = NULL;
+
+/* Allocate storage for an array of pointers to strings holding the Name
+ value for each axis. Initialise them to a null string. */
+ nax = astGetNaxes( frm );
+ anames = astMalloc( sizeof( char * )*(size_t)nax );
+ for( i = 0; i < nax; i++ ) anames[ i ] = NULL;
+
+/* Initialise the index of the next Frame axis to use. */
+ axis = 0;
+
+/* Check to see if the next 2 axes describe positions on the sky or earth
+ (see SpaceFrameReader). */
+ axes[ 0 ] = 0;
+ axes[ 1 ] = 1;
+ afrm = astPickAxes( frm, 2, axes, NULL );
+ dom = astGetDomain( afrm );
+ isearth = dom && ( !strcmp( dom, "GEO_D" ) ||
+ !strcmp( dom, "GEO_C" ) );
+
+ if( isearth || ( dom && !strcmp( dom, "SKY" ) ) ){
+ astPrimaryFrame( frm, axis, &pfrm, &junk );
+ if( scan->count[ 0 ] ) {
+
+/* We currently also use SkyFrames to represent geographical long/lat used to
+ describe observatory positions. These may have 3D positions, in which
+ case we convert the 3D position to a 2D position by ignoring the 3rd axis
+ value (height). See SpaceFrameReader. */
+ el = MakePos2D( this, scan->el[ 0 ][ 0 ], status );
+
+/* Use the Position2D to create a Region describing the uncertainty in
+ the space axes of the Frame. Also create a KeyMap holding Regions
+ describing any ancillary information stored in the Position2D. */
+ uncs[ 0 ] = Position2DReader( this, el, pfrm, pos, &panc, status );
+ if( uncs[ 0 ] ) unc = 1;
+ el = astXmlDelete( el );
+
+/* If ancillary information was returned, extract the Name element, and
+ store it twice (once for each axis) in the "names" array. */
+ if( panc && astMapGet0C( panc, AST__STCNAME, &nam ) ) {
+ anames[ axis ] = astStore( NULL, nam, strlen( nam ) + 1 );
+ anames[ axis + 1 ] = astStore( NULL, nam, strlen( nam ) + 1 );
+ }
+ }
+
+/* Increment the axis index. */
+ axis += 2;
+
+/* If the supplied Frame has no sky frame, but we found a Position2D, then
+ report a warning and ignore the Position2D. */
+ } else if( scan->count[ 0 ] ) {
+ sprintf( buff, "contains a <%s> which is not being used.",
+ astXmlGetName( scan->el[ 0 ][ 0 ] ) );
+ Report( this, elem, WARNING, buff, status );
+ }
+ afrm = astAnnul( afrm );
+
+/* Indicate we do not yet have an epoch to use. */
+ epoch = AST__BAD;
+
+/* Check to see if the Frame contains a time frame. It will be the next
+ axis if it does. */
+ afrm = astPickAxes( frm, 1, &axis, NULL );
+ dom = astGetDomain( afrm );
+ if( dom && !strcmp( dom, "TIME" ) ){
+ astPrimaryFrame( frm, axis, &gfrm, &junk );
+
+/* Report an error if it is not an AST TimeFrame. */
+ if( !astIsATimeFrame( gfrm ) && astOK ) {
+ astError( AST__INTER, "AstroCoordAreaReader(XmlChan): %s "
+ "supplied where TimeFrame expected (internal "
+ "AST programming error).", status, astGetClass( pfrm ) );
+ } else {
+ tfrm = (AstTimeFrame *) gfrm;
+ }
+
+/* Use any Time element to create a Region describing the uncertainty in the
+ time axis of the Frame. Also create a KeyMap holding Regions describing
+ any ancillary information stored in the Time element. */
+ if( scan->count[ 1 ] ) {
+ uncs[ 1 ] = TimeReader( this, scan->el[ 1 ][ 0 ], tfrm, &epoch,
+ &tanc, status );
+ if( uncs[ 1 ] ) unc = 1;
+
+/* If ancillary information was returned, extract the Name element, and
+ store it in the "names" array. */
+ if( tanc && astMapGet0C( tanc, AST__STCNAME, &nam ) ) {
+ anames[ axis ] = astStore( NULL, nam, strlen( nam ) + 1 );
+ }
+ }
+
+/* Increment the index of the next axis to use. */
+ axis++;
+
+/* If the supplied Frame has no time frame, but we found a Time element, then
+ report a warning and ignore the Time element. */
+ } else if( scan->count[ 1 ] ) {
+ Report( this, elem, WARNING, "contains a <Time> which is not needed", status );
+ }
+ afrm = astAnnul( afrm );
+
+/* Indicate we do not yet have a rest frequency to use with any redshift
+ axis. */
+ rf = AST__BAD;
+
+/* Check to see if the Frame contains a spectral frame. It will be the next
+ axis if it does. */
+ afrm = astPickAxes( frm, 1, &axis, NULL );
+ dom = astGetDomain( afrm );
+ if( dom && !strcmp( dom, "SPECTRUM" ) ){
+ astPrimaryFrame( frm, axis, &sfrm, &junk );
+
+/* Use any Spectral to create a Region describing the uncertainty in the
+ spectral axis of the Frame. If the Spectral contains a spectral value, the
+ first value will be returned so that it can be used as the rest frequency
+ for any Redshift axis. It will be in units of Hz and will be AST__BAD if
+ the Spectral did not contain any spectral values. Also create a KeyMap
+ holding Regions describing any ancillary information stored in the
+ Spectral element. */
+ if( scan->count[ 2 ] ) {
+ uncs[ 2 ] = SpectralReader( this, scan->el[ 2 ][ 0 ], sfrm, &rf,
+ &sanc, status );
+ if( uncs[ 2 ] ) unc = 1;
+
+/* If ancillary information was returned, extract the Name element, and
+ store it in the "names" array. */
+ if( sanc && astMapGet0C( sanc, AST__STCNAME, &nam ) ) {
+ anames[ axis ] = astStore( NULL, nam, strlen( nam ) + 1 );
+ }
+ }
+
+/* Increment the index of the next axis to use. */
+ axis++;
+
+/* If the supplied Frame has no spectral frame, but we found a Spectral
+ element, then report a warning and ignore the Spectral element. */
+ } else if( scan->count[ 2 ] ) {
+ Report( this, elem, WARNING, "contains a <Spectral> which is not needed", status );
+ }
+ afrm = astAnnul( afrm );
+
+/* Check to see if the Frame contains a redshift frame. It will be the next
+ axis if it does. */
+ afrm = astPickAxes( frm, 1, &axis, NULL );
+ dom = astGetDomain( afrm );
+ if( dom && !strcmp( dom, "REDSHIFT" ) ){
+ astPrimaryFrame( frm, axis, &rfrm, &junk );
+
+/* Use any Redshift to create a Region describing the uncertainty in the
+ redshift axis of the Frame. Also create a KeyMap holding Regions describing
+ any ancillary information stored in the Redshift element. */
+ if( scan->count[ 3 ] ) {
+ uncs[ 3 ] = RedshiftReader( this, scan->el[ 3 ][ 0 ], rfrm,
+ &ranc, status );
+ if( uncs[ 3 ] ) unc = 1;
+
+/* If ancillary information was returned, extract the Name element, and
+ store it in the "names" array. */
+ if( ranc && astMapGet0C( ranc, AST__STCNAME, &nam ) ) {
+ anames[ axis ] = astStore( NULL, nam, strlen( nam ) + 1 );
+ }
+ }
+
+/* Increment the index of the next axis to use. */
+ axis++;
+
+/* If the supplied Frame has no redshift frame, but we found a Redshift
+ element, then report a warning and ignore the Redshift element. */
+ } else if( scan->count[ 3 ] ) {
+ Report( this, elem, WARNING, "contains a <Redshift> which is not needed", status );
+ }
+ afrm = astAnnul( afrm );
+
+/* Now assign fixed axis values (Epoch, RestFreq, etc) to the component
+ Frames of the supplied Frame. */
+ if( epoch != AST__BAD ) {
+ if( pfrm ) astSetEpoch( pfrm, epoch );
+ if( tfrm ) astSetEpoch( tfrm, epoch );
+ if( sfrm ) astSetEpoch( sfrm, epoch );
+ if( rfrm ) astSetEpoch( rfrm, epoch );
+ astSetEpoch( frm, epoch );
+ }
+
+ if( sfrm && pfrm && astIsASpecFrame( sfrm ) && astIsASkyFrame( pfrm ) &&
+ !isearth && pos[ 0 ] != AST__BAD && pos[ 1 ] != AST__BAD ) {
+ astSetRefPos( sfrm, pfrm, pos[ 0 ], pos[ 1 ] );
+ }
+
+ if( rfrm && astIsASpecFrame( rfrm ) && rf != AST__BAD ) {
+ astSetRestFreq( rfrm, rf );
+ if( pfrm && astIsASkyFrame( pfrm ) && !isearth &&
+ pos[ 0 ] != AST__BAD && pos[ 1 ] != AST__BAD ) {
+ astSetRefPos( rfrm, pfrm, pos[ 0 ], pos[ 1 ] );
+ }
+ }
+
+/* Now combine ancillary data for each component Frame into the total
+ Frame. */
+ *anc = astKeyMap( "", status );
+ if( *anc ) {
+ empty = 1;
+
+/* Store the Names element if at least one axis has a Name item. */
+ for( i = 0; i < nax; i++ ) {
+ if( !anames[ i ] ) anames[ i ] = astStore( NULL, "", 1 );
+ }
+
+ for( i = 0; i < nax; i++ ) {
+ if( empty && strlen( anames[ i ] ) > 0 ) {
+ astMapPut1C( *anc, AST__STCNAME, nax, (const char **) anames, NULL );
+ empty = 0;
+ }
+ anames[ i ] = astFree( anames[ i ] );
+ }
+
+/* Do each of the other items, all of which are described by a Region. */
+ lo = 0.0;
+ hi = 0.0;
+ for( i = 0; i < 5; i++ ) {
+
+/* Initialise a flag indicating that we have not yet found any non-null
+ information to store for this item. */
+ use = 0;
+
+/* Initialise a pointer to the Region describing the item extruded into
+ all axes. */
+ t = NULL;
+
+/* If there is a positional Frame, determine the Region describing the
+ intersection of the total Region with the position Frame. If none is
+ supplied use a zero width Interval as a flag that no information is
+ available. */
+ if( pfrm ) {
+ if( panc && astMapGet0A( panc, key[ i ], &o ) ) {
+ t = (AstRegion *) o;
+ use = 1;
+ } else {
+ t = (AstRegion *) astInterval( pfrm, &lo, &hi, NULL, "", status );
+ }
+ }
+
+/* If there is a time Frame, determine the Region describing the intersection
+ of the total Region with the time Frame. If none is supplied use a zero
+ width Interval as a flag that no information is available. */
+ if( tfrm ) {
+ if( tanc && astMapGet0A( tanc, key[ i ], &o ) ) {
+ r = (AstRegion *) o;
+ use = 1;
+ } else {
+ r = (AstRegion *) astInterval( tfrm, &lo, &hi, NULL, "", status );
+ }
+
+/* If there were earlier axes, extrude the current total region into the
+ time axis, and use the extruded region as the new total region.*/
+ if( t ) {
+ tt = (AstRegion *) astPrism( t, r, "", status );
+ r = astAnnul( r );
+ (void) astAnnul( t );
+ t = tt;
+
+/* If this is the first axis, use the region determined for this axis as
+ the total Region.*/
+ } else {
+ t = r;
+ }
+ }
+
+/* Do the same for any spectral axis. */
+ if( sfrm ) {
+ if( sanc && astMapGet0A( sanc, key[ i ], &o ) ) {
+ r = (AstRegion *) o;
+ use = 1;
+ } else {
+ r = (AstRegion *) astInterval( sfrm, &lo, &hi, NULL, "", status );
+ }
+
+ if( t ) {
+ tt = (AstRegion *) astPrism( t, r, "", status );
+ r = astAnnul( r );
+ (void) astAnnul( t );
+ t = tt;
+ } else {
+ t = r;
+ }
+
+ }
+
+/* Do the same for any redshift axis. */
+ if( rfrm ) {
+ if( ranc && astMapGet0A( ranc, key[ i ], &o ) ) {
+ r = (AstRegion *) o;
+ use = 1;
+ } else {
+ r = (AstRegion *) astInterval( rfrm, &lo, &hi, NULL, "", status );
+ }
+
+ if( t ) {
+ tt = (AstRegion *) astPrism( t, r, "", status );
+ r = astAnnul( r );
+ (void) astAnnul( t );
+ t = tt;
+ } else {
+ t = r;
+ }
+ }
+
+/* If there is some non-null information for this item, replace the
+ stored Frame with the Frame which has set Epoch/RefLat/etc, simplify the
+ total Region and store it in the returned KeyMap. */
+ if( use ) {
+ astSetRegFS( t, frm );
+ tt = astSimplify( t );
+ astMapPut0A( *anc, key[ i ], tt, NULL );
+ tt = astAnnul( tt );
+ empty = 0;
+ }
+ if( t ) t = astAnnul( t );
+ }
+
+/* Return a NULL KeyMap pointer if the KeyMap is empty. */
+ if( empty ) *anc = astAnnul( *anc );
+ }
+
+/* Free resources. */
+ if( panc ) panc = astAnnul( panc );
+ if( tanc ) tanc = astAnnul( tanc );
+ if( sanc ) sanc = astAnnul( sanc );
+ if( ranc ) ranc = astAnnul( ranc );
+ if( pfrm ) pfrm = astAnnul( pfrm );
+ if( tfrm ) tfrm = astAnnul( tfrm );
+ if( sfrm ) sfrm = astAnnul( sfrm );
+ if( rfrm ) rfrm = astAnnul( rfrm );
+ scan = FreeIVOAScan( scan, status );
+ anames = astFree( anames );
+ }
+
+/* Annull any returned Regions if an error occurred.*/
+ if( !astOK ) {
+ uncs[ 0 ] = astAnnul( uncs[ 0 ] );
+ uncs[ 1 ] = astAnnul( uncs[ 1 ] );
+ uncs[ 2 ] = astAnnul( uncs[ 2 ] );
+ uncs[ 3 ] = astAnnul( uncs[ 3 ] );
+ unc = 0;
+ *anc = astAnnul( *anc );
+ }
+
+/* Return the result. */
+ return unc;
+}
+
+static AstObject *AstroCoordSystemReader( AstXmlChan *this,
+ AstXmlElement *elem, int *status ) {
+/*
+* Name:
+* AstroCoordSystemReader
+
+* Purpose:
+* Make an AST Object from an IVOA AstroCoordSystem element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstObject *AstroCoordSystemReader( AstXmlChan *this,
+* AstXmlElement *elem, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Object from the supplied IVOA
+* AstroCoordSystem element. This will be a Frame of some kind.
+* If the AstroCoordSystem element contains only one sub-frame
+* element, then the returned Frame will be of a suitable class
+* to describe that sub-frame (SkyFrame, SpecFrame or TimeFrame).
+* If the AstroCoordSystem element contains more than one sub-frame
+* element, then the returned Frame will be a CmpFrame in which the
+* component Frames are in the order SpaceFrame, TimeFrame,
+* SpectralFrame, RedshiftFrame.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA AstroCoordSystem element.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Object.
+
+* Notes:
+* - GenericCoordFrame sub-elements are currently ignored since it is not
+* clear how they relate to the other sub-elements.
+
+*/
+
+/* Local Variables: */
+ AstCmpFrame *tmp; /* Pointer to intermediate CmpFrame */
+ AstFrame *comp[ 4 ]; /* Pointers to component Frames */
+ AstObject *new; /* Pointer to returned Object */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *id; /* Pointer to ID attribute value */
+ const char *names[4]; /* Names of the subelements to be searched for */
+ int i; /* Index of current content item */
+ int j; /* Index to store Frame pointer at */
+ int max[4]; /* Max allowed occurrences of each name */
+ int min[4]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = SPACE_FRAME;
+ names[ 1 ] = TIME_FRAME;
+ names[ 2 ] = SPECTRAL_FRAME;
+ names[ 3 ] = REDSHIFT_FRAME;
+ min[ 0 ] = 0;
+ min[ 1 ] = 0;
+ min[ 2 ] = 0;
+ min[ 3 ] = 0;
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ max[ 2 ] = 1;
+ max[ 3 ] = 1;
+ scan = ScanIVOAElement( this, elem, 4, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Create Frames from the found sub-elements */
+ comp[ 0 ] = scan->count[0] ? (AstFrame *) SpaceFrameReader( this,
+ scan->el[ 0 ][ 0 ], status ) : NULL;
+ comp[ 1 ] = scan->count[1] ? (AstFrame *) TimeFrameReader( this,
+ scan->el[ 1 ][ 0 ], status ) : NULL;
+ comp[ 2 ] = scan->count[2] ? (AstFrame *) SpectralFrameReader( this,
+ scan->el[ 2 ][ 0 ], status ) : NULL;
+ comp[ 3 ] = scan->count[3] ? (AstFrame *) RedshiftFrameReader( this,
+ scan->el[ 3 ][ 0 ], status ) : NULL;
+
+/* If more than one frame was obtained combine them into a CmpFrame. If
+ present, the Frames are stored in the order SpaceFrame, TimeFrame,
+ SpectralFrame, RedshiftFrame. Shuffle the the higher elements of the
+ "comp" array down to fill any NULL elements. */
+ j = 0;
+ for( i = 0; i < 4; i++ ) {
+ if( comp[ i ] ) {
+ comp[ j++ ] = comp[ i ];
+ }
+ }
+
+/* Fill any unused elements at the end with NULL. */
+ for( ; j < 4; j++ ) comp[ j ] = NULL;
+
+/* If no Frames were read issue a fatal error. */
+ if( !comp[ 0 ] ) {
+ Report( this, elem, FAILURE, "contains no usable coordinate axes", status );
+
+/* If only one Frame was read return a clone of its pointer. */
+ } else if( !comp[ 1 ] ) {
+ new = astClone( comp[ 0 ] );
+
+/* If two or more Frames were read, create a CmpFrame holding the Frames. */
+ } else if( !comp[ 2 ] ) {
+ new = (AstObject *) astCmpFrame( comp[ 0 ], comp[ 1 ], "", status );
+
+ } else if( !comp[ 3 ] ) {
+ tmp = astCmpFrame( comp[ 0 ], comp[ 1 ], "", status );
+ new = (AstObject *) astCmpFrame( tmp, comp[ 2 ], "", status );
+ tmp = astAnnul( tmp );
+
+ } else {
+ tmp = astCmpFrame( comp[ 0 ], comp[ 1 ], "", status );
+ (void) astAnnul( comp[ 0 ] );
+ comp[ 0 ] = (AstFrame *) tmp;
+ tmp = astCmpFrame( comp[ 0 ], comp[ 2 ], "", status );
+ new = (AstObject *) astCmpFrame( tmp, comp[ 3 ], "", status );
+ tmp = astAnnul( tmp );
+ }
+
+/* Get the ID attribute from the AstroCoordSystem element and store in the
+ returned Frame. */
+ id = astXmlGetAttributeValue( elem, "ID" );
+ if( id ) astSetIdent( new, id );
+
+/* Free resources */
+ for( i = 0; i < 4; i++ ) {
+ if( comp[ i ] ) comp[ i ] = astAnnul( comp[ i ] );
+ }
+ scan = FreeIVOAScan( scan, status );
+
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Object. */
+ return new;
+}
+
+static double AstronTimeReader( AstXmlChan *this, AstXmlElement *elem,
+ AstTimeFrame *frm, int *status ){
+/*
+* Name:
+* AstronTimeReader
+
+* Purpose:
+* Read a time value from an IVOA AstronTime element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* double AstronTimeReader( AstXmlChan *this, AstXmlElement *elem,
+* AstTimeFrame *frm )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function returns a double representing the time specified by
+* the supplied IVOA AstronTime element, converted into the system
+* represented by the supplied Frame.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA AstronTime element.
+* frm
+* Pointer to the TimeFrame in which the returned value should be
+* defined. Relevant attributes which are not set will be set by
+* this function if possible.
+
+* Returned Value:
+* The time value, in the system described by "frm".
+
+*/
+
+/* Local Variables: */
+ AstFrameSet *fs; /* FrameSet connecting two TimeFrames */
+ AstTimeFrame *cfrm; /* TimeFrame describing XML time system */
+ AstTimeScaleType ts; /* TimeScale */
+ IVOAScan *scan; /* Structure holding scan results */
+ char buff[ 200 ]; /* Message buffer */
+ const char *iso; /* Pointer to ISO date string */
+ const char *names[3]; /* Names of the subelements to be searched for */
+ const char *time_type; /* Pointer to time type string */
+ const char *unit; /* Pointer to Unit string */
+ double fval; /* Value converted to supplied TimeFrame */
+ double offset; /* Time offset */
+ double result; /* Time offset converted to required TimeFrame */
+ double val; /* Value read from element */
+ int max[3]; /* Max allowed occurrences of each name */
+ int min[3]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ offset = 0.0;
+ result = AST__BAD;
+ val = AST__BAD;
+
+/* Check the global error status. */
+ if ( !astOK ) return offset;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "JDTime|MJDTime|ISOTime";
+ names[ 1 ] = "TimeOffset";
+ names[ 2 ] = "TimeScale|Timescale";
+ min[ 0 ] = 1;
+ min[ 1 ] = 0;
+ min[ 2 ] = 0;
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ max[ 2 ] = 1;
+ scan = ScanIVOAElement( this, elem, 3, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* The supplied TimeFrame describes the system in which the caller wants
+ the time values to be returned. This may not be the same as the system
+ in which the value is stored in the XML. We create a TimeFrame
+ describing the XML system, and later transform time values from the XML
+ system to the system required by the caller. Any attributes of the XML
+ system which are not specified in the XML are assumed to be equal to
+ the values of the corresponding attributes in the supplied TimeFrame.
+ If the XML system specifies values for attributes which have not been
+ set in the supplied TimeFrame, then the values read fomr the XML are
+ assigned to the attributes of the supplied TimeFrame. */
+ cfrm = astCopy( frm );
+
+ if( scan->count[2] ) {
+ ts = TimeScaleReader( this, scan->el[2][0], status );
+ astSetTimeScale( cfrm, ts );
+ if( !astTestTimeScale( frm ) ) astSetTimeScale( frm, ts );
+ }
+
+/* If a JDTime element was found, get its value and set the TimeFrame System
+ values. */
+ time_type = astXmlGetName( scan->el[0][0] );
+ if( !strcmp( "JDTime", time_type ) ) {
+ val = ElemValueD( this, scan->el[0][0], 2400000.5, status );
+ astSetSystem( cfrm, AST__JD );
+ if( !astTestSystem( frm ) ) astSetSystem( frm, AST__JD );
+
+/* If a ISOTime element was found, get its value and set the TimeFrame
+ System attribute to MJD (the choice of AST System for an ISOTime is
+ arbitrary - JD or JEPOCH could also have been used). */
+ } else if( !strcmp( "ISOTime", time_type ) ) {
+ astSetSystem( cfrm, AST__MJD );
+ if( !astTestSystem( frm ) ) astSetSystem( frm, AST__MJD );
+ iso = astXmlGetValue( scan->el[0][0], 0 );
+ astClearTimeOrigin( cfrm );
+ if( iso && astUnformat( cfrm, 0, iso, &val ) != strlen( iso ) ) {
+ sprintf( buff, "contains unsupported ISO time format \"%s\"",
+ iso );
+ Report( this, elem, FAILURE, buff, status );
+ }
+
+/* If an MJDTime was found, get its value and set System attributes. */
+ } else {
+ val = ElemValueD( this, scan->el[0][0], 2400000.5, status );
+ astSetSystem( cfrm, AST__MJD );
+ if( !astTestSystem( frm ) ) astSetSystem( frm, AST__MJD );
+ }
+
+/* Use this value as the TimeFrame's TimeOrigin value. Use the public
+ astSetD rather than astSetTimeOrigin since the later requires the
+ value to be supplied in the default units for the TimeFrame's System. */
+ astSetD( cfrm, "TimeOrigin", val );
+
+/* If the supplied Frame has no set TimeOrigin, also use the value
+ obtained above as the TimeOrigin in "frm". Convert it into the supplied
+ TimeFrame, and set it. Note zero is used as the axis value in cfrm
+ because the relevant epoch is zero distance away from the cfrm
+ TimeOrigin (set above). */
+ if( !astTestTimeOrigin( frm ) ) {
+
+ fs = astConvert( cfrm, frm, "" );
+ if( fs ){
+ val = 0.0;
+ astTran1( fs, 1, &val, 1, &fval );
+ astSetD( frm, "TimeOrigin", fval );
+ fs = astAnnul( fs );
+ } else if( astOK ) {
+ sprintf( buff, "contains inconsistent timescale (%s)",
+ astGetC( cfrm, "timescale" ) );
+ Report( this, elem, FAILURE, buff, status );
+ }
+ }
+
+/* If an TimeOffset element was found, get its value and the value of its
+ unit attribute (assume a default of days). Set the units in the
+ TimeFrames. */
+ if( scan->count[1] ) {
+ offset = ElemValueD( this, scan->el[1][0], 0.0, status );
+ unit = astXmlGetAttributeValue( scan->el[1][0], "unit" );
+ if( !unit ) unit = "d";
+ astSetUnit( cfrm, 0, unit );
+ if( !astTestUnit( frm, 0 ) ) astSetUnit( frm, 0, unit );
+
+/* If no offset was given, use zero. */
+ } else {
+ offset = 0.0;
+ }
+
+/* Convert the offset from the system in which it is stored in the XML to
+ the system required by the caller. */
+ fs = astConvert( cfrm, frm, "" );
+ if( fs ){
+ astTran1( fs, 1, &offset, 1, &result );
+ fs = astAnnul( fs );
+ } else if( astOK ) {
+ sprintf( buff, "contains inconsistent timescale (%s)",
+ astGetC( cfrm, "timescale" ) );
+ Report( this, elem, FAILURE, buff, status );
+ }
+
+/* Free resources. */
+ cfrm = astAnnul( cfrm );
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Return the time value. */
+ return result;
+}
+
+void astInitXmlChanVtab_( AstXmlChanVtab *vtab, const char *name, int *status ) {
+/*
+*+
+* Name:
+* astInitXmlChanVtab
+
+* Purpose:
+* Initialise a virtual function table for an XmlChan.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* void astInitXmlChanVtab( AstXmlChanVtab *vtab, const char *name )
+
+* Class Membership:
+* XmlChan vtab initialiser.
+
+* Description:
+* This function initialises the component of a virtual function
+* table which is used by the XmlChan 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 astIsAXmlChan) 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. */
+/* ------------------------------------ */
+/* Store pointers to the member functions (implemented here) that provide
+ virtual methods for this class. */
+
+ vtab->SetXmlLength = SetXmlLength;
+ vtab->ClearXmlLength = ClearXmlLength;
+ vtab->TestXmlLength = TestXmlLength;
+ vtab->GetXmlLength = GetXmlLength;
+
+ vtab->SetXmlFormat = SetXmlFormat;
+ vtab->ClearXmlFormat = ClearXmlFormat;
+ vtab->TestXmlFormat = TestXmlFormat;
+ vtab->GetXmlFormat = GetXmlFormat;
+
+ vtab->SetXmlPrefix = SetXmlPrefix;
+ vtab->ClearXmlPrefix = ClearXmlPrefix;
+ vtab->TestXmlPrefix = TestXmlPrefix;
+ vtab->GetXmlPrefix = GetXmlPrefix;
+
+/* 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;
+
+ channel->WriteBegin = WriteBegin;
+ channel->WriteIsA = WriteIsA;
+ channel->WriteEnd = WriteEnd;
+ channel->WriteInt = WriteInt;
+ channel->WriteDouble = WriteDouble;
+ channel->WriteString = WriteString;
+ channel->WriteObject = WriteObject;
+
+ channel->Read = Read;
+ channel->ReadClassData = ReadClassData;
+ channel->ReadDouble = ReadDouble;
+ channel->ReadInt = ReadInt;
+ channel->ReadObject = ReadObject;
+ channel->ReadString = ReadString;
+
+ parent_getindent = channel->GetIndent;
+ channel->GetIndent = GetIndent;
+
+ parent_getfull = channel->GetFull;
+ channel->GetFull = GetFull;
+
+ parent_getcomment = channel->GetComment;
+ channel->GetComment = GetComment;
+
+
+/* Save the inherited pointers to methods that will be extended, and
+ replace them with pointers to the new member functions. */
+ 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;
+
+/* Declare the class dump, copy and delete functions.*/
+ astSetCopy( vtab, Copy );
+ astSetDump( vtab, Dump, "XmlChan", "XML I/O channel" );
+ astSetDelete( vtab, Delete );
+
+/* 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 double AttrValueD( AstXmlChan *this, AstXmlElement *elem,
+ const char *name, double def, int *status ) {
+/*
+* Name:
+* AttrValueD
+
+* Purpose:
+* Read a floating point XML element attribute value.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* double AttrValueD( AstXmlChan *this, AstXmlElement *elem,
+* const char *name, double def, int *status )
+
+* Class Membership:
+* XmlChan member function
+
+* Description:
+* This function returns the value of a named attribute of an XML
+* element as a floating point value. A report is made if the
+* attribute value is not floating point.The supplied default value is
+* returned if the attribute is not present.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the XmlElement.
+* name
+* Pointer to a constant null-terminated character string
+* containing the name of the required attribute value.
+* def
+* If the supplied element does not have the requried attribute, then
+* this value will be returned instead.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The required attribute value, or the default if the value was not found.
+
+*/
+
+/* Local Variables: */
+ char buff[ 200 ]; /* Msg buffer */
+ const char *value; /* Pointer to attribute value */
+ double result; /* Value to be returned */
+ int nc; /* Number of characters read by astSscanf */
+ int nf; /* Number of matching fields */
+ int len; /* Length of attribute string */
+
+/* Initialise. */
+ result = def;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Get the attribute value as a string. */
+ value = astXmlGetAttributeValue( elem, name );
+
+/* If the attribute exists, attempt to decode the string to give a double
+ value, checking that the entire string is read. */
+ if( value ) {
+ nc = 0;
+ nf = astSscanf( value, " %lf %n", &result, &nc );
+ len = strlen( value );
+
+ if ( nf != 1 || nc < len ) {
+ sprintf( buff, "contains a bad <%s> value: \"%s\"", name, value );
+ Report( this, elem, WARNING, buff, status );
+ }
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static int AttrValueI( AstXmlChan *this, AstXmlElement *elem, const char *name,
+ int def, int *status ) {
+/*
+* Name:
+* AttrValueI
+
+* Purpose:
+* Read an integer XML element attribute value.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* int AttrValueI( AstXmlChan *this, AstXmlElement *elem, const char *name,
+* int def )
+
+* Class Membership:
+* XmlChan member function
+
+* Description:
+* This function returns the value of a named attribute of an XML element
+* as an integer value. A report is made if the attribute value is not
+* integer. The supplied default value is returned if the attribute is not
+* present.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the XmlElement.
+* name
+* Pointer to a constant null-terminated character string
+* containing the name of the required attribute value.
+* def
+* If the supplied element does not have the requried attribute, then
+* this value will be returned instead.
+
+* Returned Value:
+* The required attribute value, or the default if the value was not found.
+
+*/
+
+/* Local Variables: */
+ char buff[ 200 ]; /* Msg buffer */
+ const char *value; /* Pointer to attribute value */
+ int result; /* Value to be returned */
+ int nc; /* Number of characters read by astSscanf */
+ int nf; /* Number of matching fields */
+ int len; /* Length of attribute string */
+
+/* Initialise. */
+ result = def;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Get the attribute value as a string. */
+ value = astXmlGetAttributeValue( elem, name );
+
+/* If the attribute exists, attempt to decode the string to give an integer
+ value, checking that the entire string is read. */
+ if( value ) {
+ nc = 0;
+ nf = astSscanf( value, " %d %n", &result, &nc );
+ len = strlen( value );
+
+ if ( nf != 1 || nc < len ) {
+ sprintf( buff, "contains a bad <%s> value: \"%s\"", name, value );
+ Report( this, elem, WARNING, buff, status );
+ }
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static int AttrValueB( AstXmlChan *this, AstXmlElement *elem, const char *name,
+ int def, int *status ) {
+/*
+* Name:
+* AttrValueB
+
+* Purpose:
+* Read a boolean XML element attribute value.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* int AttrValueB( AstXmlChan *this, AstXmlElement *elem, const char *name,
+* int def, int *status )
+
+* Class Membership:
+* XmlChan member function
+
+* Description:
+* This function returns the value of a named attribute of an XML element
+* as a boolean. A report is made if the attribute value is not
+* boolean. The supplied default value is returned if the attribute is not
+* present.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the XmlElement.
+* name
+* Pointer to a constant null-terminated character string
+* containing the name of the required attribute value.
+* def
+* If the supplied element does not have the requried attribute, then
+* this value will be returned instead.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The required attribute value, or the default if the value was not found.
+
+*/
+
+/* Local Variables: */
+ char buff[ 200 ]; /* Msg buffer */
+ const char *value; /* Pointer to attribute value */
+ int result; /* Value to be returned */
+ int i; /* Loop count */
+
+/* Define the recognised true and false strings. */
+ const char *true[ 5 ] = { "true", "TRUE", "yes", "YES", "1" };
+ const char *false[ 5 ] = { "false", "FALSE", "no", "NO", "0" };
+
+/* Initialise. */
+ result = def;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Get the attribute value as a string. */
+ value = astXmlGetAttributeValue( elem, name );
+
+/* If the attribute exists, attempt to decode the string to give a boolean
+ value. */
+ if( value ) {
+
+/* Indicate the result has not yet been determined. */
+ result = -1;
+
+/* See if the attribute value is equal to (or an abbreviation of) any of
+ the true strings. */
+ for( i = 0; i < 5; i++ ) {
+ if( strstr( true[ i ], value ) == true[ i ] ) {
+ result = 1;
+ break;
+ }
+ }
+
+/* If not, see if the attribute value is equal to (or an abbreviation of) any
+ of the false strings. */
+ if( result == -1 ) {
+ for( i = 0; i < 5; i++ ) {
+ if( strstr( false[ i ], value ) == false[ i ] ) {
+ result = 0;
+ break;
+ }
+ }
+ }
+
+/* If not, report a warning and return the default. */
+ if( result == -1 ) {
+ result = def;
+ sprintf( buff, "contains a bad <%s> value: \"%s\"", name, value );
+ Report( this, elem, WARNING, buff, status );
+ }
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static AstRegion *BoxReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, int *status ){
+/*
+* Name:
+* BoxReader
+
+* Purpose:
+* Make an AST Region from an IVOA Box element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *BoxReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* Box element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Box element.
+* frm
+* Pointer to the 2D Frame in which the returned Region should be
+* defined. If the Unit attribute is not set, this function will
+* set it to the value supplied in "unit" before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstFrame *cfrm; /* Frame used to define returned Region */
+ AstMapping *map; /* Mapping between units */
+ AstRegion *new; /* Pointer to returned Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *funit; /* Unit string from supplied Frame */
+ const char *names[2]; /* Names of the subelements to be searched for */
+ const char *unit; /* Centre and radii unit string */
+ double cen[2]; /* Centre */
+ double size[2]; /* Axis sizes */
+ double pos[8]; /* Polygon vertex axis values */
+ double *x; /* Pointer to first vertex X axis value */
+ double *y; /* Pointer to first vertex Y axis value */
+ int i; /* Axis count */
+ int max[2]; /* Max allowed occurrences of each name */
+ int min[2]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Scan the supplied element for the required sub-elements */
+ names[ 0 ] = "Center";
+ names[ 1 ] = "Size";
+ min[ 0 ] = 1;
+ min[ 1 ] = 1;
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ scan = ScanIVOAElement( this, elem, 2, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the centre. */
+ cen[0] = 0.0;
+ cen[1] = 0.0;
+ ElemListD( this, scan->el[0][0], 2, cen, status );
+
+/* Get the size. */
+ size[0] = 0.0;
+ size[1] = 0.0;
+ ElemListD( this, scan->el[1][0], 2, size, status );
+
+/* Get the units attribute from the supplied element. These are the units
+ of the centre and size values. */
+ unit = astXmlGetAttributeValue( elem, "unit" );
+ if( !unit ) {
+ Report( this, elem, FAILURE, "contains no unit attribute", status );
+ unit = "";
+ }
+
+/* Since the SkyFrame class does not have active Units we must handle it
+ separately. */
+ if( astIsASkyFrame( frm ) ) {
+
+/* Create the anti-clockwise list of (x,y) at the four corners of the box. */
+ x = pos;
+ y = pos+ 4;
+ x[ 3 ] = cen[ 0 ] + 0.5*size[ 0 ];
+ y[ 3 ] = cen[ 1 ] - 0.5*size[ 1 ];
+ x[ 2 ] = cen[ 0 ] + 0.5*size[ 0 ];
+ y[ 2 ] = cen[ 1 ] + 0.5*size[ 1 ];
+ x[ 1 ] = cen[ 0 ] - 0.5*size[ 0 ];
+ y[ 1 ] = cen[ 1 ] + 0.5*size[ 1 ];
+ x[ 0 ] = cen[ 0 ] - 0.5*size[ 0 ];
+ y[ 0 ] = cen[ 1 ] - 0.5*size[ 1 ];
+
+/* Convert the axis values to radians. */
+ map = astUnitMapper( unit, "rad", NULL, NULL );
+ if( map ) {
+ astTran1( map, 8, pos, 1, pos );
+ map = astAnnul( map );
+ } else if( astOK ) {
+ Report( this, elem, FAILURE, "contains unusable units", status );
+ }
+
+/* Create the Polygon. */
+ new = (AstRegion *) astPolygon( frm, 4, 4, pos, NULL, "", status );
+
+/* Now handles Frames other than SkyFrames. */
+ } else {
+
+/* Create the anti-clockwise list of (x,y) at the four corners of the box. */
+ x = pos;
+ y = pos+ 4;
+ x[ 0 ] = cen[ 0 ] + 0.5*size[ 0 ];
+ y[ 0 ] = cen[ 1 ] - 0.5*size[ 1 ];
+ x[ 1 ] = cen[ 0 ] + 0.5*size[ 0 ];
+ y[ 1 ] = cen[ 1 ] + 0.5*size[ 1 ];
+ x[ 2 ] = cen[ 0 ] - 0.5*size[ 0 ];
+ y[ 2 ] = cen[ 1 ] + 0.5*size[ 1 ];
+ x[ 3 ] = cen[ 0 ] - 0.5*size[ 0 ];
+ y[ 3 ] = cen[ 1 ] - 0.5*size[ 1 ];
+
+/* Take a copy of the supplied Frame and set its Units to the value
+ obtained from the supplied element. */
+ cfrm = astCopy( frm );
+ astSetUnit( cfrm, 0, unit );
+ astSetUnit( cfrm, 1, unit );
+
+/* Create a Polygon within this modified Frame. */
+ new = (AstRegion *) astPolygon( frm, 4, 4, pos, NULL, "", status );
+
+/* If the Unit of this Region differs from that of the supplied Frame,
+ set it to the Unit of the supplied Frame. This will cause the
+ encapsulated limits to be mapped into the new Unit. If the supplied
+ Frame had no set Unit, set it to the units obtained from the supplied
+ element. */
+ for( i = 0; i < 2; i++ ) {
+ if( astTestUnit( frm, i ) ) {
+ funit = astGetUnit( frm, i );
+ if( strcmp( funit, unit ) ) astSetUnit( new, i, funit );
+ } else {
+ astSetUnit( frm, i, unit );
+ }
+ }
+
+/* Free resources */
+ cfrm = astAnnul( cfrm );
+ }
+
+/* Get any fill factor and lo/hi_include attributes from the element and
+ assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Free resources */
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static AstRegion *CircleReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, int *status ){
+/*
+* Name:
+* CircleReader
+
+* Purpose:
+* Make an AST Region from an IVOA Circle element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *CircleReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* Circle element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Circle element.
+* frm
+* Pointer to the 2D Frame in which the returned Region should be
+* defined. If the Unit attribute is not set, this function will
+* set it to the value supplied in "unit" before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstFrame *cfrm; /* Frame used to define returned Region */
+ AstMapping *map; /* Mapping between units */
+ AstRegion *new; /* Pointer to returned Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *funit; /* Unit string from supplied Frame */
+ const char *names[2]; /* Names of the subelements to be searched for */
+ const char *unit; /* Centre unit string from supplied element */
+ double cen[2]; /* Centre */
+ double rad; /* Radius */
+ int i; /* Axis count */
+ int max[2]; /* Max allowed occurrences of each name */
+ int min[2]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Scan the supplied element for the required sub-elements */
+ names[ 0 ] = "Radius";
+ names[ 1 ] = "Center";
+ min[ 0 ] = 1;
+ min[ 1 ] = 1;
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ scan = ScanIVOAElement( this, elem, 2, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the radius. */
+ rad = ElemValueD( this, scan->el[0][0], 0.0, status );
+
+/* Get the centre. */
+ cen[0] = 0.0;
+ cen[1] = 0.0;
+ ElemListD( this, scan->el[1][0], 2, cen, status );
+
+/* Get the units attribute from the supplied element. */
+ unit = astXmlGetAttributeValue( elem, "unit" );
+ if( !unit ) {
+ Report( this, elem, FAILURE, "contains no unit attribute", status );
+ unit = "";
+ }
+
+/* Since the SkyFrame class does not have active Units we must handle it
+ separately. */
+ if( astIsASkyFrame( frm ) ) {
+
+/* Convert the axis values and radius to radians. */
+ map = astUnitMapper( unit, "rad", NULL, NULL );
+ if( map ) {
+ astTran1( map, 2, cen, 1, cen );
+ astTran1( map, 1, &rad, 1, &rad );
+ map = astAnnul( map );
+ } else if( astOK ) {
+ Report( this, elem, FAILURE, "contains unusable units", status );
+ }
+
+/* Create the Circle. */
+ new = (AstRegion *) astCircle( frm, 1, cen, &rad, NULL, "", status );
+
+/* Now handles Frames other than SkyFrames. */
+ } else {
+
+/* Take a copy of the supplied Frame and set its Units to the value
+ obtained from the supplied element. */
+ cfrm = astCopy( frm );
+ astSetUnit( cfrm, 0, unit );
+ astSetUnit( cfrm, 1, unit );
+
+/* Create a Circle within this modified Frame. */
+ new = (AstRegion *) astCircle( cfrm, 1, cen, &rad, NULL, "", status );
+
+/* If the Unit of this Region differs from that of the supplied Frame,
+ set it to the Unit of the supplied Frame. This will cause the
+ encapsulated limits to be mapped into the new Unit. If the supplied
+ Frame had no set Unit, set it to the units obtained from the supplied
+ element. */
+ for( i = 0; i < 2; i++ ) {
+ if( astTestUnit( frm, i ) ) {
+ funit = astGetUnit( frm, i );
+ if( strcmp( funit, unit ) ) astSetUnit( new, i, funit );
+ } else {
+ astSetUnit( frm, i, unit );
+ }
+ }
+
+/* Free resources */
+ cfrm = astAnnul( cfrm );
+
+ }
+
+/* Get any fill factor and lo/hi_include attributes from the element and
+ assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Free resources */
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* ClearAttrib
+
+* Purpose:
+* Clear an attribute value for a XmlChan.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* void ClearAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* Channel 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
+* XmlChan so that the default value will subsequently be used.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* 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: */
+ AstXmlChan *this; /* Pointer to the XmlChan structure */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) this_object;
+
+/* Check the attribute name and clear the appropriate attribute. */
+
+/* XmlLength */
+/* --------- */
+ if ( !strcmp( attrib, "xmllength" ) ) {
+ astClearXmlLength( this );
+
+/* XmlFormat */
+/* --------- */
+ } else if ( !strcmp( attrib, "xmlformat" ) ) {
+ astClearXmlFormat( this );
+
+/* XmlPrefix */
+/* --------- */
+ } else if ( !strcmp( attrib, "xmlprefix" ) ) {
+ astClearXmlPrefix( 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 AstRegion *ConstraintReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, int *status ){
+/*
+* Name:
+* ConstraintReader
+
+* Purpose:
+* Make an AST Region from an IVOA Constraint element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *ConstraintReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* Constraint element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Constraint element.
+* frm
+* Pointer to the Frame in which the returned Region should be
+* defined. The Unit attribute is assumed to be set to "rad".
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstRegion *new; /* Pointer to returned Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *names[2]; /* Names of the subelements to be searched for */
+ double cen[2]; /* Centre long/lat values */
+ double vec[3]; /* Cartesian centre vector */
+ double rad; /* Radius */
+ int max[2]; /* Max allowed occurrences of each name */
+ int min[2]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Scan the supplied element for the required sub-elements */
+ names[ 0 ] = "Vector";
+ names[ 1 ] = "Offset";
+ min[ 0 ] = 1;
+ min[ 1 ] = 1;
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ scan = ScanIVOAElement( this, elem, 2, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the vector and convert from 3D cartesian to a 2D long/lat centre
+ position, in radians. */
+ vec[0] = 1.0;
+ vec[1] = 0.0;
+ vec[2] = 0.0;
+ ElemListD( this, scan->el[0][0], 3, vec, status );
+ palDcc2s( vec, cen, cen + 1 );
+
+/* Get the offset, and convert to a radial distance in radians. */
+ rad = acos( ElemValueD( this, scan->el[1][0], 1.0, status ) );
+
+/* Create the Circle. */
+ new = (AstRegion *) astCircle( frm, 1, cen, &rad, NULL, "", status );
+
+/* Get any fill factor and lo/hi_include attributes from the element and
+ assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Free resources */
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static AstRegion *ConvexReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, int *status ){
+/*
+* Name:
+* ConvexReader
+
+* Purpose:
+* Make an AST Region from an IVOA Convex element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *ConvexReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* Convex element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Convex element.
+* frm
+* Pointer to the 2D Frame in which the returned Region should be
+* defined. If the Unit attribute is not set, this function will
+* set it to the value supplied in "unit" before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstFrame *cfrm; /* Frame to use when building Constraints */
+ AstRegion *new; /* Pointer to returned Region */
+ AstRegion *reg; /* Pointer to component Region */
+ AstRegion *tmp; /* Pointer to new Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *names[1]; /* Names of the subelements to be searched for */
+ const char *unit; /* Unit attribute in element tag */
+ int i; /* Loop count */
+ int issky; /* Is supplied Frame a SkyFrame? */
+ int max[1]; /* Max allowed occurrences of each name */
+ int min[1]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Search the supplied element for a Region sub-element. */
+ names[ 0 ] = "Constraint";
+ min[ 0 ] = 1;
+ max[ 0 ] = INT_MAX;
+ scan = ScanIVOAElement( this, elem, 1, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Convex needs no units since all values are normalised to a unit sphere */
+ unit = astXmlGetAttributeValue( elem, "unit" );
+ if( unit ) {
+ Report( this, elem, WARNING, "contains unnecessary unit attribute", status );
+ }
+
+/* Unless the supplied Frame is a SkyFrame (which handles the Unit
+ attribute unusually), take a copy of the supplied Frame and set its
+ units to radians. */
+ issky = astIsASkyFrame( frm );
+ if( issky ) {
+ cfrm = astCopy( frm );
+ astSetUnit( cfrm, 0, "rad" );
+ astSetUnit( cfrm, 1, "rad" );
+ } else {
+ cfrm = astClone( frm );
+ }
+
+/* Create Regions from all the component Constraint elements, and combine
+ them into nested CmpRegions, using the boolean AND operator to combine
+ them. */
+ new = ConstraintReader( this, scan->el[0][0], cfrm, status );
+ for( i = 1; i < scan->count[0]; i++ ) {
+ reg = ConstraintReader( this, scan->el[0][i], cfrm, status );
+ tmp = (AstRegion *) astCmpRegion( new, reg, AST__AND, "", status );
+ reg = astAnnul( reg );
+ (void) astAnnul( new );
+ new = tmp;
+ }
+
+/* If required, modify the units back to their original values This
+ will cause the axis values defining the returned Region to be re-mapped
+ into the new units. Do not do this if the supplied Frame is a SkyFrame. */
+ if( !issky ) {
+ if( astTestUnit( frm, 0 ) ) astSetUnit( new, 0, astGetUnit( frm, 0 ) );
+ if( astTestUnit( frm, 1 ) ) astSetUnit( new, 1, astGetUnit( frm, 1 ) );
+ }
+
+/* Get any fill factor from the element and assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Free resources */
+ cfrm = astAnnul( cfrm );
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+
+static AstRegion *Coord2VecIntervalReader( AstXmlChan *this, AstXmlElement *elem,
+ const char *unit, AstFrame *frm, int *status ){
+/*
+* Name:
+* Coord2VecIntervalReader
+
+* Purpose:
+* Make an AST Region from an IVOA Coord2VecInterval element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *Coord2VecIntervalReader( AstXmlChan *this, AstXmlElement *elem,
+* const char *unit, AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* Coord2VecInterval element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Coord2VecInterval element.
+* unit
+* A string holding the units in which the axis values are stored
+* in the supplied element.
+* frm
+* Pointer to the Frame in which the returned Region should be
+* defined. If the Unit attribute is not set, this function will
+* set it to the value supplied in "unit" before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstFrame *cfrm; /* Frame used to define returned Region */
+ AstMapping *map; /* Mapping from supplied units to rads */
+ AstRegion *new; /* Pointer to returned Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *funit; /* Unit string from supplied Frame */
+ const char *names[2]; /* Names of the subelements to be searched for */
+ double hilimit[2]; /* Upper limits */
+ double lolimit[2]; /* Lower limits */
+ int max[2]; /* Max allowed occurrences of each name */
+ int min[2]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "LoLimit2Vec";
+ names[ 1 ] = "HiLimit2Vec";
+ min[ 0 ] = 0;
+ min[ 1 ] = 0;
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ scan = ScanIVOAElement( this, elem, 2, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the limits. */
+ lolimit[0] = AST__BAD;
+ lolimit[1] = AST__BAD;
+ if( scan->count[0] ) ElemListD( this, scan->el[0][0], 2, lolimit, status );
+
+ hilimit[0] = AST__BAD;
+ hilimit[1] = AST__BAD;
+ if( scan->count[1] ) ElemListD( this, scan->el[1][0], 2, hilimit, status );
+
+/* Since the SkyFrame class does not have active Units we must handle it
+ separately. */
+ if( astIsASkyFrame( frm ) ) {
+
+/* Convert the limit values to radians. */
+ map = astUnitMapper( unit, "rad", NULL, NULL );
+ if( map ) {
+ astTran1( map, 2, lolimit, 1, lolimit );
+ astTran1( map, 2, hilimit, 1, hilimit );
+ map = astAnnul( map );
+ } else if( astOK ) {
+ Report( this, elem, FAILURE, "contains unusable units", status );
+ }
+
+/* If at least one limit was found, create an Interval within the supplied
+ Frame. Otherwise create a negated NullRegion. */
+ if( lolimit[ 0 ] != AST__BAD || lolimit[ 1 ] != AST__BAD ||
+ hilimit[ 0 ] != AST__BAD || hilimit[ 1 ] != AST__BAD ) {
+ new = (AstRegion *) astInterval( frm, lolimit, hilimit, NULL, "", status );
+ } else {
+ new = (AstRegion *) astNullRegion( frm, NULL, "negated=1", status );
+ }
+
+/* Now handles Frames other than SkyFrames. */
+ } else {
+
+/* Take a copy of the supplied Frame and set its Unit attribute to the
+ supplied value. */
+ cfrm = astCopy( frm );
+ astSetUnit( cfrm, 0, unit );
+
+/* If at least one limit was found, create an Interval within this
+ modified Frame. Otherwise create a negated NullRegion. */
+ if( lolimit[ 0 ] != AST__BAD || lolimit[ 1 ] != AST__BAD ||
+ hilimit[ 0 ] != AST__BAD || hilimit[ 1 ] != AST__BAD ) {
+ new = (AstRegion *) astInterval( cfrm, lolimit, hilimit, NULL, "", status );
+ } else {
+ new = (AstRegion *) astNullRegion( cfrm, NULL, "negated=1", status );
+ }
+
+/* If the supplied units differ from that of the supplied Frame, set the
+ units in the Region to those of the supplied Frame. This will cause the
+ encapsulated limits to be mapped into the required units. If the supplied
+ Frame had no set Units, set it to the supplied units. */
+ if( astTestUnit( frm, 0 ) ) {
+ funit = astGetUnit( frm, 0 );
+ if( strcmp( funit, unit ) ) astSetUnit( new, 0, funit );
+ } else {
+ astSetUnit( frm, 0, unit );
+ }
+
+/* Free resources */
+ cfrm = astAnnul( cfrm );
+ }
+
+/* Get any fill factor and lo/hi_include attributes from the element and
+ assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Free resources */
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static AstRegion *Coord3VecIntervalReader( AstXmlChan *this, AstXmlElement *elem,
+ const char *unit, AstFrame *frm, int *status ){
+/*
+* Name:
+* Coord3VecIntervalReader
+
+* Purpose:
+* Make an AST Region from an IVOA Coord3VecInterval element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *Coord3VecIntervalReader( AstXmlChan *this, AstXmlElement *elem,
+* const char *unit, AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* Coord3VecInterval element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Coord3VecInterval element.
+* unit
+* A string holding the units in which the axis values are stored
+* in the supplied element.
+* frm
+* Pointer to the Frame in which the returned Region should be
+* defined. If the Unit attribute is not set, this function will
+* set it to the value supplied in "unit" before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstFrame *cfrm; /* Frame used to define returned Region */
+ AstRegion *new; /* Pointer to returned Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *funit; /* Unit string from supplied Frame */
+ const char *names[2]; /* Names of the subelements to be searched for */
+ double hilimit[3]; /* Upper limits */
+ double lolimit[3]; /* Lower limits */
+ int max[2]; /* Max allowed occurrences of each name */
+ int min[2]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "LoLimit3Vec";
+ names[ 1 ] = "HiLimit3Vec";
+ min[ 0 ] = 0;
+ min[ 1 ] = 0;
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ scan = ScanIVOAElement( this, elem, 2, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the limits. */
+ lolimit[0] = AST__BAD;
+ lolimit[1] = AST__BAD;
+ lolimit[2] = AST__BAD;
+ if( scan->count[0] ) ElemListD( this, scan->el[0][0], 3, lolimit, status );
+
+ hilimit[0] = AST__BAD;
+ hilimit[1] = AST__BAD;
+ hilimit[2] = AST__BAD;
+ if( scan->count[1] ) ElemListD( this, scan->el[1][0], 3, hilimit, status );
+
+/* Take a copy of the supplied Frame and set its Unit attribute to the
+ supplied value. */
+ cfrm = astCopy( frm );
+ astSetUnit( cfrm, 0, unit );
+
+/* If at least one limit was found, create an Interval within this
+ modified Frame. Otherwise create a negated NullRegion. */
+ if( lolimit[ 0 ] != AST__BAD || lolimit[ 1 ] != AST__BAD ||
+ lolimit[ 2 ] != AST__BAD ||
+ hilimit[ 0 ] != AST__BAD || hilimit[ 1 ] != AST__BAD ||
+ hilimit[ 2 ] != AST__BAD ) {
+ new = (AstRegion *) astInterval( cfrm, lolimit, hilimit, NULL, "", status );
+ } else {
+ new = (AstRegion *) astNullRegion( cfrm, NULL, "negated=1", status );
+ }
+
+/* If the supplied units differ from that of the supplied Frame, set the
+ units in the Region to those of the supplied Frame. This will cause the
+ encapsulated limits to be mapped into the required units. If the supplied
+ Frame had no set Units, set it to the supplied units. */
+ if( astTestUnit( frm, 0 ) ) {
+ funit = astGetUnit( frm, 0 );
+ if( strcmp( funit, unit ) ) astSetUnit( new, 0, funit );
+ } else {
+ astSetUnit( frm, 0, unit );
+ }
+
+/* Get any fill factor and lo/hi_include attributes from the element and
+ assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Free resources */
+ cfrm = astAnnul( cfrm );
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static AstRegion *CoordScalarIntervalReader( AstXmlChan *this, AstXmlElement *elem,
+ const char *unit, AstFrame *frm, int *status ){
+/*
+* Name:
+* CoordScalarIntervalReader
+
+* Purpose:
+* Make an AST Region from an IVOA CoordScalarInterval element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *CoordScalarIntervalReader( AstXmlChan *this, AstXmlElement *elem,
+* const char *unit, AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* CoordScalarInterval element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA CoordScalarInterval element.
+* unit
+* A string holding the units in which the axis values are stored
+* in the supplied element.
+* frm
+* Pointer to the Frame in which the returned Region should be
+* defined. If the Unit attribute is not set, this function will
+* set it to the value supplied in "unit" before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstFrame *cfrm; /* Frame used to define returned Region */
+ AstRegion *new; /* Pointer to returned Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *funit; /* Unit string from supplied Frame */
+ const char *names[2]; /* Names of the subelements to be searched for */
+ double hilimit; /* Upper limit */
+ double lolimit; /* Lower limit */
+ int max[2]; /* Max allowed occurrences of each name */
+ int min[2]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "LoLimit";
+ names[ 1 ] = "HiLimit";
+ min[ 0 ] = 0;
+ min[ 1 ] = 0;
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ scan = ScanIVOAElement( this, elem, 2, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the limits. */
+ lolimit = scan->count[0] ? ElemValueD( this, scan->el[0][0], 0.0, status ) : AST__BAD;
+ hilimit = scan->count[1] ? ElemValueD( this, scan->el[1][0], 0.0, status ) : AST__BAD;
+
+/* Take a copy of the supplied Frame and set its Unit attribute to the
+ supplied value. */
+ cfrm = astCopy( frm );
+ astSetUnit( cfrm, 0, unit );
+
+/* If at least one limit was found, create an Interval within this
+ modified Frame. Otherwise create a negated NullRegion. */
+ if( lolimit != AST__BAD || hilimit != AST__BAD ) {
+ new = (AstRegion *) astInterval( cfrm, &lolimit, &hilimit, NULL, "", status );
+ } else {
+ new = (AstRegion *) astNullRegion( cfrm, NULL, "negated=1", status );
+ }
+
+/* If the supplied units differ from that of the supplied Frame, set the
+ units in the Region to those of the supplied Frame. This will cause the
+ encapsulated limits to be mapped into the required units. If the supplied
+ Frame had no set Units, set it to the supplied units. */
+ if( astTestUnit( frm, 0 ) ) {
+ funit = astGetUnit( frm, 0 );
+ if( strcmp( funit, unit ) ) astSetUnit( new, 0, funit );
+ } else {
+ astSetUnit( frm, 0, unit );
+ }
+
+/* Get any fill factor and lo/hi_include attributes from the element and
+ assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Free resources */
+ cfrm = astAnnul( cfrm );
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static int ElemListD( AstXmlChan *this, AstXmlElement *elem, int n,
+ double *vals, int *status ) {
+/*
+* Name:
+* ElemListD
+
+* Purpose:
+* Read a floating point XML element value.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* int ElemListD( AstXmlChan *this, AstXmlElement *elem, int n,
+* double *vals, int *status )
+
+* Class Membership:
+* XmlChan member function
+
+* Description:
+* This function reads the content of the supplied element, converts its
+* contents to a list of floating point values and returns these
+* values in "values". A report is made if the element value is not a
+* space separated list of floating point values, or if it contains
+* more than "n" values. The number of values stored in "values" is
+* returned as the function value.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the XmlElement.
+* n
+* The maximum number of floating point values to read from the supplied
+* element.
+* values
+* Pointer to an array to hold the values read. This should have at
+* least "n" elements. Any unused elements are left unchanged.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The number of values stored in "values".
+
+*/
+
+/* Local Variables: */
+ AstXmlContentItem *item; /* Item no. "i" */
+ char *text; /* Pointer to string holding formatted item */
+ char buff[200]; /* Message buffer */
+ const char *p; /* Pointer to start of remaining text */
+ const char *value; /* Pointer to element value */
+ double dval; /* Value read from string */
+ int i; /* Index of current item */
+ int l; /* Used length of string */
+ int nc; /* Number of characters read by astSscanf */
+ int nitem; /* Number of items in the element */
+ int rep; /* Has a warning about excess values been made? */
+ int result; /* Value to be returned */
+
+/* Initialise. */
+ result = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* No warning has yet been made avbout excess values */
+ rep = 0;
+
+/* Loop through all content items within the supplied element. */
+ nitem = astXmlGetNitem( elem );
+ for( i = 0; i < nitem; i++ ) {
+ item = astXmlGetItem( elem, i );
+
+/* If this is non-blank character data, attempt to read values from it. */
+ if( astXmlCheckType( item, AST__XMLBLACK ) ) {
+
+/* Get the element value as a string. */
+ value = astXmlGetValue( item, 0 );
+ if( value ) {
+
+/* Loop round reading floating point values from the text until the
+ end of the string is reached. */
+ l = astChrLen( value );
+ p = value;
+ while( p < value + l ){
+
+/* Read a floating point value from the start of the remaining string,
+ storing the result in the supplied array. If succesful, increment the
+ number of values read, and increment the pointer to the start of the
+ remaining string. Abort if too many values are found. */
+ if( astSscanf( p, " %lf %n", &dval, &nc ) == 1 ) {
+ if( result < n ) {
+ vals[ result++ ] = dval;
+ p += nc;
+ } else {
+ if( !rep ) {
+ rep = 1;
+ if( n > 1 ) {
+ sprintf( buff, "contains more than %d values - "
+ "extra values will be ignored", n );
+ } else {
+ sprintf( buff, "contains more than 1 value - "
+ "extra values will be ignored" );
+ }
+ Report( this, elem, WARNING, buff, status );
+ }
+ break;
+ }
+
+/* If the remaing text is not a floating point value, then issue a report. */
+ } else {
+ Report( this, elem, FAILURE, "contains a non-numerical value", status );
+ break;
+ }
+ }
+ }
+
+/* If this is not character data, nor a comment, issue a warning. */
+ } else if( !astXmlCheckType( item, AST__XMLWHITE ) &&
+ !astXmlCheckType( item, AST__XMLCOM ) ) {
+ text = (char *) astXmlFormat( item );
+ if( text ) {
+ if( strlen( text ) > 30 ) text[ 30 ] = 0;
+ sprintf( buff, "contains the following which is being ignored: \"%s\"",
+ text );
+ text = astFree( text );
+ Report( this, elem, WARNING, buff, status );
+ }
+ }
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static double ElemValueD( AstXmlChan *this, AstXmlElement *elem, double def, int *status ) {
+/*
+* Name:
+* ElemValueD
+
+* Purpose:
+* Read a floating point XML element value.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* double ElemValueD( AstXmlChan *this, AstXmlElement *elem, double def )
+
+* Class Membership:
+* XmlChan member function
+
+* Description:
+* This function reads the content of the supplied element, converts its
+* contents to a floating point value and returns this value. A report is
+* made if the element value is not floating point. The supplied default
+* value is returned if the element is not present.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the XmlElement.
+* def
+* If the content of the supplied element is not a flaoting point
+* value, then this value will be returned instead.
+
+* Returned Value:
+* The required element value, or the default if the value was not found.
+
+*/
+
+/* Local Variables: */
+ const char *value; /* Pointer to element value */
+ double result; /* Value to be returned */
+ int nc; /* Number of characters read by astSscanf */
+ int ok; /* Value read OK? */
+
+/* Initialise. */
+ result = def;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Assume the value cannot be read. */
+ ok = 0;
+
+/* Get the element value as a string. */
+ value = astXmlGetValue( elem, 0 );
+
+/* If succesful, convert the value to floating point. */
+ if( value ) {
+ nc = 0;
+ ok = ( ( 1 == astSscanf( value, " %lf %n", &result, &nc ) )
+ && ( nc >= (int) strlen( value ) ) );
+ }
+
+/* Give a warning if not OK, and use default value. */
+ if( !ok ) {
+ Report( this, elem, FAILURE, "does not contain a floating point value", status );
+ result = def;
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static AstRegion *EllipseReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, int *status ){
+/*
+* Name:
+* EllipseReader
+
+* Purpose:
+* Make an AST Region from an IVOA Ellipse element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *EllipseReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* Ellipse element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Ellipse element.
+* frm
+* Pointer to the 2D Frame in which the returned Region should be
+* defined. If the Unit attribute is not set, this function will
+* set it to the value supplied in "unit" before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstFrame *cfrm; /* Frame used to define returned Region */
+ AstMapping *map; /* Mapping between units */
+ AstRegion *new; /* Pointer to returned Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *funit; /* Unit string from supplied Frame */
+ const char *names[4]; /* Names of the subelements to be searched for */
+ const char *unit; /* Centre and radii unit string */
+ double cen[2]; /* Centre */
+ double pa; /* Major axis position angle */
+ double rad[2]; /* Major and minor radii */
+ int i; /* Axis count */
+ int max[4]; /* Max allowed occurrences of each name */
+ int min[4]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Scan the supplied element for the required sub-elements */
+ names[ 0 ] = "Radius";
+ names[ 1 ] = "Center";
+ names[ 2 ] = "MinorRadius";
+ names[ 3 ] = "PosAngle";
+ min[ 0 ] = 1;
+ min[ 1 ] = 1;
+ min[ 2 ] = 1;
+ min[ 3 ] = 1;
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ max[ 2 ] = 1;
+ max[ 3 ] = 1;
+ scan = ScanIVOAElement( this, elem, 4, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the major radius */
+ rad[ 0 ] = ElemValueD( this, scan->el[0][0], 0.0, status );
+
+/* Get the minor radius. */
+ rad[ 1 ] = ElemValueD( this, scan->el[2][0], 0.0, status );
+
+/* Get the centre. */
+ cen[0] = 0.0;
+ cen[1] = 0.0;
+ ElemListD( this, scan->el[1][0], 2, cen, status );
+
+/* Get the position angle. This is returned in the AST convention, i.e.
+ measured in radians from from +ve second axis through positive first
+ axis. */
+ pa = PosAngleReader( this, scan->el[3][0], status );
+
+/* Get the units attribute from the supplied element. These are the units
+ of the centre and radii value. */
+ unit = astXmlGetAttributeValue( elem, "unit" );
+ if( !unit ) {
+ Report( this, elem, FAILURE, "contains no unit attribute", status );
+ unit = "";
+ }
+
+/* Since the SkyFrame class does not have active Units we must handle it
+ separately. */
+ if( astIsASkyFrame( frm ) ) {
+
+/* Convert the axis values and radii to radians. */
+ map = astUnitMapper( unit, "rad", NULL, NULL );
+ if( map ) {
+ astTran1( map, 2, cen, 1, cen );
+ astTran1( map, 2, rad, 1, rad );
+ map = astAnnul( map );
+ } else if( astOK ) {
+ Report( this, elem, FAILURE, "contains unusable units", status );
+ }
+
+/* Create the Ellipse. */
+ new = (AstRegion *) astEllipse( frm, 1, cen, rad, &pa, NULL, "", status );
+
+/* Now handles Frames other than SkyFrames. */
+ } else {
+
+/* Take a copy of the supplied Frame and set its Units to the value
+ obtained from the supplied element. */
+ cfrm = astCopy( frm );
+ astSetUnit( cfrm, 0, unit );
+ astSetUnit( cfrm, 1, unit );
+
+/* Create a Ellipse within this modified Frame. */
+ new = (AstRegion *) astEllipse( cfrm, 1, cen, rad, &pa, NULL, "", status );
+
+/* If the Unit of this Region differs from that of the supplied Frame,
+ set it to the Unit of the supplied Frame. This will cause the
+ encapsulated limits to be mapped into the new Unit. If the supplied
+ Frame had no set Unit, set it to the units obtained from the supplied
+ element. */
+ for( i = 0; i < 2; i++ ) {
+ if( astTestUnit( frm, i ) ) {
+ funit = astGetUnit( frm, i );
+ if( strcmp( funit, unit ) ) astSetUnit( new, i, funit );
+ } else {
+ astSetUnit( frm, i, unit );
+ }
+ }
+
+/* Free resources */
+ cfrm = astAnnul( cfrm );
+ }
+
+/* Get any fill factor and lo/hi_include attributes from the element and
+ assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Free resources */
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static double Error2PAReader( AstXmlChan *this, AstXmlElement *elem,
+ double *size, int *status ){
+/*
+* Name:
+* Error2PAReader
+
+* Purpose:
+* Read the contents of an Stc Error2PA element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* double Error2PAReader( AstXmlChan *this, AstXmlElement *elem,
+* double *size, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function reads the contents of an Stc Error2PA element. It can
+* also be used to read Resolution2PA, Size2PAand PixSize2PA which
+* have exactly the same structure as a Error2PA element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Ellipse element.
+* size
+* Pointer to an array to receive the 2 error sizes.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The position angle of the first error size, in radians, from positive
+* second axis to positive first axis.
+
+*/
+
+/* Local Variables: */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *names[4]; /* Names of the subelements to be searched for */
+ double pa; /* Major axis position angle */
+ int max[4]; /* Max allowed occurrences of each name */
+ int min[4]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ pa = 0.0;
+
+/* Check the global error status. */
+ if ( !astOK ) return pa;
+
+/* Scan the supplied element for the required sub-elements */
+ names[ 0 ] = "Size";
+ names[ 1 ] = "PosAngle";
+ min[ 0 ] = 1;
+ min[ 1 ] = 0;
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ scan = ScanIVOAElement( this, elem, 2, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the sizes */
+ ElemListD( this, scan->el[0][0], 2, size, status );
+
+/* Get the position angle. This is returned in the AST convention, i.e.
+ measured in radians from from +ve second axis through positive first
+ axis. */
+ pa = PosAngleReader( this, scan->el[1][0], status );
+
+/* Free resources */
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Return the position angle. */
+ return pa;
+}
+
+static void FillAndLims( AstXmlChan *this, AstXmlElement *elem, AstRegion *new, int *status ){
+/*
+* Name:
+* FillAndLims
+
+* Purpose:
+* Get fill factor and limit inclusion flags from IVOA element and
+* assign to an AST Region.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* void FillAndLims( AstXmlChan *this, AstXmlElement *elem, AstRegion *new, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function gets attributes from the supplied element describing
+* fill factor and limit inclusion flags, and assigns suitable values
+* to the supplied Region. Default values are used if the supplied
+* element does not have the required attributes.
+
+* Parameters:
+* this
+* Pointer to the XmlChan in which to store warnings.
+* elem
+* Pointer to the AstXmlElement to search.
+* new
+* Pointer to the Region in which to store the values.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ const char *text; /* Attribute text */
+ double ff; /* Fill factor */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get any fill factor attribute from the element and assign to the
+ returned Region. */
+ ff = AttrValueD( this, elem, "fill_factor", AST__BAD, status );
+ if( ff != AST__BAD ) astSetFillFactor( new, ff );
+
+/* Get the flags indicating if the limits are included in the interval.
+ If either of the limits is not included in the interval, then make the
+ Region open. Assume a default of true ("included") if the attribute is
+ missing. */
+ text = astXmlGetAttributeValue( elem, "lo_include" );
+ if( text && !strcmp( text, "false" ) ) astSetClosed( new, 0 );
+
+ text = astXmlGetAttributeValue( elem, "hi_include" );
+ if( text && !strcmp( text, "false" ) ) astSetClosed( new, 0 );
+
+}
+
+static AstXmlElement *FindAttribute( AstXmlChan *this, const char *name, int *status ) {
+/*
+* Name:
+* FindAttribute
+
+* Purpose:
+* Find an XML element representing a named AST attribute.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstXmlElement *FindAttribute( AstXmlChan *this, const char *name, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function searches the content of the current container element
+* of the supplied XmlChan looking for an element which represents a
+* named AST attribute. No error is reported if the attribute is not
+* found. Attributes which represent defaul values are ignored.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* name
+* Pointer to a string holding the required AST attribute name
+* (case-insensitive).
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the XmlElement if found, and NULL otherwise.
+
+*/
+
+/* Local Variables: */
+ AstXmlContentItem *item; /* Item no. "i" */
+ AstXmlElement *result; /* Returned pointer */
+ const char *def; /* Value from XML DEFAULT attribute */
+ const char *definedby; /* Name of class which defines the item */
+ const char *xmlname; /* Value from XML NAME attribute */
+ int i; /* Index of current item */
+ int nitem; /* Number of items still in the element */
+
+/* Initialise */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Report an error if the class being loaded has not been set. */
+ if( !this->isa_class ) {
+ astError( AST__INTER, "astRead(XmlChan): astReadNextData not called "
+ "before reading values for a %s (internal AST programming "
+ "error).", status, astXmlGetName( this->container ) );
+ }
+
+/* Check we have a container to search. */
+ if( !this->container ) {
+ astError( AST__INTER, "astRead(XmlChan): No container before reading "
+ "values for a %s (internal AST programming error).", status,
+ astXmlGetName( this->container ) );
+ }
+
+/* Check all is OK. */
+ if( astOK ) {
+
+/* Loop round all items in the elements contents. */
+ nitem = astXmlGetNitem( this->container );
+ for( i = 0; i < nitem; i++ ) {
+ item = astXmlGetItem( this->container, i );
+
+/* Ignore this item if it is not an element. */
+ if( astXmlCheckType( item, AST__XMLELEM ) ) {
+
+/* Ignore this element if its name is not ATTR. */
+ if( !astOK ) break;
+ if( !strcmp( astXmlGetName( item ), ATTR ) ){
+
+/* Ignore this element if it represents a default value. */
+ def = astXmlGetAttributeValue( item, DEFAULT );
+ if( !def || strcmp( def, TRUE ) ) {
+
+/* If this ATTR element has an XML attribute called NAME with
+ the required value (case-insensitive), we may have found a matching
+ element. */
+ xmlname = astXmlGetAttributeValue( item, NAME );
+ if( xmlname && !Ustrcmp( xmlname, name, status ) ) {
+
+/* Ignore the attribute if it does not belong to the correct part of the
+ object's class hierarchy. If it does, we have found the required
+ attribute. */
+ definedby = astXmlGetAttributeValue( item, DEFINEDBY );
+ if( definedby && !strcmp( definedby, this->isa_class ) ) {
+ result = (AstXmlElement *) item;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+/* Return the pointer. */
+ return result;
+}
+
+static AstXmlElement *FindElement( AstXmlChan *this, AstXmlElement *elem,
+ const char *name, int *status ) {
+/*
+* Name:
+* FindElement
+
+* Purpose:
+* Find a named element within a supplied element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstXmlElement *FindElement( AstXmlChan *this, AstXmlElement *elem,
+* const char *name, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function searches the content of the supplied element looking for
+* an element with the supplied Name. No error is reported if the element
+* is not found, but a Warning is issued if it found more than once.
+
+* Parameters:
+* this
+* Pointer to the XmlChan in which to store warnings.
+* elem
+* Pointer to the AstXmlElement to search.
+* name
+* Pointer to a string holding the required element name.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the XmlElement if found, and NULL otherwise.
+
+* Notes:
+* - If the supplied element contains more than one element with the
+* given name, the returned pointer locates the first element
+* encountered with the required name, and a WARNING is issued that the
+* second and subsequent elements will be ignored.
+
+*/
+
+/* Local Variables: */
+ AstXmlContentItem *item; /* Item no. "i" */
+ AstXmlElement *result; /* Returned pointer */
+ char buff[ 200 ]; /* Message buffer */
+ int i; /* Index of current item */
+ int nitem; /* Number of items still in the element */
+ int warned; /* Has a warning been issued? */
+
+/* Initialise */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Loop round all items in the elements contents. */
+ warned = 0;
+ nitem = astXmlGetNitem( elem );
+ for( i = 0; i < nitem; i++ ) {
+ item = astXmlGetItem( elem, i );
+
+/* Ignore this item if it is not an element. */
+ if( astXmlCheckType( item, AST__XMLELEM ) ) {
+
+/* If this element's name is the given name. */
+ if( !strcmp( astXmlGetName( item ), name ) ){
+
+/* If this is the first element with the required name, store its
+ pointer. */
+ if( !result ) {
+ result = (AstXmlElement *) item;
+
+/* Otherwise add a Warning (unles a Warning has already been issued). */
+ } else if( !warned ) {
+ warned = 1;
+ sprintf( buff, "contains more than one %s element. The "
+ "second and subsequent such elements will be "
+ "ignored", name );
+ Report( this, elem, WARNING, buff, status );
+ }
+ }
+ }
+ }
+
+/* Return the pointer. */
+ return result;
+}
+
+static IVOAReader FindIVOAClass( AstXmlElement *elem, int *is_ivoa, int *status ) {
+/*
+* Name:
+* FindIVOAClass
+
+* Purpose:
+* Return a pointer to a function which will create an AST Object from
+* an IVOA element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* IVOAReader FindIVOAClass( AstXmlElement *elem, int *is_ivoa, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function checks the namespace of the supplied element to see if
+* it is a known IVOA namespace. If it is, it returns the "is_ivoa"
+* flag set to non-zero (otherwise it is returned as zero). It then
+* checks to see if an AST object can be created from the IVOA
+* element. If so, a pointer to the function which will do the
+* conversion is returned. Otherwise a NULL pointer is returned.
+
+* Parameters:
+* elem
+* Pointer to the element to check.
+* is_ivoa
+* Pointer to an int in which to return a flag indicating if the
+* supplied element belongs to a known IVOA namespace.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Pointer to the function (if any) which can produce an AST Object
+* from the supplied element, or NULL if conversion is not possible.
+
+* Notes:
+* - NULL is returned if this function is invoked with the error
+* status set, or if it should fail for any reason.
+*/
+
+/* Local Variables: */
+ IVOAReader result; /* Returned pointer */
+ const char *ivoa; /* Pointer to "ivoa" substring */
+ const char *name; /* Pointer to string holding element name */
+ const char *stc; /* Pointer to "stc" substring */
+ const char *uri; /* Pointer to string holding element namespace URI */
+
+/* Initialise */
+ *is_ivoa = 0;
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Get the element name.*/
+ name = astXmlGetName( elem );
+
+/* Get the namespace URI for the element, and see if it contains
+ sub-strings "STC" (or "stc") and "IVOA" (or "ivoa"). */
+ uri = astXmlGetURI( elem );
+ if( uri ) {
+ stc = strstr( uri, "STC" );
+ if( !stc ) stc = strstr( uri, "stc" );
+ ivoa = strstr( uri, "IVOA" );
+ if( !ivoa ) ivoa = strstr( uri, "ivoa" );
+
+ } else {
+ stc = NULL;
+ ivoa = NULL;
+ }
+
+/* If it is a known IVOA namespace, proceed. */
+ if( name && ( stc || ivoa ) ){
+ *is_ivoa = 1;
+
+/* Look for element types which can be converted to AST objects, and
+ return a pointer to the corresponding reader function. */
+ if( !strcmp( name, STC_RESOURCE_PROFILE ) ) {
+ result = StcMetadataReader;
+
+ } else if( !strcmp( name, SEARCH_LOCATION ) ) {
+ result = StcMetadataReader;
+
+ } else if( !strcmp( name, CATALOG_ENTRY_LOCATION ) ) {
+ result = StcMetadataReader;
+
+ } else if( !strcmp( name, OBSERVATION_LOCATION ) ) {
+ result = StcMetadataReader;
+
+ } else if( !strcmp( name, OBS_DATA_LOCATION ) ) {
+ result = ObsDataLocationReader;
+
+ } else if( !strcmp( name, ASTRO_COORD_SYSTEM ) ) {
+ result = AstroCoordSystemReader;
+
+ } else if( !strcmp( name, TIME_FRAME ) ) {
+ result = TimeFrameReader;
+
+ } else if( !strcmp( name, SPACE_FRAME ) ) {
+ result = SpaceFrameReader;
+
+ } else if( !strcmp( name, SPECTRAL_FRAME ) ) {
+ result = SpectralFrameReader;
+
+ } else if( !strcmp( name, REDSHIFT_FRAME ) ) {
+ result = RedshiftFrameReader;
+
+ } else if( !strcmp( name, REDSHIFT_FRAME ) ) {
+ result = RedshiftFrameReader;
+
+ }
+ }
+
+/* Return null if an error occurred. */
+ if( !astOK ) result = NULL;
+
+/* Return the result. */
+ return result;
+}
+
+static const char *FindNextIsA( AstXmlElement *elem, int start, int *status ) {
+/*
+* Name:
+* FindNextIsA
+
+* Purpose:
+* Find the next "isa" element within an XML element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* const char *FindNextIsA( AstXmlElement *elem, int start, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function searches the content of the specified element,
+* starting at the item with the speicfied index, until it finds the
+* next "isa" element. It returns the value of the "class" attribute
+* of the found "isa" element, or the name of the supplied element
+* if no "isa" element is found.
+
+* Parameters:
+* elem
+* Pointer to the XmlElement (an element describing an AST Object).
+* start
+* The index of the first content item to check.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the class string.
+
+*/
+
+/* Local Variables: */
+ AstXmlContentItem *item; /* Item no. "i" */
+ const char *result; /* Returned string */
+ int i; /* Index of current item */
+ int nitem; /* Number of items still i nthe element */
+
+/* Initialise */
+ result = astXmlGetName( elem );
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Loop round all items contained in the element, starting at the given
+ index. */
+ nitem = astXmlGetNitem( elem );
+ for( i = start; i < nitem; i++ ) {
+ item = astXmlGetItem( elem, i );
+
+/* Check this item is an XmlElement with name ISA. */
+ if( astXmlCheckType( item, AST__XMLELEM ) ) {
+ if( astOK && !strcmp( astXmlGetName( item ), ISA ) ) {
+
+/* The returned string is the value of the "class" attribute of this
+ element. */
+ result = astXmlGetAttributeValue( item, "class" );
+
+/* Report an error if the element does not have a class attribute. */
+ if( !result && astOK ) {
+ astError( AST__BADIN, "astRead(XmlChan): The tag %s "
+ "does not include a \"class\" attribute.", status,
+ GetTag( (AstXmlObject *) item, 1, status ) );
+ }
+
+ break;
+
+ }
+ }
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static AstXmlElement *FindObject( AstXmlChan *this, const char *name, int *status ) {
+/*
+* Name:
+* FindObject
+
+* Purpose:
+* Find an XML element representing a named AST Object.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstXmlElement *FindObject( AstXmlChan *this, const char *name, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function searches the content of the current container element
+* of the supplied XmlChan looking for an element which represents a
+* named AST Object. No error is reported if the object is not
+* found. Objects which represent default values are ignored.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* name
+* Pointer to a string holding the required AST object name
+* (case-insensitive).
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the XmlElement if found, and NULL otherwise.
+
+*/
+
+/* Local Variables: */
+ AstXmlContentItem *item; /* Item */
+ AstXmlElement *result; /* Returned pointer */
+ const char *def; /* Value from XML DEFAULT attribute */
+ const char *definedby; /* Name of class which defines the item */
+ const char *xmlname; /* Value from XML LABEL attribute */
+ int i; /* Index of current item */
+ int nitem; /* Number of items still i nthe element */
+
+/* Initialise */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Report an error if the class being loaded has not been set. */
+ if( !this->isa_class ) {
+ astError( AST__INTER, "astRead(XmlChan): astReadNextData not called "
+ "before reading values for a %s (internal AST programming "
+ "error).", status, astXmlGetName( this->container ) );
+ }
+
+/* Check we have a container to search. */
+ if( !this->container ) {
+ astError( AST__INTER, "astRead(XmlChan): No container before reading "
+ "values for a %s (internal AST programming error).", status,
+ astXmlGetName( this->container ) );
+ }
+
+/* Check all is OK. */
+ if( astOK ) {
+
+/* Loop round all items in the elements contents. */
+ nitem = astXmlGetNitem( this->container );
+ for( i = 0; i < nitem; i++ ) {
+ item = astXmlGetItem( this->container, i );
+
+/* Ignore this item if it is not an element. */
+ if( astXmlCheckType( item, AST__XMLELEM ) ) {
+
+/* Ignore this element if its name is ATTR. */
+ if( astOK && strcmp( astXmlGetName( item ), ATTR ) ){
+
+/* Ignore this element if it represents a default value. */
+ def = astXmlGetAttributeValue( item, DEFAULT );
+ if( !def || strcmp( def, TRUE ) ) {
+
+/* If this non-ATTR element has an XML attribute called LABEL with
+ the required value (case-insensitive), we may have found a matching element. */
+ xmlname = astXmlGetAttributeValue( item, LABEL );
+ if( xmlname && !Ustrcmp( xmlname, name, status ) ) {
+
+/* Ignore the element if it does not belong to the correct part of the
+ object's class hierarchy. If it does, we have found the required
+ object. */
+ definedby = astXmlGetAttributeValue( item, DEFINEDBY );
+ if( definedby && !strcmp( definedby, this->isa_class ) ) {
+ result = (AstXmlElement *) item;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+/* Return the pointer. */
+ return result;
+}
+
+static int FindString( int n, const char *list[], const char *test,
+ const char *text, const char *method,
+ const char *class, int *status ){
+/*
+* Name:
+* FindString
+
+* Purpose:
+* Find a given string within an array of character strings.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* int FindString( int n, const char *list[], const char *test,
+* const char *text, const char *method, const char *class, int *status )
+
+* Class Membership:
+* XmlChan method.
+
+* Description:
+* This function identifies a supplied string within a supplied
+* array of valid strings, and returns the index of the string within
+* the array. The test option may not be abbreviated, but case is
+* insignificant.
+
+* Parameters:
+* n
+* The number of strings in the array pointed to be "list".
+* list
+* A pointer to an array of legal character strings.
+* test
+* A candidate string.
+* text
+* A string giving a description of the object, parameter,
+* attribute, etc, to which the test value refers.
+* This is only for use in constructing error messages. It should
+* start with a lower case letter.
+* method
+* Pointer to a string holding the name of the calling method.
+* This is only for use in constructing error messages.
+* class
+* Pointer to a string holding the name of the supplied object class.
+* This is only for use in constructing error messages.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The index of the identified string within the supplied array, starting
+* at zero.
+
+* Notes:
+* - A value of -1 is returned if an error has already occurred, or
+* if this function should fail for any reason (for instance if the
+* supplied option is not specified in the supplied list).
+
+*/
+
+/* Local Variables: */
+ int ret; /* The returned index */
+
+/* Check global status. */
+ if( !astOK ) return -1;
+
+/* Compare the test string with each element of the supplied list. Leave
+ the loop when a match is found. */
+ for( ret = 0; ret < n; ret++ ) {
+ if( !Ustrcmp( test, list[ ret ], status ) ) break;
+ }
+
+/* Report an error if the supplied test string does not match any element
+ in the supplied list. */
+ if( ret >= n && astOK ) {
+ astError( AST__RDERR, "%s(%s): Illegal value '%s' supplied for %s.", status,
+ method, class, test, text );
+ ret = -1;
+ }
+
+/* Return the answer. */
+ return ret;
+}
+
+static IVOAScan *FreeIVOAScan( IVOAScan *in, int *status ){
+/*
+* Name:
+* FreeIVOAScan
+
+* Purpose:
+* Free resources used by an IVOAScan structure.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* IVOAScan *FreeIVOAScan( IVOAScan *in )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function frees resources used by an IVOAScan structure (such
+* as returned by the ScanIVOAElement function).
+
+* Parameters:
+* in
+* Pointer to the IVOAScan structure.
+
+* Returned Value:
+* A NULL pointer.
+
+*/
+
+/* Local Variables: */
+ int j; /* Index of current name */
+
+/* Check the supplied pointer can be used safely. */
+ if( in ) {
+
+/* Free the arrays holding the element pointers. */
+ for( j = 0; j < in->n; j++ ) {
+ in->count[ j ] = 0;
+ in->el[ j ] = astFree( in->el[ j ] );
+ }
+
+/* Free the array holding the pointers to the arrays holding the element
+ pointers. */
+ in->el = astFree( in->el );
+
+/* Free the array holding the number of element pointers stored. */
+ in->count = astFree( in->count );
+
+/* For safety, put a zero in the name count. */
+ in->n = 0;
+
+/* Free the whole structure. */
+ in = astFree( in );
+
+ }
+
+ return NULL;
+}
+
+static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* GetAttrib
+
+* Purpose:
+* Get the value of a specified attribute for a XmlChan.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* const char *GetAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* XmlChan 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 XmlChan, formatted as a character string.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* 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 XmlChan, 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 XmlChan. 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 */
+ AstXmlChan *this; /* Pointer to the XmlChan 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 XmlChan structure. */
+ this = (AstXmlChan *) 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. */
+
+/* XmlLength */
+/* --------- */
+ if ( !strcmp( attrib, "xmllength" ) ) {
+ ival = astGetXmlLength( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%d", ival );
+ result = getattrib_buff;
+ }
+
+/* XmlFormat */
+/* --------- */
+ } else if ( !strcmp( attrib, "xmlformat" ) ) {
+ ival = astGetXmlFormat( this );
+ if ( astOK ) {
+ if( ival == NATIVE_FORMAT ){
+ result = NATIVE_STRING;
+
+ } else if( ival == QUOTED_FORMAT ){
+ result = QUOTED_STRING;
+
+ } else if( ival == IVOA_FORMAT ){
+ result = IVOA_STRING;
+
+ } else {
+ result = UNKNOWN_STRING;
+ }
+ }
+
+/* XmlPrefix */
+/* --------- */
+ } else if ( !strcmp( attrib, "xmlprefix" ) ) {
+ result = astGetXmlPrefix( this );
+
+/* 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 int GetComment( AstChannel *this, int *status ) {
+/*
+* Name:
+* GetComment
+
+* Purpose:
+* Get the value of the Comment attribute of a Channel.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* int GetComment( AstChannel *this, int *status )
+
+* Class Membership:
+* XmlChan member function (over-rides the protected astGetComment
+* method inherited from the Channel class).
+
+* Description:
+* This function returns the value of the Comment attribute of the XmlChan.
+* It changs the default value from 1 (provided by the parent Channel
+* class) to zero.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* - The Comment value.
+*/
+
+ return astTestComment( this ) ? (*parent_getcomment)( this, status ) : 0;
+}
+
+static int GetFull( AstChannel *this, int *status ) {
+/*
+* Name:
+* GetFull
+
+* Purpose:
+* Get the value of the Full attribute of a Channel.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* int GetFull( AstChannel *this, int *status )
+
+* Class Membership:
+* XmlChan member function (over-rides the protected astGetFull
+* method inherited from the Channel class).
+
+* Description:
+* This function returns the value of the Full attribute of the XmlChan.
+* It changs the default value from zero (provided by the parent Channel
+* class) to -1.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* - The Full value.
+*/
+
+ return astTestFull( this ) ? (*parent_getfull)( this, status ) : -1;
+}
+
+static int GetIndent( AstChannel *this, int *status ) {
+/*
+* Name:
+* GetIndent
+
+* Purpose:
+* Get the value of the Indent attribute for an XmlChan.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "XmlChan.h"
+* int GetIndent( AstChannel *this, int *status )
+
+* Class Membership:
+* XmlChan 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 XmlChan.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* 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 char GetNextChar( void *data, int *status ) {
+/*
+* Name:
+* GetNextChar
+
+* Purpose:
+* Get the next character from the XML source.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* char GetNextChar( void *data, int *status )
+
+* Class Membership:
+* XmlChan member function
+
+* Description:
+* This function returns the next character from the XML source,
+* getting a new string if necessary.
+
+* Parameters:
+* data
+* Pointer to a structure holding data needed to perform the read.
+* This should be a pointer to the XmlChan being read. If NULL is
+* supplied, then any internal resources are freed and a value of
+* zero is returned.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* - The next source character, or zero if NULL is supplied for "data".
+
+* Notes:
+* - Zero is returned if there is no more text to read.
+* - Zero is returned if an error has already occurred, or if this
+* function should failed for any reason.
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+ AstXmlChan *this; /* Pointer to the XmlChan */
+ char result; /* The returned character */
+
+/* Initiialise */
+ result = 0;
+
+/* Get a pointer to the XmlChan. */
+ this = (AstXmlChan *) data;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(this);
+
+/* If a NULL pointer is supplied free any memory holding text already
+ read from the source, and return zero. */
+ if( !data ) {
+ getnextchar_buf = astFree( getnextchar_buf );
+ return 0;
+ }
+
+/* Check the global status */
+ if( !astOK ) return result;
+
+/* We read a new line from the source if: 1) the reset flag is set in the
+ XmlChan, 2) we have reached the terminating null in the previous line,
+ or 3) we do not yet have a line of text. */
+ if( this->reset_source || *getnextchar_c == 0 || !getnextchar_buf ) {
+ this->reset_source = 0;
+
+/* Free the memory used to hold any previous text. */
+ if( getnextchar_buf ) getnextchar_buf = astFree( getnextchar_buf );
+
+/* Read a new line of text from the source. */
+ getnextchar_buf = astGetNextText( this );
+
+/* Read a new line if the previous line was empty. */
+ while( getnextchar_buf && !getnextchar_buf[ 0 ] ) {
+ astFree( getnextchar_buf );
+ getnextchar_buf = astGetNextText( this );
+ }
+
+/* Reset the pointer to the next character to the start of the new
+ string. */
+ getnextchar_c = getnextchar_buf;
+
+/* If all has gone OK, return the first character and then increment getnextchar_c to
+ point to the next character. */
+ if( getnextchar_c && astOK ) result = *(getnextchar_c++);
+
+/* If we are reading a previously read line, return the character located
+ by getnextchar_c and increment getnextchar_c. */
+ } else {
+ result = *(getnextchar_c++);
+ }
+
+/* Return the result */
+ return result;
+
+}
+
+static const char *GetTag( AstXmlObject *this, int opening, int *status ){
+/*
+* Name:
+* GetTag
+
+* Purpose:
+* Returns a string holding an XML tag describing the given XmlObject.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* const char *GetTag( AstXmlObject *this, int opening, int *status )
+
+* Description:
+* This function returns a pointer to static string
+* containing an XML tag describing the given XmlObject. It is a
+* wrapper for the astXmlGetTag function defined in xml.h, but it
+* additionally removes any "definedby" attribute before formating the
+* tag (the "definedby" attribute is added by the ReadClassData
+* function and is not part of the XML text read from the source).
+
+* Parameters:
+* this
+* Pointer to the XmlObject.
+* opening
+* Indicates which tag is to be returned; the start tag or the end
+* tag. If non-zero the start tag is returned. Otherwise, the
+* end tag is returned. If the supplied XmlObject has no end
+* tag (i.e. if it is an empty element, or if it is not an element),
+* then NULL is returned but no error is reported.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Pointer to a null terminated static string holding the tag.
+
+* Notes:
+* - Empty elements are represented as an start tag of the form <.../>,
+* with no corresponding end tag.
+* - NULL is returned if an error has already occurred, or if this
+* function should fail for any reason.
+*-
+*/
+
+/* Local Variables: */
+ AstXmlElement *elem; /* Pointer to XML element */
+ const char *result; /* The returned pointer */
+ const char *ptr; /* The value of the "definedby" attribute */
+ const char *class; /* Copy of the value of the "definedby" attribute */
+
+/* Initialise */
+ result = NULL;
+
+/* If the object is an element, check for the "definedby" attribute. */
+ if( astXmlCheckType( this, AST__XMLELEM ) ) {
+ elem = (AstXmlElement *) this;
+
+/* See if the element contains a "definedby" attribute. */
+ ptr = astXmlGetAttributeValue( elem, DEFINEDBY );
+
+/* If so, temporarily remove it, format the tag and then put it back. */
+ if( ptr ) {
+ class = astStore( NULL, ptr, strlen( ptr ) + 1 );
+ astXmlRemoveAttr( elem, DEFINEDBY, NULL );
+ result = astXmlGetTag( elem, opening );
+ astXmlAddAttr( elem, DEFINEDBY, class, NULL );
+ class = astFree( (void *) class );
+
+/* If not, just use astXmlGetTag. */
+ } else {
+ result = astXmlGetTag( this, opening );
+ }
+
+/* If the object is not an element, just use astXmlGetTag. */
+ } else {
+ result = astXmlGetTag( this, opening );
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static AstRegion *IntersectionReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, int *status ){
+/*
+* Name:
+* IntersectionReader
+
+* Purpose:
+* Make an AST Region from an IVOA Intersection region element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *IntersectionReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* Intersection region element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Intersection region element.
+* frm
+* Pointer to the 2D Frame in which the returned Region should be
+* defined. If the Unit attribute is not set, this function will
+* set it to the value supplied in "unit" before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstRegion *new; /* Pointer to returned Region */
+ AstRegion *reg; /* Pointer to component Region */
+ AstRegion *tmp; /* Pointer to new Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *names[1]; /* Names of the subelements to be searched for */
+ int i; /* Loop count */
+ int max[1]; /* Max allowed occurrences of each name */
+ int min[1]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Search the supplied element for a Region sub-element. */
+ names[ 0 ] = "Intersection|Union|Negation|AllSky|Circle|Ellipse|Polygon|"
+ "Convex|Box";
+ min[ 0 ] = 2;
+ max[ 0 ] = INT_MAX;
+ scan = ScanIVOAElement( this, elem, 1, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Create Regions from all the component region elements, and combine
+ them into nested CmpRegions, using the boolean AND operator to combine
+ them. */
+ new = RegionReader( this, scan->el[0][0], frm, status );
+ for( i = 1; i < scan->count[0]; i++ ) {
+ reg = RegionReader( this, scan->el[0][i], frm, status );
+ tmp = (AstRegion *) astCmpRegion( new, reg, AST__AND, "", status );
+ reg = astAnnul( reg );
+ (void) astAnnul( new );
+ new = tmp;
+ }
+
+/* Get any fill factor from the element and assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Free resources */
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static int IsUsable( AstXmlElement *elem, int *status ){
+/*
+* Name:
+* IsUsable
+
+* Purpose:
+* See if an XmlElement could describe an AST object.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* int IsUsable( AstXmlElement *elem, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function checks if an instance of an AST class could be
+* created from the supplied XmlElement.
+
+* Parameters:
+* elem
+* A pointer to the XmlElement, or NULL.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* If an AST Object could be created from the supplied element, +1 is
+* returned. Otherwise, -1 is returned. Zero is returned if the supplied
+* pointer is NULL.
+
+* Notes:
+* - A value of zero 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 */
+ const char *class; /* Pointer to element name */
+ const char *uri; /* Pointer to namespace URI */
+ IVOAReader reader; /* Pointer to reader function */
+ int is_ivoa; /* Element belongs to an IVOA namespace? */
+ int oldrep; /* Original value of the Reporting flag */
+ int result; /* Result value to be returned */
+
+/* Check the global error status, and the supplied pointer. */
+ if ( !astOK || !elem ) return 0;
+
+/* Get a pointer to the structure holding thread-specific global data. */
+ astGET_GLOBALS(NULL);
+
+/* Initialise */
+ result = -1;
+
+/* See if the element is in a supported IVOA namespace, and has a reader
+ function for converting it to an AST object. If so, set the default
+ XmlFormat to IVOA, and set the result non-zero. */
+ reader = FindIVOAClass( elem, &is_ivoa, status );
+ if( is_ivoa ){
+ if( reader ) result = 1;
+ if( isusable_this ) isusable_this->formatdef = IVOA_FORMAT;
+ }
+
+/* If the element is not an IVOA class, only proceed if the URI is not
+ defined, or if it the AST URI. */
+ uri = astXmlGetURI( elem );
+ if( result == -1 && ( !uri || !strcmp( uri, AST__XMLNS ) ) ) {
+
+/* Get the element name. This will be an AST class name if the element
+ describes an AST Object. */
+ class = astXmlGetName( elem );
+
+/* Attempt to get the loader for a class of this name. If no loader exists an
+ error would normally be reported. Therefore we switch off error reporting
+ before making this call. After the class we clear any error status and
+ switch error reporting back on. If no error occurs whilst getting the
+ loader, then the class name must be a valid AST class name and so return
+ a non-zero result value. */
+ if( astOK ) {
+ oldrep = astReporting( 0 );
+ astGetLoader( class, status );
+ if( astOK ) {
+ result = 1;
+ } else {
+ astClearStatus;
+ }
+ astReporting( oldrep );
+ }
+
+/* If the element is in no namespace, use the AST URI as the default
+ namespace for it and its children. */
+ if( !uri ) astXmlAddURI( elem, NULL, AST__XMLNS );
+
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static AstObject *MakeAstFromXml( AstXmlChan *this, AstXmlElement *elem, int *status ) {
+/*
+* Name:
+* MakeAstFromXml
+
+* Purpose:
+* Make an AST Object from an XML element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstObject *MakeAstFromXml( AstXmlChan *this, AstXmlElement *elem, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Object from the supplied XML element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the XML element containing a description of the AST
+* object.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Object.
+*/
+
+/* Local Variables: */
+ AstLoaderType *loader; /* Pointer to loader for Object */
+ AstObject *new; /* Pointer to returned Object */
+ AstXmlParent *old_container; /* Element from which items are being read */
+ IVOAReader reader; /* Pointer to reader function */
+ const char *class; /* Pointer to Object class name string */
+ int is_ivoa; /* Element belongs to an IVOA namespace? */
+ int i; /* Index of content item */
+ int nitem; /* No. of items of content within element */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* First deal with IVOA format. */
+/* ---------------------------- */
+ if( astGetXmlFormat( this ) == IVOA_FORMAT ) {
+
+/* Get a pointer to a function which will produce an AST object from
+ elements of the class of the supplied element. */
+ reader = FindIVOAClass( elem, &is_ivoa, status );
+
+/* If found, invoke the function to create the new AST object. */
+ if( is_ivoa && reader ) {
+ new = ( *reader )( this, elem, status );
+
+/* IVOA reader functions do not remove used content as they are read
+ from the element (unlike AST native readers). Therefore empty the
+ element of all content now to indicate that the element contained no
+ unrecognised content. This prevents an error being reported. If there
+ was in fact any unrecognised content, then an error will already have
+ been reported. */
+ nitem = astXmlGetNitem( elem );
+ for( i = nitem - 1; i >= 0; i-- ) astXmlDelete( astXmlGetItem( elem, i ) );
+
+/* If not found, report an error. This should not happen since the IsUsable
+ function should already have checked that the element is usable. */
+ } else if( astOK ){
+ astError( AST__INTER, "astRead(XmlChan): MakeAstFromIVOA does not "
+ "support IVOA class \"%s\" (internal AST programming error).", status,
+ astXmlGetName( elem ) );
+ }
+
+/* Now deal with other (i.e. NATIVE and QUOTED) format. */
+/* ---------------------------------------------------- */
+ } else {
+
+/* Get the AST class name. This is the name of the XML element. */
+ class = astXmlGetName( elem );
+
+/* Use the associated class name to locate the loader for that
+ class. This function will then be used to build the Object. */
+ loader = astGetLoader( class, status );
+
+/* If OK, save the pointer to the current container element, and indicate
+ that the supplied element is now to be used as the current container.
+ The "current container" is the XML element from which values are being
+ read. */
+ if( astOK ) {
+ old_container = this->container;
+ this->container = (AstXmlParent *) elem;
+
+/* The "isa_class" item in the XmlChan structure contains a pointer to
+ the name of the class whose loader is currently being invoked. It is set
+ by the loader itself as a side effect of calling the astReadClassData
+ function. Initialise it to NULL to indicate that astReadClassData has
+ not yet been called. */
+ this->isa_class = NULL;
+
+/* Invoke the loader, which reads the Object definition from the
+ current XML container (i.e. the supplied XML element) and builds the
+ Object. Supply NULL/zero values to the loader so that it will substitute
+ values appropriate to its own class. */
+ new = (*loader)( NULL, (size_t) 0, NULL, NULL, (AstChannel *)
+ this, status );
+
+/* Re-instate the original container. */
+ this->container = old_container;
+ }
+ }
+
+/* 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 double MakeMJD( AstTimeFrame *frm, double time, int *status ) {
+/*
+* Name:
+* MakeMJD
+
+* Purpose:
+* Create an MJD value from a TimeFrame axis value.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* double MakeMJD( AstTimeFrame *frm, double time, int *status )
+
+* Class Membership:
+* XmlChan member function
+
+* Description:
+* This function converts the supplied time value from the system
+* represented by the supplied TimeFrame into an absolute TBD MJD,
+* in units of days.
+
+* Parameters:
+* frm
+* Pointer to the TimeFrame defining the system in which "time" is
+* supplied.
+* time
+* The time value to convert.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The equivalent MJD value.
+
+*/
+
+/* Local Variables: */
+ AstFrameSet *fs;
+ AstTimeFrame *cfrm;
+ double result;
+
+/* Initialise. */
+ result = AST__BAD;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Create a copy of the supplied TimeFrame, and set its attributes to
+ describe the required MJD system. */
+ cfrm = astCopy( frm );
+ astSetSystem( cfrm, AST__MJD );
+ astSetUnit( cfrm, 0, "d" );
+ astSetTimeScale( cfrm, AST__TDB );
+ astSetTimeOrigin( cfrm, 0.0 );
+
+/* Find the Mapping from the supplied TimeFrame to this TimeFrame. Use it to
+ transform the supplied time value */
+ fs = astConvert( frm, cfrm, "" );
+ if( fs ) {
+ astTran1( fs, 1, &time, 1, &result );
+
+/* Free resources */
+ fs = astAnnul( fs );
+ }
+ cfrm = astAnnul( cfrm );
+
+/* Result */
+ return result;
+
+}
+
+static AstXmlElement *MakePos2D( AstXmlChan *this, AstXmlElement *elem, int *status ){
+/*
+* Name:
+* MakePos2D
+
+* Purpose:
+* Create an STC Position2D element from the supplied Position3D.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstXmlElement *MakePos2D( AstXmlChan *this, AstXmlElement *elem, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function returns a pointer to a Position2D element by throwing
+* away the last axis in the supplied Position3D element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the Position3D element.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Position2D element.
+
+
+*/
+
+/* Local Variables: */
+ AstXmlElement *el; /* Pointer to subelement */
+ AstXmlElement *new; /* Pointer to returned XmlElement */
+ IVOAScan *scan; /* Structure holding scan results */
+ char **words; /* Array of words read from string */
+ char *unit2; /* New Unit string */
+ char buff[100]; /* Text buffer */
+ const char *names[3]; /* Names of the subelements to be searched for */
+ const char *unit; /* Unit string */
+ double pos[3]; /* Values read from Position3D */
+ int i; /* Loop count */
+ int l1; /* Length of word 1 */
+ int l2; /* Length of word 2 */
+ int max[3]; /* Max allowed occurrences of each name */
+ int min[3]; /* Min allowed occurrences of each name */
+ int n; /* Number of words read from string */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* If the supplied element is not a Position3D just copy it. */
+ if( strcmp( astXmlGetName( elem ), "Position3D" ) ) {
+ new = (AstXmlElement *) astXmlCopy( elem );
+
+/* Otherwise, we create a new Position2D and add required content to it. */
+ } else {
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "Name";
+ names[ 1 ] = "Error3";
+ names[ 2 ] = "Value3";
+ max[ 0 ] = 1;
+ max[ 1 ] = 2;
+ max[ 2 ] = 1;
+ min[ 0 ] = 1;
+ min[ 1 ] = 0;
+ min[ 2 ] = 0;
+ scan = ScanIVOAElement( this, elem, 3, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Create an empty XML element with name "Position2D". */
+ new = astXmlAddElement( NULL, "Position2D", NULL );
+
+/* Get the units attribute from the supplied element. These are the units
+ of the positional axis values. Copy the first 2 words to the unit
+ attribute of the new element. */
+ unit = astXmlGetAttributeValue( elem, "unit" );
+ if( unit ) {
+ words = astChrSplit( unit, &n );
+ if( words ) {
+ if( n > 2 ) {
+ l1 = strlen( words[ 0 ] );
+ l2 = strlen( words[ 1 ] );
+ unit2 = astMalloc( l1 + l2 + 2 );
+ if( unit2 ) {
+ strcpy( unit2, words[ 0 ] );
+ unit2[ l1 ] = ' ';
+ strcpy( unit2 + l1 + 1, words[ 1 ] );
+ unit2[ l1 + l2 + 1 ] = 0;
+ astXmlAddAttr( new, "unit", unit2, NULL );
+ unit2 = astFree( unit2 );
+ }
+ } else {
+ astXmlAddAttr( new, "unit", unit, NULL );
+ }
+
+ if( words ) {
+ for( i = 0; i < n; i++ ) words[ i ] = astFree( words[ i ] );
+ words = astFree( words );
+ }
+ }
+ }
+
+/* If this Position3D contains a Name which can be read, obtain it
+ and store it in the returned Position2D. */
+ if( scan->count[ 0 ] > 0 ) {
+ el = astXmlAddElement( new, "Name", NULL );
+ astXmlAddCharData( el, 0, astXmlGetValue( scan->el[ 0 ][ 0 ], 0 ) );
+ }
+
+/* If this Position3D contains a Value which can be read, obtain it,
+ format the first 2 values and store in the returned Position2D. */
+ if( scan->count[ 2 ] > 0 ) {
+ ElemListD( this, scan->el[ 2 ][ 0 ], 3, pos, status );
+ el = astXmlAddElement( new, "Value2", NULL );
+ sprintf( buff, "%.*g %.*g", AST__DBL_DIG, pos[0], AST__DBL_DIG, pos[1] );
+ astXmlAddCharData( el, 0, buff );
+ }
+
+/* If this Position3D contains an Error which can be read, obtain it,
+ format the first 2 values and store in the returned Position2D. */
+ if( scan->count[ 1 ] > 0 ) {
+ ElemListD( this, scan->el[ 1 ][ 0 ], 3, pos, status );
+ el = astXmlAddElement( new, "Error2", NULL );
+ sprintf( buff, "%.*g %.*g", AST__DBL_DIG, pos[0], AST__DBL_DIG, pos[1] );
+ astXmlAddCharData( el, 0, buff );
+ }
+
+/* Free resources */
+ scan = FreeIVOAScan( scan, status );
+ }
+ }
+
+/* Return the result.*/
+ return new;
+
+}
+
+static AstRegion *NegationReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, int *status ){
+/*
+* Name:
+* NegationReader
+
+* Purpose:
+* Make an AST Region from an IVOA Negation region element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *NegationReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* Negation region element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Negation region element.
+* frm
+* Pointer to the 2D Frame in which the returned Region should be
+* defined. If the Unit attribute is not set, this function will
+* set it to the value supplied in "unit" before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstRegion *new; /* Pointer to returned Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *names[1]; /* Names of the subelements to be searched for */
+ int max[1]; /* Max allowed occurrences of each name */
+ int min[1]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Search the supplied element for a Region sub-element. */
+ names[ 0 ] = "Intersection|Union|Negation|AllSky|Circle|Ellipse|Polygon|"
+ "Convex|Box";
+ min[ 0 ] = 1;
+ max[ 0 ] = 1;
+ scan = ScanIVOAElement( this, elem, 1, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Create a Region from the component region element, and negate it. */
+ new = RegionReader( this, scan->el[0][0], frm, status );
+ astNegate( new );
+
+/* Get any fill factor from the element and assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Free resources */
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static AstObject *ObsDataLocationReader( AstXmlChan *this,
+ AstXmlElement *elem, int *status ) {
+/*
+* Name:
+* ObsDataLocationReader
+
+* Purpose:
+* Make an AST Object from an IVOA ObsDataLocationReader element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstObject *ObsDataLocationReader( AstXmlChan *this,
+* AstXmlElement *elem, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Object from the supplied IVOA
+* ObsDataLocationReader element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA ObsDataLocationReader element.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Object.
+*/
+
+/* Local Variables: */
+ AstPointList *obs; /* PointList defining the observatory position */
+ AstStcObsDataLocation *stc; /* Pointer to returned Object */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *names[2]; /* Names of the subelements to be searched for */
+ int max[2]; /* Max allowed occurrences of each name */
+ int min[2]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ stc = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return (AstObject *) stc;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "ObservatoryLocation";
+ names[ 1 ] = "ObservationLocation";
+ min[ 0 ] = 1;
+ min[ 1 ] = 1;
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ scan = ScanIVOAElement( this, elem, 2, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Read the observation location. */
+ stc = (AstStcObsDataLocation *) StcMetadataReader( this, scan->el[ 1 ][ 0 ], status );
+
+/* Read the observatory location, returning a Pointlist describing the
+ observatory position (if possible), and modifiying the observation
+ Region by (if possible) assigning the observatory location to the
+ ObsLon and ObsLat attributes of any SpecFrames in the Region, and the
+ ObsLon and ObsLat attributes of any TimeFrames in the Region. */
+ obs = ObservatoryLocationReader( this, scan->el[ 0 ][ 0 ], stc, status );
+ if( obs ) {
+ astStcSetObs( stc, obs );
+ obs = astAnnul( obs );
+ }
+
+/* Free resources. */
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Return the pointer to the new Object. */
+ return (AstObject *) stc;
+}
+
+static AstPointList *ObservatoryLocationReader( AstXmlChan *this,
+ AstXmlElement *elem,
+ AstStcObsDataLocation *obs, int *status ){
+/*
+* Name:
+* ObservatoryLocationReader
+
+* Purpose:
+* Make an AST PointList from an IVOA ObservatoryLocationReader element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstPointList *ObservatoryLocationReader( AstXmlChan *this,
+* AstXmlElement *elem,
+* AstStcObsDataLocation *obs, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST PointList from the supplied IVOA
+* ObservatoryLocationReader element, and also modifies the supplied
+* StcObsDataLocation so that the ObsLon and ObsLat attributes hold
+* the observatory position (if appropriate).
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA ObservatoryLocation element.
+* obs
+* Pointer to the StcObsDataLocation in which to store the
+* observatory position (if terrestrial).
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new PointList.
+
+*/
+
+/* Local Constants: */
+#define A0 6378140.0 /* Earth equatorial radius (metres) */
+
+/* Local Variables: */
+ AstFrame *frm; /* Pointer to obsvtory lon/lat Frame */
+ AstFrame *obs_frm; /* Pointer to obsvation lon/lat Frame */
+ AstFrame *pfrm; /* Pointer to axis primary Frame */
+ AstKeyMap *km; /* KeyMap holding AstroCoords info */
+ AstObject *new; /* Pointer to returned Region */
+ AstObject *o; /* Pointer to retrieved Region */
+ AstPointSet *ps; /* Pointer to PointSet holding obs lon/lat */
+ AstRegion *err; /* Pointer to error Region */
+ AstStc *stc; /* Pointer to Observatory location stc */
+ char setting[ 100 ]; /* Attribute setting string */
+ const char *dom; /* Domain string */
+ double **ptr; /* Pointers to axis values for obs lon/lat */
+ double lambda; /* Geodetic longitude radians */
+ double phi; /* Geodetic latitude radians */
+ double lon; /* Geocentric longitude radians */
+ double lat; /* Geocentric latitude radians */
+ int i; /* Index of Frame axis */
+ int nax; /* Number of Frame axes */
+ int paxis; /* Index of primary Frame axis */
+
+/* Check the global error status. */
+ if ( !astOK ) return NULL;
+
+/* Initialise */
+ new = NULL;
+
+/* Read the ObservatoryLocation as an StcMetadata element (it will be
+ represented by a NullRegion). */
+ stc = (AstStc *) StcMetadataReader( this, elem, status );
+
+/* Extract the first AstroCoords KeyMap from the stc. */
+ if( astGetStcNCoord( stc ) == 0 ) {
+ Report( this, elem, FAILURE, "contains no observatory position", status );
+ } else {
+ km = astGetStcCoord( stc, 1 );
+
+/* Extract the PointList holding the axis values from the KeyMap. */
+ if( !astMapGet0A( km, AST__STCVALUE, &new ) ){
+ Report( this, elem, FAILURE, "contains no observatory position", status );
+
+/* Extract any position uncertainty, and store as the uncertainty in the
+ value PointList. */
+ } else if( astMapGet0A( km, AST__STCERROR, &o ) ){
+ err = (AstRegion *) o;
+ astSetUnc( new, err );
+
+/* Free resources */
+ err = astAnnul( err );
+ }
+ km = astAnnul( km );
+ }
+ stc = astAnnul( stc );
+
+/* Check the Region is a PointList. */
+ if( !astIsAPointList( new ) && astOK ) {
+ astError( AST__INTER, "ObservatoryLocationReader(XmlChan): The "
+ "observatory location is described by a %s rather than "
+ "a PointList (internal AST programming error).", status,
+ astGetClass( new ) );
+ }
+
+/* If possible, we use the observatory location to set the value of the
+ ObsLon and ObsLat attributes of any SpecFrames, and the ObsLon and
+ ObsLat attributes of any TimeFrames, in the supplied ObsDataLocation.
+ For this to be possible, the PointList being returned must represent
+ either geodetic or geocentric longitude/latitude. If it is geocentric,
+ the values need to be converted to geodetic. */
+ ps = astRegTransform( new, NULL, 1, NULL, &frm );
+ ptr = astGetPoints( ps );
+ if( ptr ){
+ nax = astGetNaxes( frm );
+ lon = AST__BAD;
+ lat = AST__BAD;
+ lambda = AST__BAD;
+ phi = AST__BAD;
+ for( i = 0; i < nax; i++ ) {
+ astPrimaryFrame( frm, i, &pfrm, &paxis );
+ dom = astGetDomain( pfrm );
+ if( dom ) {
+ if( !strcmp( dom, "GEO_C" ) ){
+ if( lon == AST__BAD ) {
+ lon = ptr[i][0];
+ astSetLabel( pfrm, 0, "Geodetic longitude" );
+ } else {
+ lat = ptr[i][0];
+ astSetLabel( pfrm, 1, "Geodetic latitude" );
+ astSetDomain( pfrm, "GEO_D" );
+ }
+
+ } else if( !strcmp( dom, "GEO_D" ) ){
+ if( lambda == AST__BAD ) {
+ lambda = ptr[i][0];
+ } else {
+ phi = ptr[i][0];
+ }
+ }
+ }
+ pfrm = astAnnul( pfrm );
+ }
+
+/* If required, convert from geocentric lon/lat to geodetic lon/lat. */
+ if( lon != AST__BAD ) {
+ double pos[ 3 ], height;
+
+/* Convert the supplied geocentric lon/lat to terrestrial Cartesian
+ (x,y,z) values. The (x,y,z) system has origin at the centre of the
+ earth, Z axis going through the north pole, X axis at (long,lat)=(0,0),
+ and Y axis at (long,lat) = (E90,0). Assume an equatorial sea level
+ position. */
+ palDcs2c( lon, lat, pos );
+ pos[ 0 ] *= A0;
+ pos[ 1 ] *= A0;
+ pos[ 2 ] *= A0;
+
+/* Get the corresponding geodetic lon/lat. */
+ eraGc2gd( 1, pos, &lambda, &phi, &height );
+ }
+
+ if( lambda != AST__BAD ) {
+ obs_frm = astGetFrame( ((AstRegion *) obs)->frameset, AST__CURRENT );
+ nax = astGetNaxes( obs );
+ for( i = 0; i < nax; i++ ) {
+ astPrimaryFrame( obs_frm, i, &pfrm, &paxis );
+ if( astIsASpecFrame( pfrm ) ) {
+ sprintf( setting, "ObsLon(%d)=%.*g", i + 1, AST__DBL_DIG, lambda*AST__DR2D );
+ astRegSetAttrib( obs, setting, NULL );
+ sprintf( setting, "ObsLat(%d)=%.*g", i + 1, AST__DBL_DIG, phi*AST__DR2D );
+ astRegSetAttrib( obs, setting, NULL );
+ } else if( astIsATimeFrame( pfrm ) ) {
+ sprintf( setting, "ObsLon(%d)=%.*g", i + 1, AST__DBL_DIG, lambda*AST__DR2D );
+ astRegSetAttrib( obs, setting, NULL );
+ sprintf( setting, "ObsLat(%d)=%.*g", i + 1, AST__DBL_DIG, phi*AST__DR2D );
+ astRegSetAttrib( obs, setting, NULL );
+ }
+ pfrm = astAnnul( pfrm );
+ }
+ obs_frm = astAnnul( obs_frm );
+ }
+ }
+
+/* Free resources */
+ frm = astAnnul( frm );
+ ps = astAnnul( ps );
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return (AstPointList *) new;
+}
+#undef A0
+
+static void OutputText( AstXmlChan *this, const char *text, int mxlen, int *status ) {
+/*
+* Name:
+* OutputText
+
+* Purpose:
+* Write a string to the sink.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* void OutputText( AstXmlChan *this, const char *text, int mxlen, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function writes the supplied text to the output sink,
+* splitting it into lines of no more than "mxlen" characters, if
+* required.
+
+* Parameters:
+* this
+* A pointer to the XmlChan.
+* text
+* Pointer to the (potentially long) null terminated string to write
+* out to the sink.
+* mxlen
+* The maximum allowed output line length. If zero, no limit is
+* placed on the output line length and the supplied text is always
+* written out as a single string.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ char *breakat; /* Pointer to terminating character */
+ char *c; /* Pointer to start of next chunk */
+ char *lastend; /* Pointer to last closing quote */
+ char *lastspace; /* Pointer to last whitespace character */
+ char *linestart; /* Pointer to start of current line */
+ char quote; /* Opening quote character */
+ char tt; /* Character temporarily replaced by 0 */
+ int len; /* Length of current line */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* If "mxlen" is zero, just output the string as supplied. */
+ if( mxlen < 1 ) {
+ astPutNextText( this, text );
+
+/* Otherwise, output the string split up into lines */
+ } else {
+ c = (char *) text - 1;
+ quote = 0;
+ lastend = NULL;
+ lastspace = NULL;
+ len = 0;
+ linestart = (char *) text;
+
+/* Loop round each character in the text */
+ while( *(++c ) ){
+
+/* Note if we are currently inside a quoted string. Remember the quote
+ character (" or ') so that we can look out for the corresponding
+ closing quote. Note the position of the previous quote end. */
+ if( !quote ) {
+ if( *c == '\"' || *c == '\'' ) quote = *c;
+ } else {
+ if( *c == quote ) {
+ quote = 0;
+ lastend = c;
+ }
+ }
+
+/* Note the position of hte previous space. */
+ if( isspace( *c ) ) lastspace = c;
+
+/* If we have exceeded the maximum allowed line length, split it. If we
+ are inside a quote, we split it at the last quote end (if any). If we
+ are not in a quote, we split it at the last space, or the last quote
+ end (which ever is closest). To cover the case where the end quote is
+ first character beyoind the limit, reduce the limit a bit. */
+ len++;
+ if( len >= mxlen - 2 ) {
+ if( !quote || !lastend ) {
+ if( lastend && lastspace ){
+ breakat = ( lastend > lastspace ) ? lastend + 1: lastspace;
+ } else if( lastend ){
+ breakat = lastend + 1;
+ } else if( lastspace ){
+ breakat = lastspace;
+ } else {
+ breakat = c;
+ }
+ } else {
+ breakat = lastend + 1;
+ }
+ } else {
+ breakat = NULL;
+ }
+
+/* If we have a line break, output the current line. */
+ if( breakat ) {
+
+/* Terminate the string, first saving the character which is replaced by the
+ terminating zero so that it can be re-instated later. */
+ tt = *breakat;
+ *breakat = 0;
+
+/* Write out the newly terminated chunk. */
+ astPutNextText( this, linestart );
+
+/* Move on to ths start of the next chunk, decrement the number of characters
+ remaining, and re-instate the character previously over-written by
+ zero. */
+ c = breakat;
+ linestart = c;
+ *c = tt;
+ len = 0;
+ quote = 0;
+ }
+ }
+
+/* Write out any remaining text (this will be less than "mxlen"
+ characters long)*/
+ if( linestart && *linestart ) astPutNextText( this, linestart );
+ }
+}
+
+static AstRegion *PolygonReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, int *status ){
+/*
+* Name:
+* PolygonReader
+
+* Purpose:
+* Make an AST Region from an IVOA Polygon element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *PolygonReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* Polygon element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Polygon element.
+* frm
+* Pointer to the 2D Frame in which the returned Region should be
+* defined. If the Unit attribute is not set, this function will
+* set it to the value supplied in "unit" before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstFrame *cfrm;
+ AstMapping *map;
+ AstRegion *new;
+ IVOAScan *scan;
+ const char *names[1];
+ const char *unit;
+ const char *funit;
+ double *pos;
+ double *x0;
+ double *y0;
+ double lbnd[2];
+ double ubnd[2];
+ int axcon;
+ int axlon;
+ int i;
+ int is_box;
+ int is_sky;
+ int laxcon;
+ int max[1];
+ int min[1];
+ int nv;
+ int small[ 4 ];
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Scan the supplied element for the required sub-elements */
+ names[ 0 ] = "Vertex";
+ min[ 0 ] = 1;
+ max[ 0 ] = INT_MAX;
+ scan = ScanIVOAElement( this, elem, 1, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* See if the Frame is a SkyFrame, and if so, get the index of the
+ longitude axis. */
+ is_sky = astIsASkyFrame( frm );
+ axlon = is_sky ? astGetLonAxis( (AstSkyFrame *) frm ) : -1;
+
+/* Get the units attribute from the supplied element. These are the units
+ of the vertex axis values. */
+ unit = astXmlGetAttributeValue( elem, "unit" );
+ if( !unit ) {
+ Report( this, elem, FAILURE, "contains no unit attribute", status );
+ unit = "";
+ }
+
+/* Create an array to hold the axis values at the vertices. */
+ nv = scan->count[0];
+ pos = astMalloc( sizeof( double )*(size_t) (2*nv) );
+
+/* Read each vertex element in turn. Record whether or not the first 4
+ vertices have <SmallCircles>. */
+ x0 = pos;
+ y0 = x0 + nv;
+ for( i = 0; i < nv; i++, x0++, y0++ ) {
+ small [ i % 4 ] = VertexReader( this, scan->el[0][i], x0, y0, status );
+ }
+
+/* Reset the pointers so that they point to the first x and y values. */
+ x0 = pos;
+ y0 = x0 + nv;
+
+/* Since the SkyFrame class does not have active Units we must handle it
+ separately. Convert the axis values from the supplied units (e.g.
+ degrees) to radians. */
+ if( is_sky ) {
+ map = astUnitMapper( unit, "rad", NULL, NULL );
+ if( map ) {
+ astTran1( map, nv*2, pos, 1, pos );
+ map = astAnnul( map );
+ } else if( astOK ) {
+ Report( this, elem, FAILURE, "contains unusable units", status );
+ }
+ }
+
+/* If there are 4 vertices we may be able to create an AST Box (not the
+ same as an STC Box) instead of a Polygon.*/
+ is_box = 0;
+ ubnd[0] = x0[ 0 ];
+ lbnd[0] = ubnd[0];
+ ubnd[1] = y0[ 0 ];
+ lbnd[1] = ubnd[1];
+
+ if( nv == 4 ) {
+
+/* See if the edge which ends at the 1st vertex has a constant value on
+ either axis. Is so, note the index of the axis which is held constant. */
+ is_box = 1;
+ if( is_sky && small[ 0 ] ) {
+ laxcon = 1 - axlon;
+
+ } else if( astEQUAL( x0[ 0 ], x0[ 3 ] ) ) {
+ laxcon = 0;
+
+ } else if( astEQUAL( y0[ 0 ], y0[ 3 ] ) ) {
+ laxcon = 1;
+
+ } else {
+ is_box = 0;
+ }
+
+/* If the first edge represents a constant axis value, see if the others
+ do too (ensuring that the axes which are held constant alternate). Also
+ find the bounds of the box.*/
+ if( is_box ) {
+ for( i = 1; i < 4; i++ ) {
+ if( is_sky && small[ i ] ) {
+ axcon = 1 - axlon;
+
+ } else if( astEQUAL( x0[ i ], x0[ i - 1 ] ) ) {
+ axcon = 0;
+
+ } else if( astEQUAL( y0[ i ], y0[ i - 1 ] ) ) {
+ axcon = 1;
+
+ } else {
+ is_box = 0;
+ break;
+ }
+
+ if( axcon != 1 - laxcon ) {
+ is_box = 0;
+ break;
+ }
+
+ if( x0[ i ] > ubnd[0] ) {
+ ubnd[0] = x0[ i ];
+
+ } else if( x0[ i ] < lbnd[0] ) {
+ lbnd[0] = x0[ i ];
+ }
+
+ if( y0[ i ] > ubnd[1] ) {
+ ubnd[1] = y0[ i ];
+
+ } else if( y0[ i ] < lbnd[1] ) {
+ lbnd[1] = y0[ i ];
+ }
+
+ laxcon = axcon;
+ }
+ }
+ }
+
+/* Since the SkyFrame class does not have active Units we must handle it
+ separately. The axis values have already been converted from the supplied
+ units (e.g. degrees) to radians. */
+ if( is_sky ) {
+
+/* Create the Polygon or Box within the SkyFrame. */
+ if( is_box ) {
+ new = (AstRegion *) astBox( frm, 1, lbnd, ubnd, NULL, "", status );
+ } else {
+ new = (AstRegion *) astPolygon( frm, nv, nv, pos, NULL, "", status );
+ }
+
+/* Now handles Polygons in Frames other than SkyFrames. */
+ } else {
+
+/* Take a copy of the supplied Frame and set its Units to the value
+ obtained from the supplied element. */
+ cfrm = astCopy( frm );
+ astSetUnit( cfrm, 0, unit );
+ astSetUnit( cfrm, 1, unit );
+
+/* Create the Polygon or Box within the SkyFrame. */
+ if( is_box ) {
+ new = (AstRegion *) astBox( cfrm, 1, lbnd, ubnd, NULL, "", status );
+ } else {
+ new = (AstRegion *) astPolygon( cfrm, nv, nv, pos, NULL, "", status );
+ }
+
+/* If the Unit of this Region differs from that of the supplied Frame,
+ set it to the Unit of the supplied Frame. This will cause the
+ encapsulated limits to be mapped into the new Unit. If the supplied
+ Frame had no set Unit, set it to the units obtained from the supplied
+ element. */
+ for( i = 0; i < 2; i++ ) {
+ if( astTestUnit( frm, i ) ) {
+ funit = astGetUnit( frm, i );
+ if( strcmp( funit, unit ) ) astSetUnit( new, i, funit );
+ } else {
+ astSetUnit( frm, i, unit );
+ }
+ }
+
+/* Free resources */
+ cfrm = astAnnul( cfrm );
+ }
+
+/* Get any fill factor and lo/hi_include attributes from the element and
+ assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Free resources */
+ pos = astFree( pos );
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static double PosAngleReader( AstXmlChan *this, AstXmlElement *elem, int *status ){
+/*
+* Name:
+* PosAngleReader
+
+* Purpose:
+* Read an AST position angle value from an IVOA PosAngle element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* double PosAngleReader( AstXmlChan *this, AstXmlElement *elem, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function reads the supplied PosANgle element and returns a value
+* representing a position angle in the AST convention (radians from
+* +ve second axis to +ve first axis).
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Ellipse element.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The position angle.
+
+*/
+
+/* Local Variables: */
+ char buff[ 200 ]; /* Message buffer */
+ const char *paunit; /* Position angle unit string */
+ const char *ref; /* Reference axis string */
+ double result; /* Position angle value */
+
+/* Check the global error status. */
+ if ( !astOK ) return 0.0;
+
+/* Get the numerical position angle stored in the supplied PosAngle
+ element. */
+ result = ElemValueD( this, elem, 0.0, status );
+
+/* Get the units attribute from the supplied element. These are the units
+ of the above value. Default is degrees. */
+ paunit = astXmlGetAttributeValue( elem, "unit" );
+
+/* Convert the position angle to radians. */
+ if( !paunit || !strcmp( paunit, "deg" ) ) {
+ result *= AST__DD2R;
+
+ } else if( !strcmp( paunit, "h" ) ) {
+ result *= 15*AST__DD2R;
+
+ } else if( !strcmp( paunit, "arcmin" ) ) {
+ result *= AST__DD2R/60.0;
+
+ } else if( !strcmp( paunit, "arcsec" ) ) {
+ result *= AST__DD2R/3600.0;
+
+ } else {
+ sprintf( buff, "contains unusable angle units \"%s\"", paunit );
+ Report( this, elem, FAILURE, buff, status );
+ }
+
+/* Get the reference attribute from the supplied element. This indicates
+ the sense and origin of the stored angle value. Convert the result
+ to the AST convention, which is the equivalent of "Y" (which is the same
+ as "North"). "X" means "from X to Y", "Y" means "from Y to X". Default
+ is "X". */
+ ref = astXmlGetAttributeValue( elem, "reference" );
+ if( !ref || !Ustrcmp( ref, "X", status ) ) {
+ result = AST__DPIBY2 - result;
+
+ } else if( Ustrcmp( ref, "Y", status ) && Ustrcmp( ref, "North", status ) ) {
+ sprintf( buff, "contains unusable reference attribute \"%s\" "
+ "(will assume \"Y\" instead)", ref );
+ Report( this, elem, WARNING, buff, status );
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static AstRegion *Position2DReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, double *pos,
+ AstKeyMap **anc, int *status ){
+/*
+* Name:
+* Position2DReader
+
+* Purpose:
+* Modify a Frame to take account of an STC <Position2D> element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *Position2DReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, double *pos, int axis,
+* AstKeyMap **anc, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function reads the supplied STC <Position2D> element, and uses it,
+* if possible, to create the uncertainty associated with the spatial
+* axis in the supplied Frame.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Position2D element.
+* frm
+* Pointer to the 2D spatial Frame.
+* pos
+* Pointer to an array in which to return the spatial axis values
+* represented by the supplied Position2D element. This array is
+* returned filled with AST__BAD If the supplied Position2D element
+* does not contain any axis values.
+* anc
+* Address of a location at which to put a pointer to a newly
+* created KeyMap. This KeyMap will contain ancillary information
+* from the Position2D. The keys identify the item of ancillary
+* information (Name, Value, Error, Resolution, Size, Pixel Size).
+* The value associated with the Name key is string containing
+* the Name item from the Position2D. The value
+* associated with each of the other keys is a pointer to a 2D Region
+* within the supplied Frame, corresponding to the value, error,
+* resolution, etc. Keys will not be present in the returned KeyMap
+* if the corresponding item of ancillary information is not present
+* in the Position2D. A NULL pointer is returned if there is no
+* ancillary information at all.
+* status
+* Pointer to the inherited status variable.
+
+* Returned:
+* The uncertainty Region, or NULL if the supplied Position2D element
+* does not specify an uncertainty.
+
+*/
+
+/* Local Variables: */
+ AstMapping *map1; /* Mapping from first axis units to radians */
+ AstMapping *map2; /* Mapping from second axis units to radians */
+ AstPointSet *pset; /* Pointset holding Position2D axis values */
+ AstRegion *r; /* Region to store in ancillary KeyMap */
+ AstRegion *result; /* Returned uncertainty Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ char **words; /* Array of words extracted from string */
+ const char *name; /* Pointer to XML element name */
+ const char *names[6]; /* Names of the subelements to be searched for */
+ const char *unit1; /* Pointer to axis 1 unit attribute string */
+ const char *unit2; /* Pointer to axis 2 unit attribute string */
+ const char *unit; /* Pointer to Position2D's unit attribute string */
+ double **ptr; /* Arrays holding Position2D axis values */
+ double cen[ 2 ]; /* Centre values */
+ double hw[ 2 ]; /* Half widths values */
+ double pa; /* Error position angle */
+ int i; /* Loop count */
+ int max[6]; /* Max allowed occurrences of each name */
+ int min[6]; /* Min allowed occurrences of each name */
+ int nword; /* Number of words extracted from string */
+
+/* Initialise */
+ result = NULL;
+ pos[ 0 ] = AST__BAD;
+ pos[ 1 ] = AST__BAD;
+ *anc = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "Name";
+ names[ 1 ] = "Error2|Error2PA";
+ names[ 2 ] = "Value2";
+ names[ 3 ] = "Resolution2|Resolution2PA";
+ names[ 4 ] = "Size2|Size2PA";
+ names[ 5 ] = "PixSize2|PixSize2PA";
+ max[ 0 ] = 1;
+ max[ 1 ] = 2;
+ max[ 2 ] = 1;
+ max[ 3 ] = 2;
+ max[ 4 ] = 2;
+ max[ 5 ] = 2;
+ min[ 0 ] = 1;
+ min[ 1 ] = 0;
+ min[ 2 ] = 0;
+ min[ 3 ] = 0;
+ min[ 4 ] = 0;
+ min[ 5 ] = 0;
+ scan = ScanIVOAElement( this, elem, 6, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Create a KeyMap to return holding ancilary info, and put the Name into
+ it. */
+ *anc = astKeyMap( "", status );
+ if( scan->count[0] > 0 ) astMapPut0C( *anc, AST__STCNAME,
+ astXmlGetValue( scan->el[0][0], 0 ), NULL );
+
+/* Get the units attribute from the supplied element. These are the units
+ of the positional axis values. Split into units for each of the two
+ axes. */
+ unit1 = "";
+ unit2 = "";
+ words = NULL;
+ unit = astXmlGetAttributeValue( elem, "unit" );
+
+ if( !unit ) {
+ Report( this, elem, FAILURE, "contains no unit attribute", status );
+
+ } else {
+ words = astChrSplit( unit, &nword );
+ if( words ) {
+ unit1 = words[ 0 ];
+ unit2 = nword > 1 ? words[ 1 ] : words[ 0 ];
+ }
+ }
+
+/* Since the SkyFrame class does not have active Units we must convert the
+ axis values from the supplied units (e.g. degrees) to radians. Allow
+ for different units on the two axes. */
+ map1 = astUnitMapper( unit1, "rad", NULL, NULL );
+ if( !map1 ) Report( this, elem, FAILURE, "contains unusable units for axis 1", status );
+
+ if( unit1 && unit2 && strcmp( unit1, unit2 ) ) {
+ map2 = astUnitMapper( unit2, "rad", NULL, NULL );
+ if( !map2 ) Report( this, elem, FAILURE, "contains unusable units for axis 2", status );
+ } else {
+ map2 = astClone( map1 );
+ }
+
+/* If this Position2D contains a Value which can be read, obtain it. Otherwise,
+ issue a warning. */
+ if( scan->count[ 2 ] > 0 ) {
+ ElemListD( this, scan->el[ 2 ][ 0 ], 2, pos, status );
+
+/* Convert to radians. */
+ if( map1 == map2 ) {
+ astTran1( map1, 2, pos, 1, pos );
+ } else {
+ astTran1( map1, 1, pos, 1, pos );
+ astTran1( map2, 1, pos + 1, 1, pos + 1 );
+ }
+
+/* If this Position2D contains a value which cannot be used, issue a warning. */
+ if( pos[ 1 ] == AST__BAD ) {
+ Report( this, elem, WARNING, "contains an unreadable <Value>", status );
+ }
+
+/* Create a PointList from it and store in the returned ancillary KeyMap. */
+ pset = astPointSet( 1, 2, "", status );
+ ptr = astGetPoints( pset );
+ if( astOK ) {
+ ptr[ 0 ][ 0 ] = pos[ 0 ];
+ ptr[ 1 ][ 0 ] = pos[ 1 ];
+ r = (AstRegion *) astPointList( frm, pset, NULL, "", status );
+ astMapPut0A( *anc, AST__STCVALUE, r, NULL );
+ r = astAnnul( r );
+ }
+ pset = astAnnul( pset );
+ }
+
+/* Does this Position2D contain any Error? */
+ if( scan->count[ 1 ] > 0 && map1 ) {
+
+/* Issue a warning if more than 1 Error value was found. */
+ if( scan->count[ 1 ] > 1 ) {
+ Report( this, elem, WARNING, "contains more than one <Error>"
+ " element. AST can only use the first", status );
+ }
+
+/* If the error has no position angle, just read it as a list of double.
+ Otherwise, read the Error2PA structure. */
+ name = astXmlGetName( scan->el[ 1 ][ 0 ] );
+ if( name ) {
+ if( !strcmp( name, "Error2" ) ) {
+ ElemListD( this, scan->el[ 1 ][ 0 ], 2, hw, status );
+ pa = AST__BAD;
+ } else {
+ pa = Error2PAReader( this, scan->el[ 1 ][ 0 ], hw, status );
+ }
+
+/* Convert to radians, and halve to get the half-width. */
+ if( map1 == map2 ) {
+ astTran1( map1, 2, hw, 1, hw );
+ } else {
+ astTran1( map1, 1, hw, 1, hw );
+ astTran1( map2, 1, hw + 1, 1, hw + 1 );
+ }
+
+ if( hw[ 0 ] != AST__BAD ) hw[ 0 ] *= 0.5;
+ if( hw[ 1 ] != AST__BAD ) hw[ 1 ] *= 0.5;
+
+/* Create an Ellipse or Box to describe the error */
+ cen[ 0 ] = 0.0;
+ cen[ 1 ] = 0.0;
+ if( pa != AST__BAD ) {
+ result = (AstRegion *) astEllipse( frm, 1, cen, hw, &pa,
+ NULL, "", status );
+ } else {
+ result = (AstRegion *) astBox( frm, 0, cen, hw, NULL, "", status );
+ }
+
+/* Store in the returned ancillary KeyMap. */
+ astMapPut0A( *anc, AST__STCERROR, result, NULL );
+ }
+ }
+
+/* Does this Position2D contain any Resolution? */
+ if( scan->count[ 3 ] > 0 && map1 ) {
+
+/* Issue a warning if more than 1 Resolution value was found. */
+ if( scan->count[ 3 ] > 1 ) {
+ Report( this, elem, WARNING, "contains more than one <Resolution>"
+ " element. AST can only use the first", status );
+ }
+
+/* If the resolution has no position angle, just read it as a list of double.
+ Otherwise, read the Resolution2PA structure (which is exactly the same
+ as an Error2PA structure). */
+ name = astXmlGetName( scan->el[ 3 ][ 0 ] );
+ if( name ) {
+ if( !strcmp( name, "Resolution2" ) ) {
+ ElemListD( this, scan->el[ 3 ][ 0 ], 2, hw, status );
+ pa = AST__BAD;
+ } else {
+ pa = Error2PAReader( this, scan->el[ 3 ][ 0 ], hw, status );
+ }
+
+/* Convert to radians, and halve to get the half-width. */
+ if( map1 == map2 ) {
+ astTran1( map1, 2, hw, 1, hw );
+ } else {
+ astTran1( map1, 1, hw, 1, hw );
+ astTran1( map2, 1, hw + 1, 1, hw + 1 );
+ }
+
+ if( hw[ 0 ] != AST__BAD ) hw[ 0 ] *= 0.5;
+ if( hw[ 1 ] != AST__BAD ) hw[ 1 ] *= 0.5;
+
+/* Create an Ellipse or Box to describe the resolution */
+ cen[ 0 ] = 0.0;
+ cen[ 1 ] = 0.0;
+ if( pa != AST__BAD ) {
+ r = (AstRegion *) astEllipse( frm, 1, cen, hw, &pa,
+ NULL, "", status );
+ } else {
+ r = (AstRegion *) astBox( frm, 0, cen, hw, NULL, "", status );
+ }
+
+/* Store in the returned ancillary KeyMap. */
+ astMapPut0A( *anc, AST__STCRES, r, NULL );
+ r = astAnnul( r );
+ }
+ }
+
+/* Does this Position2D contain any Size? */
+ if( scan->count[ 4 ] > 0 && map1 ) {
+
+/* Issue a warning if more than 1 Size value was found. */
+ if( scan->count[ 4 ] > 1 ) {
+ Report( this, elem, WARNING, "contains more than one <Size>"
+ " element. AST can only use the first", status );
+ }
+
+/* If the size has no position angle, just read it as a list of double.
+ Otherwise, read the Size2PA structure (which is exactly the same
+ as an Error2PA structure). */
+ name = astXmlGetName( scan->el[ 4 ][ 0 ] );
+ if( name ) {
+ if( !strcmp( name, "Size2" ) ) {
+ ElemListD( this, scan->el[ 4 ][ 0 ], 2, hw, status );
+ pa = AST__BAD;
+ } else {
+ pa = Error2PAReader( this, scan->el[ 4 ][ 0 ], hw, status );
+ }
+
+/* Convert to radians, and halve to get the half-width. */
+ if( map1 == map2 ) {
+ astTran1( map1, 2, hw, 1, hw );
+ } else {
+ astTran1( map1, 1, hw, 1, hw );
+ astTran1( map2, 1, hw + 1, 1, hw + 1 );
+ }
+
+ if( hw[ 0 ] != AST__BAD ) hw[ 0 ] *= 0.5;
+ if( hw[ 1 ] != AST__BAD ) hw[ 1 ] *= 0.5;
+
+/* Create an Ellipse or Box to describe the size */
+ cen[ 0 ] = 0.0;
+ cen[ 1 ] = 0.0;
+ if( pa != AST__BAD ) {
+ r = (AstRegion *) astEllipse( frm, 1, cen, hw, &pa,
+ NULL, "", status );
+ } else {
+ r = (AstRegion *) astBox( frm, 0, cen, hw, NULL, "", status );
+ }
+
+/* Store in the returned ancillary KeyMap. */
+ astMapPut0A( *anc, AST__STCSIZE, r, NULL );
+ r = astAnnul( r );
+ }
+ }
+
+/* Does this Position2D contain any PixSize? */
+ if( scan->count[ 5 ] > 0 && map1 ) {
+
+/* Issue a warning if more than 1 PixSize value was found. */
+ if( scan->count[ 5 ] > 1 ) {
+ Report( this, elem, WARNING, "contains more than one <PixSize>"
+ " element. AST can only use the first", status );
+ }
+
+/* If the pixsize has no position angle, just read it as a list of double.
+ Otherwise, read the PixSize2PA structure (which is exactly the same
+ as an Error2PA structure). */
+ name = astXmlGetName( scan->el[ 5 ][ 0 ] );
+ if( name ) {
+ if( !strcmp( name, "PixSize2" ) ) {
+ ElemListD( this, scan->el[ 5 ][ 0 ], 2, hw, status );
+ pa = AST__BAD;
+ } else {
+ pa = Error2PAReader( this, scan->el[ 5 ][ 0 ], hw, status );
+ }
+
+/* Convert to radians, and halve to get the half-width. */
+ if( map1 == map2 ) {
+ astTran1( map1, 2, hw, 1, hw );
+ } else {
+ astTran1( map1, 1, hw, 1, hw );
+ astTran1( map2, 1, hw + 1, 1, hw + 1 );
+ }
+
+ if( hw[ 0 ] != AST__BAD ) hw[ 0 ] *= 0.5;
+ if( hw[ 1 ] != AST__BAD ) hw[ 1 ] *= 0.5;
+
+/* Create an Ellipse or Box to describe the pixsize */
+ cen[ 0 ] = 0.0;
+ cen[ 1 ] = 0.0;
+ if( pa != AST__BAD ) {
+ r = (AstRegion *) astEllipse( frm, 1, cen, hw, &pa,
+ NULL, "", status );
+ } else {
+ r = (AstRegion *) astBox( frm, 0, cen, hw, NULL, "", status );
+ }
+
+/* Store in the returned ancillary KeyMap. */
+ astMapPut0A( *anc, AST__STCPIXSZ, r, NULL );
+ r = astAnnul( r );
+ }
+ }
+
+/* Free resources */
+ if( map1 ) map1 = astAnnul( map1 );
+ if( map2 ) map2 = astAnnul( map2 );
+ scan = FreeIVOAScan( scan, status );
+ if( words ) {
+ for( i = 0; i < nword; i++ ) words[ i ] = astFree( words[ i ] );
+ words = astFree( words );
+ }
+
+ }
+
+/* Return NULL if an error occurred. */
+ if( !astOK ) result = astAnnul( result );
+
+/* Return the result */
+ return result;
+
+}
+
+static AstRegion *PositionIntervalReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, int *status ){
+/*
+* Name:
+* PositionIntervalReader
+
+* Purpose:
+* Make an AST Region from an IVOA PositionInterval element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *PositionIntervalReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* PositionInterval element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA PositionInterval element.
+* frm
+* Pointer to the Frame in which the returned Region should be
+* defined. If the Unit or System attribute is not set, this
+* function will decide on the values to be used, and set these
+* values in the supplied Frame before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstRegion *new; /* Pointer to returned Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *names[1]; /* Names of the subelements to be searched for */
+ const char *unit; /* Unit string from supplied element */
+ int max[1]; /* Max allowed occurrences of each name */
+ int min[1]; /* Min allowed occurrences of each name */
+ int ndim; /* Number of axes in supplied Frame */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Search the supplied element for the required sub-element. */
+ ndim = astGetNaxes( frm );
+ if( ndim == 1 ) {
+ names[ 0 ] = "CoordScalarInterval";
+ } else if( ndim == 2 ) {
+ names[ 0 ] = "Coord2VecInterval";
+ } else if( ndim == 3 ) {
+ names[ 0 ] = "Coord3VecInterval";
+ } else if( astOK ) {
+ astError( AST__INTER, "PositionIntervalReader(XmlChan): Supplied "
+ "Frame has more than 3 axes (internal AST programming error )." , status);
+ }
+ min[ 0 ] = 1;
+ max[ 0 ] = 1;
+ scan = ScanIVOAElement( this, elem, 1, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the units attribute from the supplied element. */
+ unit = astXmlGetAttributeValue( elem, "unit" );
+ if( !unit ) {
+ Report( this, elem, FAILURE, "contains no unit attribute", status );
+ unit = "";
+ }
+
+/* Read 1-d intervals */
+ if( ndim == 1 ) {
+ new = CoordScalarIntervalReader( this, scan->el[0][0], unit, frm, status );
+
+/* Read 2-d intervals */
+ } else if( ndim == 2 ) {
+ new = Coord2VecIntervalReader( this, scan->el[0][0], unit, frm, status );
+
+/* Read 3-d intervals */
+ } else if( ndim == 3 ) {
+ new = Coord3VecIntervalReader( this, scan->el[0][0], unit, frm, status );
+
+/* Report error for other dimensionalities */
+ } else if( astOK ) {
+ astError( AST__INTER, "PositionIntervalReader(XmlChan): Supplied "
+ "Frame has more than 3 axes (internal AST programming error )." , status);
+ }
+
+/* Free resources */
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static AstObject *Read( AstChannel *this_channel, int *status ) {
+/*
+* Name:
+* Read
+
+* Purpose:
+* Read an Object from a Channel.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstObject *Read( AstChannel *this_channel, int *status )
+
+* Class Membership:
+* XmlChan member function (over-rides the astRead method
+* inherited from the Channel class).
+
+* Description:
+* This function reads an Object from an XmlChan.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Object.
+*/
+
+/* Local Variables: */
+ AstObject *new; /* Pointer to returned Object */
+ AstXmlChan *this; /* Pointer to the XmlChan structure */
+ AstXmlElement *elem; /* XML element holding AST Object */
+ int def_fmt; /* Original default format */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) this_channel;
+
+/* Save the current default format, and then reset it to NATIVE */
+ def_fmt = this->formatdef;
+ this->formatdef = NATIVE_FORMAT;
+
+/* First we construct an in-memory XML representation of the data source,
+ by reading text up to the end of the first element encountered from
+ which an AST Object could be created. If the Skip attribute is zero, then
+ an error is reported if there is any text prior to the start of the first
+ usable element. If Skip is non-zero any initial text prior to the start
+ of the first usable element is ignored. */
+ elem = ReadXmlText( this, status );
+
+/* Check a usable element was found. */
+ if( elem ) {
+
+/* The "current container element" is the XML element from which items
+ are currently being read. Indicate that we are currently not reading
+ any element (not used for IVOA formats). */
+ this->container = NULL;
+
+/* Next we create a new AST Object from this in-memory XML representation
+ of the source. */
+ new = MakeAstFromXml( this, elem, status );
+
+/* Remove the element. This will cause an error to be reported if
+ the element contains any items which have not been used. */
+ elem = Remove( this, elem, status );
+ }
+
+/* If an error has occurred, annul the document. */
+ if( !astOK ) this->readcontext = astXmlAnnul( this->readcontext );
+
+/* If an error occurred, clean up by deleting the new Object and
+ return a NULL pointer, and re-instate original default format. */
+ if ( !astOK ) {
+ new = astDelete( new );
+ this->formatdef = def_fmt;
+ }
+
+/* Return the pointer to the new Object. */
+ return new;
+}
+
+static void ReadClassData( AstChannel *this_channel, const char *class, int *status ) {
+/*
+* Name:
+* ReadClassData
+
+* Purpose:
+* Read values from a data source for a class loader.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* void ReadClassData( AstChannel *this, const char *class, int *status )
+
+* Class Membership:
+* XmlChan member function (over-rides the astReadClassData method
+* inherited from the Channel class).
+
+* Description:
+* This function reads the data for a class from the data source
+* associated with a Channel, so as to provide values for
+* initialising the instance variables of that class as part of
+* building a complete Object. This function should be invoked by
+* the loader for each class immediately before it attempts to read
+* these values.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* class
+* A pointer to a constant null-terminated string containing the
+* name of the class whose loader is requesting the data (note
+* this is not usually the same as the class name of the Object
+* being built). This value allows the class structure of the
+* input data to be validated.
+*-
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ AstXmlChan *this; /* Pointer to the XmlChan structure */
+ AstXmlContentItem *item; /* Pointer to next item of content */
+ const char *definedby; /* Class defining current content items */
+ int nitem; /* Number of items in container */
+ int i; /* Loop counter */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) this_channel;
+
+/* Check we have a container, and then store the name of the class being
+ loaded. */
+ if( !this->container ){
+ astError( AST__INTER, "astRead(XmlChan): Invalid attempt to read "
+ "%s data - there is currently no container element "
+ "(internal AST programming error).", status, class );
+
+ } else {
+ this->isa_class = class;
+
+/* Go through all the content elements within the current container and
+ give them an extra attribute named "definedby" the value of which is
+ the name of the class which defines the associated AST attribute or
+ object. This is determined by the the "isa" elements - an element is
+ "definedby" the class noted in the next following "isa" element, or by
+ the class being loaded if there is no following "isa" element. */
+
+/* Find the first "isa" element and get the value of its "class" attribute.
+ If none is found the name of the class being loaded is used. */
+ definedby = FindNextIsA( (AstXmlElement *) this->container, 0, status );
+
+/* Loop round all elements within the container. */
+ nitem = astXmlGetNitem( this->container );
+ for( i = 0; astOK && i < nitem; i++ ) {
+ item = astXmlGetItem( this->container, i );
+ if( astXmlCheckType( item, AST__XMLELEM ) ) {
+
+/* If this is an "ISA" element, then we have ended the scope of the
+ current "isa" class. All subsequent items will be defined by the class
+ mentioned in the next following "ISA" element. Find the next ISA
+ element and get its class. */
+ if( astOK && !strcmp( astXmlGetName( item ), ISA ) ) {
+ definedby = FindNextIsA( (AstXmlElement *) this->container, i + 1, status );
+
+/* For other element types, add a "definedby" attribute holding the name
+ of the class defined by the current ISA element. */
+ } else {
+ astXmlAddAttr( item, DEFINEDBY, definedby, NULL );
+ }
+ }
+ }
+ }
+}
+
+static double ReadDouble( AstChannel *this_channel, const char *name, double def, int *status ) {
+/*
+* Name:
+* ReadDouble
+
+* Purpose:
+* Read a double value as part of loading a class.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* double ReadDouble( AstChannel *this, const char *name, double def, int *status )
+
+* Class Membership:
+* XmlChan member function (over-rides the astReadDouble method
+* inherited from the Channel class).
+
+* Description:
+* This function searches the current container element of an XmlChan to
+* identify a double value with a specified name. If such a value
+* is found, it is returned, otherwise a default value is returned
+* instead.
+*
+* This function should only be invoked from within the loader
+* function associated with a class, in order to return a double
+* value to be assigned to an instance variable. It must be
+* preceded by a call to the astReadClassData function.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* name
+* Pointer to a constant null-terminated character string
+* containing the name of the required value. This must be in
+* lower case with no surrounding white space. Note that names
+* longer than 6 characters will not match any value.
+* def
+* If no suitable value can be found (e.g. it is absent from the
+* data stream being read), then this value will be returned
+* instead.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The required value, or the default if the value was not found.
+
+* Notes:
+* - A value of 0.0 will be returned if this function is invoked
+* with the global error status set, or if it should fail for any
+* reason.
+*-
+*/
+
+/* Local Variables: */
+ AstXmlChan *this; /* Pointer to the XmlChan structure */
+ AstXmlElement *element; /* Pointer to element holding required value */
+ const char *value; /* Pointer to attribute value */
+ double result; /* Value to be returned */
+ int nc; /* Number of characters read by astSscanf */
+
+/* Initialise. */
+ result = 0.0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) this_channel;
+
+/* Search the current container element for an ATTR element
+ describing an AST attribute of the specified name. This call ignores
+ ATTR elements which represent default values. No error is
+ reported if an ATTR element with the given name cannot be
+ found. */
+ element = FindAttribute( this, name, status );
+
+/* If an element was found, attempt to decode the string to give a double
+ value, checking that the entire string is read (and checking for the
+ magic string used to represent bad values). If this fails, then the
+ wrong name has probably been given, or the input data are corrupt,
+ so report an error. */
+ if( element ) {
+ value = astXmlGetAttributeValue( element, VALUE );
+ if( value ) {
+ nc = 0;
+ if ( ( 0 == astSscanf( value, " " BAD_STRING " %n",
+ &nc ) )
+ && ( nc >= (int) strlen( value ) ) ) {
+ result = AST__BAD;
+
+ } else if ( !( ( 1 == astSscanf( value, " %lf %n", &result, &nc ) )
+ && ( nc >= (int) strlen( value ) ) ) ) {
+ astError( AST__BADIN, "astRead(XmlChan): The value \"%s = %s\" "
+ "cannot be read as a double precision floating point "
+ "number.", status, name, value );
+
+/* If the value was succesfully read, remove the ATTR element
+ from the container. */
+ } else {
+ element = Remove( this, element, status );
+ }
+
+/* Report an error if the attribute does not have a value. */
+ } else {
+ astError( AST__BADIN, "astRead(XmlChan): No value for attribute "
+ "\"%s\" within element \"%s\".", status, name,
+ GetTag( (AstXmlObject *) element, 1, status ) );
+ }
+
+/* If no suitable element was found, then use the default value instead. */
+ } else {
+ result = def;
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static int ReadInt( AstChannel *this_channel, const char *name, int def, int *status ) {
+/*
+* Name:
+* ReadInt
+
+* Purpose:
+* Read a int value as part of loading a class.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* int ReadInt( AstChannel *this, const char *name, int def )
+
+* Class Membership:
+* XmlChan member function (over-rides the astReadInt method
+* inherited from the Channel class).
+
+* Description:
+* This function searches the current container element of an XmlChan to
+* identify a int value with a specified name. If such a value
+* is found, it is returned, otherwise a default value is returned
+* instead.
+*
+* This function should only be invoked from within the loader
+* function associated with a class, in order to return a int
+* value to be assigned to an instance variable. It must be
+* preceded by a call to the astReadClassData function.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* name
+* Pointer to a constant null-terminated character string
+* containing the name of the required value. This must be in
+* lower case with no surrounding white space. Note that names
+* longer than 6 characters will not match any value.
+* def
+* If no suitable value can be found (e.g. it is absent from the
+* data stream being read), then this value will be returned
+* instead.
+
+* Returned Value:
+* The required value, or the default if the value was not found.
+
+* Notes:
+* - A value of zero will be returned if this function is invoked
+* with the global error status set, or if it should fail for any
+* reason.
+*-
+*/
+
+/* Local Variables: */
+ AstXmlChan *this; /* Pointer to the XmlChan structure */
+ AstXmlElement *element; /* Pointer to element holding required value */
+ const char *value; /* Pointer to attribute value */
+ int result; /* Value to be returned */
+ int nc; /* Number of characters read by astSscanf */
+
+/* Initialise. */
+ result = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) this_channel;
+
+/* Search the current container element for an ATTR element
+ describing an AST attribute of the specified name. This call ignores
+ ATTR elements which represent default values. No error is
+ reported if an ATTR element with the given name cannot be
+ found. */
+ element = FindAttribute( this, name, status );
+
+/* If an element was found, attempt to decode the string to give a int
+ value, checking that the entire string is read. If this fails, then the
+ wrong name has probably been given, or the input data are corrupt,
+ so report an error. */
+ if( element ) {
+ value = astXmlGetAttributeValue( element, VALUE );
+ if( value ) {
+ nc = 0;
+ if ( !( ( 1 == astSscanf( value, " %d %n", &result, &nc ) )
+ && ( nc >= (int) strlen( value ) ) ) ) {
+ astError( AST__BADIN,
+ "astRead(XmlChan): The value \"%s = %s\" cannot "
+ "be read as an integer.", status, name, value );
+
+/* If the value was succesfully read, remove the ATTR element
+ from the container. */
+ } else {
+ element = Remove( this, element, status );
+ }
+
+/* Report an error if the attribute does not have a value. */
+ } else {
+ astError( AST__BADIN, "astRead(XmlChan): No value for attribute "
+ "\"%s\" within element \"%s\".", status, name,
+ GetTag( (AstXmlObject *) element, 1, status ) );
+ }
+
+/* If no suitable element was found, then use the default value instead. */
+ } else {
+ result = def;
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static AstObject *ReadObject( AstChannel *this_channel, const char *name,
+ AstObject *def, int *status ) {
+/*
+* Name:
+* ReadObject
+
+* Purpose:
+* Read a (sub)Object as part of loading a class.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* char *ReadObject( AstChannel *this, const char *name, AstObject *def )
+
+* Class Membership:
+* XmlChan member function (over-rides the astReadObject method
+* inherited from the Channel class).
+
+* Description:
+* This function searches the current container element of an XmlChan to
+* identify an Object with a specified name. If such an Object
+* is found, a pointer to it is returned, otherwise a default pointer
+* is returned instead.
+*
+* This function should only be invoked from within the loader
+* function associated with a class, in order to return a int
+* value to be assigned to an instance variable. It must be
+* preceded by a call to the astReadClassData function.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* name
+* Pointer to a constant null-terminated character string
+* containing the name of the required value. This must be in
+* lower case with no surrounding white space. Note that names
+* longer than 6 characters will not match any value.
+* def
+* If no suitable value can be found (e.g. it is absent from the
+* data stream being read), then this value will be returned
+* instead.
+
+* Returned Value:
+* A pointer to the Object, or a clone of the default pointer if
+* the Object was not found.
+
+* 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: */
+ AstXmlChan *this; /* Pointer to the XmlChan structure */
+ AstXmlElement *element; /* Pointer to element holding required value */
+ AstObject *result; /* Value to be returned */
+ const char *isa_class; /* Class currently being loaded */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) this_channel;
+
+/* Search the current container element for an element with a name which
+ is not ATTR and with the specified LABEL. This call ignores
+ elements which represent default values. No error is reported if an
+ element with the given label cannot be found. */
+ element = FindObject( this, name, status );
+
+/* If an element was found, make an AST object from it. First remember
+ the class currently being loaded so that it can be re-instated. */
+ if( element ) {
+ isa_class = this->isa_class;
+ result = MakeAstFromXml( this, element, status );
+ this->isa_class = isa_class;
+
+/* Remove the element from the container. */
+ element = Remove( this, element, status );
+
+/* If no suitable Value structure was found, clone the default
+ pointer, if given. */
+ } else if ( def ) {
+ result = astClone( def );
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static char *ReadString( AstChannel *this_channel, const char *name, const char *def, int *status ) {
+/*
+* Name:
+* ReadString
+
+* Purpose:
+* Read a string value as part of loading a class.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* char *ReadString( AstChannel *this, const char *name, const char *def )
+
+* Class Membership:
+* XmlChan member function (over-rides the astReadString method
+* inherited from the Channel class).
+
+* Description:
+* This function searches the current container element of an XmlChan to
+* identify a string value with a specified name. If such a value
+* is found, it is returned, otherwise a default value is returned
+* instead.
+*
+* This function should only be invoked from within the loader
+* function associated with a class, in order to return a int
+* value to be assigned to an instance variable. It must be
+* preceded by a call to the astReadClassData function.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* name
+* Pointer to a constant null-terminated character string
+* containing the name of the required value. This must be in
+* lower case with no surrounding white space. Note that names
+* longer than 6 characters will not match any value.
+* def
+* If no suitable value can be found (e.g. it is absent from the
+* data stream being read), then this value will be returned
+* instead.
+
+* Returned Value:
+* A pointer to a dynamically allocated null-terminated string
+* containing the value required, or to a copy of the default
+* string if the value was not found (or NULL if the "def" pointer
+* was NULL).
+
+* Notes:
+* - It is the caller's responsibility to arrange for the memory
+* holding the returned string to be freed (using astFree) when it
+* is no longer required.
+* - 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: */
+ AstXmlChan *this; /* Pointer to the XmlChan structure */
+ AstXmlElement *element; /* Pointer to element holding required value */
+ char *result; /* Value to be returned */
+ const char *value; /* Pointer to attribute value */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) this_channel;
+
+/* Search the current container element for an ATTR element
+ describing an AST attribute of the specified name. This call ignores
+ ATTR elements which represent default values. No error is
+ reported if an ATTR element with the given name cannot be
+ found. */
+ element = FindAttribute( this, name, status );
+
+/* If an element was found, return a copy of the "value" string. */
+ if( element ) {
+ value = astXmlGetAttributeValue( element, VALUE );
+ if( value ) {
+ result = astStore( NULL, value, strlen( value ) + 1 );
+
+/* If the new default for XmlFormat has not yet been set, note if this
+ element contained a "quoted" attribute. */
+ if( this->formatdef == NATIVE_FORMAT ) {
+ if( astXmlGetAttributeValue( element, QUOTED ) ) {
+ this->formatdef = QUOTED_FORMAT;
+ }
+ }
+
+/* Remove the ATTR element from the container. */
+ element = Remove( this, element, status );
+
+/* Report an error if the attribute does not have a value. */
+ } else {
+ astError( AST__BADIN, "astRead(XmlChan): No value for attribute "
+ "\"%s\" within element \"%s\".", status, name,
+ GetTag( (AstXmlObject *) element, 1, status ) );
+ }
+
+/* If no suitable Value structure was found, then make a dynamic copy
+ of the default string (if given) and return a pointer to this. */
+ } else if ( def ) {
+ result = astStore( NULL, def, strlen( def ) + (size_t) 1 );
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static AstXmlElement *ReadXmlText( AstXmlChan *this, int *status ){
+/*
+* Name:
+* ReadXmlText
+
+* Purpose:
+* Create an in-memory XML tree from an XML text source.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstXmlElement *ReadXmlText( AstXmlChan *this, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function constructs an in-memory XML representation of the data
+* source by reading text up to the end of the first element encountered
+* from which an AST Object could be constructed. If the Skip attribute is
+* zero, then an error is reported if there is any text prior to the start
+* of the first AST Object. If Skip is non-zero any initial text prior to
+* the start of the first usable element is ignored.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the returned XmlElement. This should be annuled using
+* astXmlAnnul when no longer needed. NULL is returned if the end of
+* the source text is reached without finding a en element from which
+* an AST Object could be read.
+
+* Notes:
+* - A NULL pointer is returned if an error has already occurred, of
+* if this function should fail for any reason.
+
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Declare the thread specific global data */
+ AstXmlElement *result; /* Returned pointer */
+ int skip; /* Skip over initial irrelevant markup? */
+
+/* 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);
+
+/* Get the value of the Skip attribute. This indicates if we should skip
+ over any irrelevant markup prior to the first element from which an
+ AST object could be created. */
+ skip = astGetSkip( this );
+
+/* Store a pointer to the XmlChan in a module variable so that the IsUsable function
+ can access its properties. */
+ isusable_this = this;
+
+/* Read characters from the XML source and return an XmlElement structure
+ containing the first usable element encountered. */
+ result = astXmlReadDocument( &(this->readcontext), IsUsable, skip,
+ GetNextChar, this );
+
+/* Nullify the module variable for safety. */
+ isusable_this = NULL;
+
+/* If no usable element was found, annul the document. */
+ if( !result ) this->readcontext = astXmlAnnul( this->readcontext );
+
+/* Delete the returned element if an error has occurred. */
+ if( !astOK ) result = astXmlAnnulTree( result );
+
+/* Return the result. */
+ return result;
+
+}
+
+static void ReCentreAnc( AstRegion *region, int nanc, AstKeyMap **ancs, int *status ){
+/*
+* Name:
+* ReCentreAnc
+
+* Purpose:
+* Re-centre the Regions describing ancillary information extracted
+* from an AstroCoords elements.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* ReCentreAnc( AstRegion *region, int nanc, AstKeyMap **ancs, int *status )
+
+* Class Membership:
+* XmlChan member function
+
+* Description:
+* This function recentres the Regions which describe ancillary
+* information from an AstroCoords element so that it is centred at
+* the centre of the associated AstroCoordsArea element.
+
+* Parameters:
+* region
+* Pointer to the Region defining the associated AstroCoordsArea.
+* nanc
+* Number of KeyMap pointers stored in "ancs"
+* ancs
+* Pointer to an array of "nanc" elements, each being a pointer to
+* a KeyMap. Each one describes the ancilary information in an
+* AstroCoords element associated with the AstroCoordsArea decribed
+* by "region". Each KeyMap has elements with keys AST__STCERROR,
+* AST__STCRES, AST__STCSIZE, AST__STCPIXSZ, AST__STCVALUE each of
+* which holds a pointer to a Region. These Regions are modified on
+* exit so that they are centred on a point which inside the supplied
+* Region.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ AstFrame *frm;
+ AstFrame *pfrm;
+ AstFrameSet *fs;
+ AstMapping *map;
+ AstMapping *smap;
+ AstObject *o;
+ AstRegion *r2;
+ AstRegion *r;
+ char orgatt[ 20 ];
+ char orgset[ 80 ];
+ char setting[ 80 ];
+ char sysatt[ 20 ];
+ char sysset[ 80 ];
+ const char *old_unit;
+ const char *time_unit;
+ double *lbnd;
+ double *mid;
+ double *ubnd;
+ double epoch;
+ int i;
+ int j;
+ int k;
+ int ndim;
+ int paxis;
+ int time_axis;
+
+ static const char *key[ 5 ] = { AST__STCERROR,
+ AST__STCRES,
+ AST__STCSIZE,
+ AST__STCPIXSZ,
+ AST__STCVALUE };
+
+/* Check the inherited status. Also return if no Keymaps supplied. */
+ if( !nanc || !astOK ) return;
+
+/* Get the Frame encapsulated by the suipplied Region. */
+ frm = astRegFrame( region );
+
+/* Get the bounds of the supplied Region. */
+ ndim = astGetNaxes( frm );
+ lbnd = astMalloc( sizeof( double )*(size_t) ndim );
+ ubnd = astMalloc( sizeof( double )*(size_t) ndim );
+ mid = astMalloc( sizeof( double )*(size_t) ndim );
+ if( mid ) {
+ astGetRegionBounds( region, lbnd, ubnd );
+
+/* Get a mid point, taking account of unbounded axes. Also find the index of
+ the time axis (if any) in the supplied Region, and get the System and
+ TimeOrigin values for the time axis. */
+ time_axis = -1;
+ time_unit = NULL;
+ orgatt[ 0 ] = 0;
+ sysatt[ 0 ] = 0;
+ for( i = 0; i < ndim; i++ ) {
+ if( lbnd[ i ] > -0.5*DBL_MAX ) {
+ if( ubnd[ i ] < 0.5*DBL_MAX ) {
+ mid[ i ] = 0.5*( lbnd[ i ] + ubnd[ i ] );
+ } else {
+ mid[ i ] = lbnd[ i ];
+ }
+ } else {
+ if( ubnd[ i ] < 0.5*DBL_MAX ) {
+ mid[ i ] = ubnd[ i ];
+ } else {
+ mid[ i ] = 0.0;
+ }
+ }
+
+/* If we have not found a time axis, see if the current axis is a time axis. */
+ if( time_axis == -1 ) {
+ astPrimaryFrame( frm, i, &pfrm, &paxis );
+ if( astIsATimeFrame( pfrm ) ) {
+
+/* If so, record its index. */
+ time_axis = i;
+
+/* If the TimeOrigin attribute is set, save its value. Create strings
+ holding the attribute name and appropriate setting string for use with
+ the ancillary regions. */
+ if( astTestTimeOrigin( (AstTimeFrame *) pfrm ) ) {
+ sprintf( orgatt, "TimeOrigin(%d)", i + 1 );
+ sprintf( orgset, "TimeOrigin(%d)=%s", i + 1,
+ astGetC( pfrm, "TimeOrigin" ) );
+ }
+
+/* If the System attribute is set, save its value. Create strings
+ holding the attribute name and appropriate setting string for use with
+ the ancillary regions. */
+ if( astTestSystem( pfrm ) ) {
+ sprintf( sysatt, "System(%d)", i + 1 );
+ sprintf( sysset, "System(%d)=%s", i + 1,
+ astGetC( pfrm, "System" ) );
+ }
+
+ time_unit = astGetUnit( pfrm, 0 );
+ }
+ pfrm = astAnnul( pfrm );
+ }
+
+ }
+
+/* Get the Region Epoch. */
+ if( astTestEpoch( frm ) ){
+ epoch = astGetEpoch( frm );
+ sprintf( setting, "Epoch=MJD %.*g", AST__DBL_DIG, epoch );
+ } else {
+ setting[ 0 ] = 0;
+ epoch = AST__BAD;
+ }
+
+/* Loop round each KeyMap. */
+ for( j = 0; j < nanc; j++ ) {
+
+/* Loop round each of the relevant KeyMap elements (skip the "Value"
+ element since this should not be re-centred). */
+ for( k = 0; k < 5; k++ ) {
+ if( astMapGet0A( ancs[ j ], key[ k ], &o ) ) {
+ r = (AstRegion *) o;
+
+/* The System and TimeOrigin attributes of the STC Region are set when the
+ AstroCoordArea is read. This occurs after the ancillary Coords Regions are
+ created. Consequently, the ancillary Coords Regions may not have set
+ System and/or TimeOrigin values. So, for System and TimeOrigin, if
+ the attribute is set in the supplied Region but not set in the ancillary
+ Region, transfer the set value to the ancillary Region. */
+ if( strlen( sysatt ) && strlen( orgatt ) ) {
+ if( !astTest( r, sysatt ) && !astTest( r, orgatt ) ) {
+ astRegSetAttrib( r, sysset, NULL );
+
+ old_unit = astGetUnit( r, time_axis );
+ if( old_unit && time_unit &&
+ strcmp( old_unit, time_unit ) ) {
+ if( !astTestUnit( r, time_axis ) ) {
+ old_unit = NULL;
+ } else {
+ old_unit = astStore( NULL, old_unit,
+ strlen( old_unit ) + 1 );
+ }
+ astSetUnit( r, time_axis, time_unit );
+ }
+ astRegSetAttrib( r, orgset, NULL );
+ if( !old_unit ) {
+ astClearUnit( r, time_axis );
+ } else if( strcmp( old_unit, time_unit ) ) {
+ astSetUnit( r, time_axis, old_unit );
+ old_unit = astFree( (void *) old_unit );
+ }
+ }
+ }
+
+/* Re-centre the Regions held in this element of the KeyMap, and set
+ its Epoch (do not re-centre the "Value" element). */
+ if( strcmp( key[ k ], AST__STCVALUE ) ) {
+
+/* First ensure the ancillary Region refers to the supplied Frame. */
+ fs = astConvert( r, frm, "" );
+ if( fs ) {
+ map = astGetMapping( fs, AST__BASE, AST__CURRENT );
+ smap = astSimplify( map );
+ if( !astIsAUnitMap( smap ) ) {
+ r2 = astMapRegion( r, smap, frm );
+ astMapPut0A( ancs[ j ], key[ k ], r2, NULL );
+ (void) astAnnul( r );
+ r = r2;
+ }
+ map = astAnnul( map );
+ smap = astAnnul( smap );
+ fs = astAnnul( fs );
+
+/* Now set the epoch and re-centre.*/
+ if( epoch != AST__BAD ) astRegSetAttrib( r, setting, NULL );
+ astRegCentre( r, mid, NULL, 0, AST__CURRENT );
+ }
+ }
+ r = astAnnul( r );
+ }
+ }
+ }
+ }
+
+/* Free resources. */
+ lbnd = astFree( lbnd );
+ ubnd = astFree( ubnd );
+ mid = astFree( mid );
+ frm = astAnnul( frm );
+}
+
+static AstObject *RedshiftFrameReader( AstXmlChan *this,
+ AstXmlElement *elem, int *status ) {
+/*
+* Name:
+* RedshiftFrameReader
+
+* Purpose:
+* Make an AST Object from an IVOA RedshiftFrame element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstObject *RedshiftFrameReader( AstXmlChan *this, AstXmlElement *elem, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Object from the supplied IVOA
+* RedshiftFrame element. The returned Object is a SpecFrame in which
+* the Domain is set explicitly to REDSHIFT.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA RedshiftFrame element.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Object.
+
+*/
+
+/* Local Variables: */
+ AstSpecFrame *new; /* Pointer to returned Object */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *names[3]; /* Names of the subelements to be searched for */
+ const char *sor; /* StdOfRest for returned Frame */
+ const char *type; /* Doppler type (velocity or redshift) */
+ const char *sys; /* Spectral system */
+ int max[3]; /* Max allowed occurrences of each name */
+ int min[3]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return (AstObject *) new;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "TOPOCENTER|BARYCENTER|HELIOCENTER|GEOCENTER|LSR|"
+ "LSRK|GALACTIC_CENTER|LOCAL_GROUP_CENTER|LSRD";
+ names[ 1 ] = DOPPLER_DEFINITION;
+ names[ 2 ] = "Name";
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ max[ 2 ] = 1;
+ min[ 0 ] = 1;
+ min[ 1 ] = 1;
+ min[ 2 ] = 0;
+ scan = ScanIVOAElement( this, elem, 3, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the name of the Element specifying the reference position and find
+ the corresponding AST name.*/
+ sor = astXmlGetName( scan->el[0][0] );
+ if( !strcmp( sor, "TOPOCENTER" ) ) {
+ sor = "Topo";
+ } else if( !strcmp( sor, "BARYCENTER" ) ){
+ sor = "Bary";
+ } else if( !strcmp( sor, "GEOCENTER" ) ){
+ sor = "Geo";
+ } else if( !strcmp( sor, "LSR" ) || !strcmp( sor, "LSRK" ) ) {
+ sor = "LSRK";
+ } else if( !strcmp( sor, "LSRD" ) ) {
+ sor = "LSRD";
+ } else if( !strcmp( sor, "GALACTIC_CENTER" ) ) {
+ sor = "Galactic";
+ } else if( !strcmp( sor, "LOCAL_GROUP_CENTER" ) ) {
+ sor = "Local_group";
+ } else if( !strcmp( sor, "HELIOCENTER" ) ) {
+ sor = "Helio";
+ } else if( astOK ){
+ astError( AST__INTER, "RedshiftFrameReader(XmlChan): Unknown "
+ "standard of rest %s (internal AST programming error).", status,
+ sor );
+ }
+
+/* Issue a warning if the reference position includes an ephemeris. */
+ if( FindElement( this, scan->el[0][0], "PlanetaryEphem", status ) ) {
+ Report( this, scan->el[0][0], WARNING, "contains a <PlanetaryEphem> "
+ "element which will be ignored", status );
+ }
+
+/* Get the value of the value_type attribute from the element. */
+ type = astXmlGetAttributeValue( elem, "value_type" );
+ if( !type ) type = "VELOCITY";
+
+/* If the type is REDSHIFT, set the system to redshift. Also check that
+ any <DopplerDefinition> element is "OPTICAL". */
+ if( !strcmp( type, "REDSHIFT" ) ) {
+ sys = astXmlGetValue( scan->el[1][0], 0 );
+ if( sys && !strcmp( sys, "OPTICAL" ) ) {
+ sys = "REDSHIFT";
+ } else {
+ Report( this, elem, FAILURE, "specifies dimensionless "
+ "redshift (z) but has non-optical <DopplerDefinition>", status );
+ }
+
+/* Otherwise, get the value of the Doppler definition element, and translate
+ it to an AST value.*/
+ } else {
+ sys = astXmlGetValue( scan->el[1][0], 0 );
+ if( !sys ) {
+ Report( this, elem, FAILURE, "contains a <DopplerDefinition> "
+ "element which is not simply character data", status );
+
+ } else if( !strcmp( sys, "OPTICAL" ) ) {
+ sys = "VOPT";
+
+ } else if( !strcmp( sys, "RADIO" ) ) {
+ sys = "VRAD";
+
+ } else if( !strcmp( sys, "RELATIVISTIC" ) ) {
+ sys = "VREL";
+
+ } else {
+ Report( this, elem, FAILURE, "contains unsupported Doppler definition", status );
+ }
+ }
+
+/* Create a suitable SpecFrame. */
+ new = astSpecFrame( "Domain=REDSHIFT,System=%s,StdOfRest=%s", status, sys, sor);
+
+/* If the SpectralFrame has a <Name> element use it as the SpecFrame title. */
+ if( scan->count[2] ) astSetTitle( new, astXmlGetValue( scan->el[2][0], 0 ) );
+
+/* Free resources */
+ scan = FreeIVOAScan( scan, status );
+
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Object. */
+ return (AstObject *) new;
+}
+
+static AstRegion *RedshiftIntervalReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, int *status ){
+/*
+* Name:
+* RedshiftIntervalReader
+
+* Purpose:
+* Make an AST Region from an IVOA RedshiftInterval element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *RedshiftIntervalReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* RedshiftInterval element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA RedshiftInterval element.
+* frm
+* Pointer to the Frame in which the returned Region should be
+* defined. If the Unit or System attribute is not set, this
+* function will decide on the values to be used, and set these
+* values in the supplied Frame before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstFrame *cfrm; /* Frame used to define returned Region */
+ AstRegion *new; /* Pointer to returned Region */
+ AstSystemType sys; /* Redshift system */
+ IVOAScan *scan; /* Structure holding scan results */
+ char *unit; /* Unit string from supplied element */
+ const char *funit; /* Unit string from supplied Frame */
+ const char *names[2]; /* Names of the subelements to be searched for */
+ double hilimit; /* Upper spectral limit */
+ double lolimit; /* Lower spectral limit */
+ int max[2]; /* Max allowed occurrences of each name */
+ int min[2]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "LoLimit";
+ names[ 1 ] = "HiLimit";
+ min[ 0 ] = 0;
+ min[ 1 ] = 0;
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ scan = ScanIVOAElement( this, elem, 2, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the limits. */
+ lolimit = scan->count[0] ? ElemValueD( this, scan->el[0][0], 0.0, status ) : AST__BAD;
+ hilimit = scan->count[1] ? ElemValueD( this, scan->el[1][0], 0.0, status ) : AST__BAD;
+
+/* Use any unit and vel_time_unit attributes in the supplied element to
+ determine the system and units for the redshift Frame. */
+ sys = RedshiftSys( this, elem, &unit, 1, status );
+
+/* If no system has been set in the supplied Frame, set a default system
+ now (radio velocity if both units are present, dimensionaless redshift
+ otherwise). */
+ if( !astTestSystem( frm ) ) {
+ astSetSystem( frm, sys );
+
+/* The ReddshiftSys function always returns AST__VRADIO if the velocity
+ is not dimensionless. In this case, if the supplied Frame has system
+ explicitly set AST__VOPTICAL, we use the supplied Frame preference of
+ optical/radio instead of the default returned by RedshiftSys. */
+ } else if( sys != AST__REDSHIFT ) {
+ sys = astGetSystem( frm );
+ if( sys == AST__REDSHIFT ) sys = AST__VRADIO;
+ }
+
+/* Take a copy of the supplied Frame and set its Units to the value found
+ above. */
+ cfrm = astCopy( frm );
+ astSetUnit( cfrm, 0, unit );
+
+/* If at least one limit was found, create an Interval within this
+ modified Frame. Otherwise create a negated NullRegion. */
+ if( lolimit != AST__BAD || hilimit != AST__BAD ) {
+ new = (AstRegion *) astInterval( cfrm, &lolimit, &hilimit, NULL, "", status );
+ } else {
+ new = (AstRegion *) astNullRegion( cfrm, NULL, "negated=1", status );
+ }
+
+/* If the Units of this Region differs from that of the supplied Frame,
+ set it to the Units of the supplied Frame. This will cause the
+ encapsulated limits to be mapped into the new Units. If the supplied
+ Frame had no set Units, set it to the units implied by the supplied
+ XML element. */
+ if( astTestUnit( frm, 0 ) ) {
+ funit = astGetUnit( frm, 0 );
+ if( strcmp( funit, unit ) ) astSetUnit( new, 0, funit );
+ } else {
+ astSetUnit( frm, 0, unit );
+ }
+
+/* Get any fill factor and lo/hi_include attributes from the element and
+ assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Free resources */
+ cfrm = astAnnul( cfrm );
+ if( unit ) unit = astFree( unit );
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static AstRegion *RedshiftReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, AstKeyMap **anc, int *status ){
+/*
+* Name:
+* RedshiftReader
+
+* Purpose:
+* Modify a Frame to take account of an STC <Redshift> element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *RedshiftReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, AstKeyMap **anc, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function reads the supplied STC <Redshift> element, and uses it,
+* if possible, to create the uncertainty associated with the redshift
+* axis in the supplied Frame.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Redshift element.
+* frm
+* Pointer to the 1D redshift Frame.
+* anc
+* Address of a location at which to put a pointer to a newly
+* created KeyMap. This KeyMap will contain ancillary information
+* from the Redshift. The keys identify the item of ancillary
+* information (Name, Value, Error, Resolution, Size, Pixel Size).
+* The value associated with the Name key is string containing
+* the Name item from the Redshift. The value associated with each of
+* the other keys is a pointer to a 1D Region within the supplied
+* Frame, corresponding to the value, error, resolution, etc. Keys
+* will not be present in the returned KeyMap if the corresponding
+* item of ancillary information is not present in the Redshift. A
+* NULL pointer is returned if there is no ancillary information at all.
+* status
+* Pointer to the inherited status variable.
+
+* Returned:
+* The uncertainty Region, or NULL if the supplied Redshift element
+* does not specify an uncertainty.
+
+*/
+
+/* Local Variables: */
+ AstFrameSet *fs; /* FrameSet connecting "sf1" and "sf2" */
+ AstMapping *map; /* Mapping from <Redshift> Frame to supplied Frame */
+ AstRegion *r2; /* Region mapped into returned Frame */
+ AstRegion *r3; /* Simplified Region mapped into returned Frame */
+ AstRegion *r; /* Original Region */
+ AstRegion *result; /* Returned uncertainty Region */
+ AstSpecFrame *sf1; /* SpecFrame describing value element */
+ AstSystemType fsys; /* Redshift system from supplied Stc */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *name; /* Pointer to XML element name */
+ const char *names[6]; /* Names of the subelements to be searched for */
+ char *unit; /* Pointer to Redshift's unit attribute string */
+ double lbnd[ 1 ] ; /* Lower interval bounds */
+ double tmp; /* Temporary storage */
+ double ubnd[ 1 ] ; /* Upper interval bounds */
+ double v; /* Axis value */
+ int max[6]; /* Max allowed occurrences of each name */
+ int min[6]; /* Min allowed occurrences of each name */
+
+/* Initialise */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "Name";
+ names[ 1 ] = "Error";
+ names[ 2 ] = "Value";
+ names[ 3 ] = "Resolution";
+ names[ 4 ] = "Size";
+ names[ 5 ] = "PixSize";
+ max[ 0 ] = 1;
+ max[ 1 ] = 2;
+ max[ 2 ] = 1;
+ max[ 3 ] = 2;
+ max[ 4 ] = 2;
+ max[ 5 ] = 2;
+ min[ 0 ] = 1;
+ min[ 1 ] = 0;
+ min[ 2 ] = 0;
+ min[ 3 ] = 0;
+ min[ 4 ] = 0;
+ min[ 5 ] = 0;
+ scan = ScanIVOAElement( this, elem, 6, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Create a KeyMap to return holding ancilary info, and put the Name into
+ it. */
+ *anc = astKeyMap( "", status );
+ if( scan->count[0] > 0 ) astMapPut0C( *anc, AST__STCNAME,
+ astXmlGetValue( scan->el[0][0], 0 ), NULL );
+
+/* Determine the units and system implied by the <Redshift> element.
+ The returned system is AST__REDSHIFT if there is no unit attribute in
+ the <Redshift> element, and is AST__VRADIO otherwise. */
+ fsys = RedshiftSys( this, elem, &unit, 1, status );
+
+/* If no system has been set in the supplied Frame, set it now to the system
+ determined above. */
+ if( !astTestSystem( frm ) ) {
+ astSetSystem( frm, fsys );
+
+/* The ReddshiftSys function above always returns AST__VRADIO if the velocity
+ is not dimensionless. However, the supplied Frame may have System set
+ explicitly to AST__VOPTICAL. In this case change the "fsys" value to use
+ AST__VOPTICAL. */
+ } else if( fsys != AST__REDSHIFT ) {
+ fsys = astGetSystem( frm );
+ if( fsys == AST__REDSHIFT ) fsys = AST__VRADIO;
+ }
+
+/* If the supplied Frame has no set units, set them now to the units of
+ the Redshift element (if any, and if the redshift is not dimensionless). */
+ if( unit && fsys != AST__REDSHIFT &&
+ astGetSystem( frm ) != AST__REDSHIFT && !astTestUnit( frm, 0 ) ) {
+ astSetUnit( frm, 0, unit );
+ }
+
+/* The values represented by the <Redshift> element may not be in the same
+ system, units, etc as the supplied SpecFrame. We will need to be able to
+ convert from one to the other, so create a SpecFrame describing the
+ system and units used by the <Redshift> element. */
+ sf1 = astCopy( frm );
+ astSetSystem( sf1, fsys );
+ if( unit ) {
+ astSetUnit( sf1, 0, unit );
+ unit = astFree( unit );
+ }
+
+/* Find the Mapping from Redshift value to the supplied SpecFrame value */
+ fs = astConvert( sf1, frm, "" );
+ if( fs ) {
+ map = astGetMapping( fs, AST__BASE, AST__CURRENT );
+ fs = astAnnul( fs );
+ } else {
+ map = NULL;
+ Report( this, elem, FAILURE, "connot convert AstroCoords "
+ "redshift values to the required redshift system", status );
+ }
+
+/* If this Redshift contains a Value which can be read, obtain it. */
+ if( scan->count[ 2 ] > 0 ) {
+ name = astXmlGetName( scan->el[ 2 ][ 0 ] );
+ if( name && !strcmp( name, "Value" ) ) {
+ v = ElemValueD( this, scan->el[ 2 ][ 0 ], AST__BAD, status );
+
+/* Convert the value into the supplied SpecFrame system. Create an
+ Interval describing it and store it in the returned ancillary keyMap.
+ Note we create an Interval rather than a PintList since the Prism
+ class can only extrude using Intervals. */
+ astTran1( map, 1, &v, 1, &tmp );
+ r = (AstRegion *) astInterval( frm, &tmp, &tmp, NULL, "", status ) ;
+ astMapPut0A( *anc, AST__STCVALUE, r, NULL );
+ r = astAnnul( r );
+ }
+ }
+
+/* Check for Error values in the Redshift. */
+ if( scan->count[ 1 ] > 0 ) {
+
+/* Issue a warning if more than 1 Error value was found. */
+ if( scan->count[ 1 ] > 1 ) {
+ Report( this, elem, WARNING, "contains more than one <Error>"
+ " element. AST can only use the first", status );
+ }
+
+/* Get the first Error value. */
+ v = ElemValueD( this, scan->el[1][0], AST__BAD, status );
+ if( v != AST__BAD ) {
+
+/* Create the upper and lower limits of an error bar centred on zero. */
+ ubnd[ 0 ] = 0.5*fabs( v );
+ lbnd[ 0 ] = -ubnd[ 0 ];
+
+/* Create an Interval within the Frame represented by the Redshift element.
+ Map it into the supplied Frame. Simplify it. Store in the returned
+ ancillary KeyMap. */
+ r = (AstRegion *) astInterval( sf1, lbnd, ubnd, NULL, "", status );
+ r2 = astMapRegion( r, map, frm );
+ result = astSimplify( r2 );
+ astMapPut0A( *anc, AST__STCERROR, result, NULL );
+ r2 = astAnnul( r2 );
+ r = astAnnul( r );
+ }
+ }
+
+/* Check for Resolution values in the Redshift. */
+ if( scan->count[ 3 ] > 0 ) {
+
+/* Issue a warning if more than 1 value was found. */
+ if( scan->count[ 3 ] > 1 ) {
+ Report( this, elem, WARNING, "contains more than one <Resolution>"
+ " element. AST can only use the first", status );
+ }
+
+/* Get the first value. */
+ v = ElemValueD( this, scan->el[3][0], AST__BAD, status );
+ if( v != AST__BAD ) {
+
+/* Create the upper and lower limits of an interval centred on zero. */
+ ubnd[ 0 ] = 0.5*fabs( v );
+ lbnd[ 0 ] = -ubnd[ 0 ];
+
+/* Create an Interval within the Frame represented by the Redshift element.
+ Map it into the supplied Frame. Simplify it. Store in the returned
+ ancillary KeyMap. */
+ r = (AstRegion *) astInterval( sf1, lbnd, ubnd, NULL, "", status );
+ r2 = astMapRegion( r, map, frm );
+ r3 = astSimplify( r2 );
+ astMapPut0A( *anc, AST__STCRES, r3, NULL );
+ r3 = astAnnul( r3 );
+ r2 = astAnnul( r2 );
+ r = astAnnul( r );
+ }
+ }
+
+/* Check for Size values in the Redshift. */
+ if( scan->count[ 4 ] > 0 ) {
+
+/* Issue a warning if more than 1 value was found. */
+ if( scan->count[ 4 ] > 1 ) {
+ Report( this, elem, WARNING, "contains more than one <Size>"
+ " element. AST can only use the first", status );
+ }
+
+/* Get the first value. */
+ v = ElemValueD( this, scan->el[4][0], AST__BAD, status );
+ if( v != AST__BAD ) {
+
+/* Create the upper and lower limits of an interval centred on zero. */
+ ubnd[ 0 ] = 0.5*fabs( v );
+ lbnd[ 0 ] = -ubnd[ 0 ];
+
+/* Create an Interval within the Frame represented by the Redshift element.
+ Map it into the supplied Frame. Simplify it. Store in the returned
+ ancillary KeyMap. */
+ r = (AstRegion *) astInterval( sf1, lbnd, ubnd, NULL, "", status );
+ r2 = astMapRegion( r, map, frm );
+ r3 = astSimplify( r2 );
+ astMapPut0A( *anc, AST__STCSIZE, r3, NULL );
+ r3 = astAnnul( r3 );
+ r2 = astAnnul( r2 );
+ r = astAnnul( r );
+ }
+ }
+
+/* Check for PixSize values in the Redshift. */
+ if( scan->count[ 5] > 0 ) {
+
+/* Issue a warning if more than 1 value was found. */
+ if( scan->count[ 5 ] > 1 ) {
+ Report( this, elem, WARNING, "contains more than one <PixSize>"
+ " element. AST can only use the first", status );
+ }
+
+/* Get the first value. */
+ v = ElemValueD( this, scan->el[5][0], AST__BAD, status );
+ if( v != AST__BAD ) {
+
+/* Create the upper and lower limits of an interval centred on zero. */
+ ubnd[ 0 ] = 0.5*fabs( v );
+ lbnd[ 0 ] = -ubnd[ 0 ];
+
+/* Create an Interval within the Frame represented by the Redshift element.
+ Map it into the supplied Frame. Simplify it. Store in the returned
+ ancillary KeyMap. */
+ r = (AstRegion *) astInterval( sf1, lbnd, ubnd, NULL, "", status );
+ r2 = astMapRegion( r, map, frm );
+ r3 = astSimplify( r2 );
+ astMapPut0A( *anc, AST__STCPIXSZ, r3, NULL );
+ r3 = astAnnul( r3 );
+ r2 = astAnnul( r2 );
+ r = astAnnul( r );
+ }
+ }
+
+/* Free resources. */
+ scan = FreeIVOAScan( scan, status );
+ sf1 = astAnnul( sf1 );
+ map = astAnnul( map );
+ }
+
+/* Return NULL if an error occurred. */
+ if( !astOK ) result = astAnnul( result );
+
+/* Return the result */
+ return result;
+
+}
+
+static AstSystemType RedshiftSys( AstXmlChan *this, AstXmlElement *elem,
+ char **unit, int report, int *status ){
+/*
+* Name:
+* RedshiftSys
+
+* Purpose:
+* Determine the redshift system described by the attributes in a
+* given element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstSystemType RedshiftSys( AstXmlChan *this, AstXmlElement *elem,
+* char **unit, int report, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function determines the redshift system described by the unit and
+* vel_time_unit attributes in the supplied element. It optionally reports
+* an error if the units are not recognised.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA element containing the unit attribute to be used.
+* unit
+* Pointer to a location at which to return a pointer to a
+* dynamically allocated string in which is stored the total units string
+* implied by the unit and vel_time_unit attributes. This string
+* should be freed when no longer needed using astFree. A NULL
+* pointer is returned if either of the two attributes (unit and
+* vel_time_unit) is not found in the supplied element, or if an error
+* occurs.
+* report
+* If non-zero, then a failure is reported if the spectral system
+* cannot be determined from the supplied string.
+* status
+* Pointer to the inherited status variable.
+
+* Returned:
+* The redshift system (radio velocity if both unit and vel_time_unit
+* attributes are present in the supplied element, or dimensionaless
+* redshift otherwise).
+
+*/
+
+/* Local Variables: */
+ const char *punit; /* Pointer to positional unit string */
+ const char *tunit; /* Pointer to time unit string */
+ int pl; /* Length of punit string */
+ int tl; /* Length of tunit string */
+
+/* Initialise. */
+ *unit = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return AST__BADSYSTEM;
+
+/* Get the Unit attribute from the element (this describes units of position) */
+ punit = astXmlGetAttributeValue( elem, "unit" );
+ if( punit ) {
+
+/* Check it is a linear measure (not angular). */
+ if( strstr( "m", punit ) &&
+ strstr( "km", punit ) &&
+ strstr( "mm", punit ) &&
+ strstr( "AU", punit ) &&
+ strstr( "kpc", punit ) &&
+ strstr( "Mpc", punit ) &&
+ strstr( "lyr", punit ) ) {
+ if( report ) Report( this, elem, FAILURE, "contains an angular unit attribute", status );
+ }
+ }
+
+/* Get the vel_time_unit attribute from the element (this describes units of
+ time). If OK, construct the total unit string (eg "km/h") . */
+ tunit = astXmlGetAttributeValue( elem, "vel_time_unit" );
+ if( tunit ) {
+ if( !punit ) {
+ if( report ) Report( this, elem, FAILURE, "contains time units but not position units - assuming Z", status );
+ } else {
+ pl = strlen( punit );
+ tl = strlen( tunit );
+ *unit = astMalloc( (size_t)( pl + tl + 2 ) );
+ if( *unit ) {
+ strcpy( *unit, punit );
+ (*unit)[ pl ] = '/';
+ strcpy( *unit + pl + 1, tunit );
+ }
+ }
+
+ } else if( punit ) {
+ if( report ) Report( this, elem, FAILURE, "contains position units but not time units - assuming Z", status );
+ }
+
+/* Return a default system (radio velocity if both units are present,
+ dimensionless redshift otherwise). */
+ return ( punit && tunit ) ? AST__VRADIO : AST__REDSHIFT;
+}
+
+static AstRegion *RegionReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, int *status ){
+/*
+* Name:
+* RegionReader
+
+* Purpose:
+* Make an AST Region from any subclass of IVOA Region element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *RegionReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* element which can be of any subclass of Region.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA element.
+* frm
+* Pointer to the 2D Frame in which the returned Region should be
+* defined. If the Unit attribute is not set, this function will
+* set it to the value supplied in "unit" before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstRegion *new; /* Pointer to returned Region */
+ const char *name; /* Region type */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Process each supported type of STC Region. */
+ name = astXmlGetName( elem );
+ if( !strcmp( name, "Intersection" ) ) {
+ new = IntersectionReader( this, elem, frm, status );
+
+ } else if( !strcmp( name, "Union" ) ) {
+ new = UnionReader( this, elem, frm, status );
+
+ } else if( !strcmp( name, "Negation" ) ) {
+ new = NegationReader( this, elem, frm, status );
+
+ } else if( !strcmp( name, "AllSky" ) ) {
+ new = AllSkyReader( this, elem, frm, status );
+
+ } else if( !strcmp( name, "Circle" ) ) {
+ new = CircleReader( this, elem, frm, status );
+
+ } else if( !strcmp( name, "Ellipse" ) ) {
+ new = EllipseReader( this, elem, frm, status );
+
+ } else if( !strcmp( name, "Polygon" ) ) {
+ new = PolygonReader( this, elem, frm, status );
+
+ } else if( !strcmp( name, "Box" ) ) {
+ new = BoxReader( this, elem, frm, status );
+
+ } else if( !strcmp( name, "Convex" ) ) {
+ new = ConvexReader( this, elem, frm, status );
+
+ } else {
+ astError( AST__INTER, "RegionReader(XmlChan): Does not yet "
+ "support \"%s\" regions (internal AST programming "
+ "error).", status, name );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static AstXmlElement *Remove( AstXmlChan *this, AstXmlElement *element, int *status ) {
+/*
+* Name:
+* Remove
+
+* Purpose:
+* Remove an element from the current container element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstXmlElement *Remove( AstXmlChan *this, AstXmlElement *element, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function removes the specified element from the current
+* container element, and then annuls the removed element. An error is
+* reported if the element being removed contains anything other than
+* comments, "isa" elements and blank character data (all contents should
+* have been consumed by the process of reading the object).
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* element
+* Pointer to the XML element to be removed.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A NULL pointer is returned.
+
+*/
+
+/* Local Variables: */
+ AstXmlContentItem *item; /* Item */
+ const char *def; /* Pointer to default attribute value */
+ int i; /* Index of current item */
+ int nitem; /* Number of items still in the element */
+
+/* Check the global error status, and the supplied element. */
+ if ( !astOK || !element ) return NULL;
+
+/* Check we have a container from which to remove the element. If so,
+ check that the container is the elements parent. If so, remove the
+ element from its parent container. */
+ if( this->container ) {
+ if( (AstXmlParent *) this->container != astXmlGetParent( element ) ){
+ astError( AST__INTER, "Remove(XmlChan): Supplied element is not "
+ "contained within the current container element (internal "
+ "AST programming error)." , status);
+ } else {
+ astXmlRemoveItem( element );
+ }
+ }
+
+/* Check that the element being removed is empty (apart from comments,
+ defaulted values and "isa" elements). */
+ nitem = astXmlGetNitem( element );
+ for( i = 0; i < nitem; i++ ) {
+ item = astXmlGetItem( element, i );
+ if( astXmlCheckType( item, AST__XMLELEM ) ) {
+
+/* See if this element represents a default value */
+ def = astXmlGetAttributeValue( item, DEFAULT );
+
+/* Default values and "isa" elements are OK. */
+ if( ( !def || strcmp( def, TRUE ) ) && astOK &&
+ strcmp( astXmlGetName( item ), ISA ) ) {
+
+/* Remove any "definedby" attribute (added by ReadClassData) so that it
+ does not appear in the error message. */
+ if( astXmlGetAttributeValue( item, DEFINEDBY ) ) {
+ astXmlRemoveAttr( item, DEFINEDBY, NULL );
+ }
+
+/* Report the error. */
+ if( astOK ) astError( AST__BADIN, "astRead(XmlChan): The following "
+ "tag was not recognised as valid input within "
+ "a %s: %s", status, astXmlGetName( element ),
+ GetTag( (AstXmlObject *) item, 1, status ) );
+ break;
+ }
+
+/* Character data is OK so long as it contains only white space */
+ } else if( astXmlCheckType( item, AST__XMLBLACK ) ) {
+ astError( AST__BADIN, "astRead(XmlChan): The following character "
+ "data was not recognised as valid input within a %s: %s", status,
+ astXmlGetName( element ), astXmlGetValue( item, 0 ) );
+ break;
+
+ } else if( astXmlCheckType( item, AST__XMLCDATA ) ) {
+ astError( AST__BADIN, "astRead(XmlChan): The following CDATA section "
+ "data was not recognised as valid input within a %s: %s", status,
+ astXmlGetName( element ), astXmlGetValue( item, 0 ) );
+ break;
+
+ } else if( astXmlCheckType( item, AST__XMLPI ) ) {
+ astError( AST__BADIN, "astRead(XmlChan): The following processing "
+ "instruction was not recognised as valid input within "
+ "a %s: %s", status, astXmlGetName( element ), GetTag( (AstXmlObject *) item, 1, status ) );
+ break;
+ }
+ }
+
+/* Remove the element from its parent and the annul it. */
+ astXmlRemoveItem( element );
+ astXmlAnnul( element );
+
+/* Return a NULL pointer. */
+ return NULL;
+}
+
+static void Report( AstXmlChan *this, AstXmlElement *elem, int severity,
+ const char *msg, int *status ){
+/*
+* Name:
+* Report
+
+* Purpose:
+* Handle problems reading supplied XML.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* void Report( AstXmlChan *this, AstXmlElement *elem, int severity,
+* const char *msg, int *status )
+
+* Class Membership:
+* XmlChan member function
+
+* Description:
+* This function handles conditions which arise whilst interpreting
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the XmlElement which cound not be interpreted.
+* severity
+* WARNING (in which case the message is added to a list of
+* warnings, but execution continues), or FAILURE, in which case
+* an error is reported using astError, or RESET in which case any
+* warnings stored in the XmlChan are removed ("elem" and "msg" are
+* ignored).
+* msg
+* A message describing the condition.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ char *text; /* Pointer to tformatted element text */
+ const char *name; /* Element name */
+
+ if( severity == RESET ) astAddWarning( this, 0, NULL, NULL, status );
+
+ if( !astOK ) return;
+
+ if( severity == WARNING && !astGetStrict( this ) ) {
+ name = astXmlGetName( elem );
+ astAddWarning( this, 1, "astRead(%s): Warning whilst reading %s %s "
+ "element: %s", "astRead", status, astGetClass( this ),
+ ANA(name), name, msg );
+ } else {
+ text = (char *) astXmlGetTag( elem, 1 );
+ astError( AST__BADIN, "astRead(%s): Failed to read %s element: %s", status,
+ astGetClass( this ), text, msg );
+ text = astFree( text );
+ }
+}
+
+static IVOAScan *ScanIVOAElement( AstXmlChan *this, AstXmlElement *elem, int n,
+ const char *names[], int min[], int max[], int *status ){
+/*
+* Name:
+* ScanIVOAElement
+
+* Purpose:
+* Identify required sub-elements within an IVOA element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* IVOAScan *ScanIVOAElement( AstXmlChan *this, AstXmlElement *elem, int n,
+* const char *names[], int min[], int max[], int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function searches the supplied XML element for named sub-elements.
+* A structure is returned containing the number of sub-elements found
+* with each name, and pointers to the sub-elements. This structure
+* should be freed using FreeIVOAScan when no longer needed.
+*
+* Reports are made about any content in the supplied element which is
+* not specified in the list of known sub-element names (excepting
+* comments and white space).
+*
+* Reports are also made if the number of sub-elements found with each
+* known name is inappropriate (the minimum and maximum allowed
+* occurrences of each name is specified by the caller).
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the XML element to be searched.
+* n
+* The number of names supplied in "names"
+* names
+* An array holding pointers to strings giving the names of the known
+* sub-elements. Each string may be either a single element name,
+* or a set of element names separated by "|" (the string must
+* also start and end with a "|"). If a set is supplied, then the
+* associated "min" and "max" values specify the minimum and maximum
+* total number of occurrences of all names in the set, and the
+* occurrence count stored in the returned structure gives the total
+* number of occurrences of all names in the set.
+* min
+* An array holding the mimimum number of occrrences of each name within
+* the element being searched. Supplied in the same order as the names
+* themselves.
+* max
+* An array holding the maximum number of occrrences of each name within
+* the element being searched. Supplied in the same order as the names
+* themselves.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the IVOAScan structure holding the results of the
+* scan. A NULL pointer is returned unless all names have at least
+* their minimum number of occurrences in the supplied element. A NULL
+* pointer is returned if an error occurs.
+
+*/
+
+/* Local Variables: */
+ AstXmlContentItem *item; /* Current content item */
+ IVOAScan *result; /* Pointer to returned structure */
+ char *text; /* Pointer formatted item string */
+ char buff[ 200 ]; /* Message buffer */
+ const char *name; /* Pointer to element name string */
+ const char *w1; /* Pointer to word to use in message */
+ const char *w2; /* Pointer to word to use in message */
+ const char *p; /* Pointer to start of name in string */
+ int i; /* Index of current content item */
+ int j; /* Index of current name */
+ int k; /* Index of current occurrence of name */
+ int l; /* Length of element name */
+ int known; /* Was content item known? */
+ int nitem; /* No. of content items in supplied element */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Initialise a structure to hold the results of the scan. */
+ result = astMalloc( sizeof(IVOAScan) );
+ if( result ) {
+ result->n = n;
+ result->count = astMalloc( sizeof(int)*(size_t)n);
+ result->el = astMalloc( sizeof(AstXmlElement **)*(size_t)n);
+ if( result->el ) {
+ for( j = 0; j < n; j++ ) {
+ result->count[ j ] = 0;
+ result->el[ j ] = NULL;
+ }
+ }
+ }
+
+/* Loop round all items in the elements contents. */
+ if( astOK ) {
+ nitem = astXmlGetNitem( elem );
+ for( i = 0; i < nitem; i++ ) {
+ item = astXmlGetItem( elem, i );
+ known = 1;
+
+/* If it is not an XML element, it is not known. */
+ if( !astXmlCheckType( item, AST__XMLELEM ) ) {
+ known = 0;
+
+/* If it is an element, get the name of the element. */
+ } else {
+ name = astXmlGetName( item );
+
+/* See if this name is in the supplied list of known names. */
+ known = 0;
+ j = 0;
+ if( name ) {
+ l = strlen( name );
+ for( j = 0; j < n; j++ ) {
+ p = strstr( names[ j ], name );
+ if( p ){
+ if( p == names[ j ] ) {
+ if( p[ l ] == 0 || p[ l ] == '|' ) {
+ known = 1;
+ break;
+ }
+ } else {
+ if( p[ -1 ] == '|' && ( p[ l ] == 0 || p[ l ] == '|' ) ) {
+ known = 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+/* If it is known, store the element in the results structure */
+ if( known ) {
+ k = ( result->count[ j ] )++;
+ result->el[ j ]= astGrow( result->el[ j ], k + 1,
+ sizeof( AstXmlElement * ) );
+ if( result->el[ j ] ) {
+ result->el[ j ][ k ] = (AstXmlElement *) item;
+ } else {
+ break;
+ }
+ }
+ }
+
+/* If this content item was not known, issue a warning unless it is a comment
+ or white space. */
+ if( !known && !astXmlCheckType( item, AST__XMLCOM ) &&
+ !astXmlCheckType( item, AST__XMLWHITE ) ) {
+ text = (char *) astXmlFormat( item );
+ if( text ) {
+ if( strlen( text ) > 30 ) text[ 30 ] = 0;
+ sprintf( buff, "contains the following which is being ignored: \"%s\"",
+ text );
+ text = astFree( text );
+ Report( this, elem, WARNING, buff, status );
+ }
+ }
+ }
+
+/* Now check that the number of instances of each element found is OK.
+ Report warnings or failures if not. */
+ if( astOK ) {
+ for( j = 0; j < n; j++ ) {
+ if( result->count[ j ] < min[ j ] ) {
+ w1 = ( result->count[ j ] == 1 ) ? "element" : "elements";
+ w2 = ( min[ j ] == 1 ) ? "is" : "are";
+ sprintf( buff, "contains %d <%s> %s but at least %d %s needed",
+ result->count[ j ], names[ j ], w1, min[ j ], w2 );
+ Report( this, elem, FAILURE, buff, status );
+
+ } else if ( result->count[ j ] > max[ j ] ) {
+ w1 = ( result->count[ j ] == 1 ) ? "element" : "elements";
+ w2 = ( max[ j ] == 1 ) ? "is" : "are";
+ sprintf( buff, "contains %d <%s> %s but no more than %d %s "
+ "allowed (only the first will be used)",
+ result->count[ j ], names[ j ], w1, max[ j ], w2 );
+ Report( this, elem, WARNING, buff, status );
+ }
+ }
+ }
+ }
+
+/* Return NULL if an error occurred. */
+ if( !astOK ) result = FreeIVOAScan( result, status );
+
+/* Return the results structure.*/
+ return result;
+}
+
+static void SetAttrib( AstObject *this_object, const char *setting, int *status ) {
+/*
+* Name:
+* SetAttrib
+
+* Purpose:
+* Set an attribute value for a XmlChan.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* void SetAttrib( AstObject *this, const char *setting )
+
+* Class Membership:
+* XmlChan member function (over-rides the astSetAttrib protected
+* method inherited from the Channel class).
+
+* Description:
+* This function assigns an attribute value for a XmlChan, 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 XmlChan.
+* setting
+* Pointer to a null terminated string specifying the new attribute
+* value.
+*/
+
+/* Local Variables: */
+ AstXmlChan *this; /* Pointer to the XmlChan structure */
+ int ival; /* Integer attribute value */
+ int len; /* Length of setting string */
+ int nc; /* Number of characters read by "astSscanf" */
+ int pr; /* Offset to start of string */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) 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. */
+
+/* XmlLength */
+/* ----------*/
+ if ( nc = 0,
+ ( 1 == astSscanf( setting, "xmllength= %d %n", &ival, &nc ) )
+ && ( nc >= len ) ) {
+ astSetXmlLength( this, ival );
+
+/* XmlFormat */
+/* ----------*/
+ } else if( nc = 0,
+ ( 0 == astSscanf( setting, "xmlformat=%n%*[^\n]%n", &ival, &nc ) )
+ && ( nc >= len ) ) {
+
+ nc = astChrLen( setting + ival );
+
+ if( !Ustrncmp( setting + ival, NATIVE_STRING, nc, status ) ){
+ astSetXmlFormat( this, NATIVE_FORMAT );
+
+ } else if( !Ustrncmp( setting + ival, QUOTED_STRING, nc, status ) ){
+ astSetXmlFormat( this, QUOTED_FORMAT );
+
+ } else if( !Ustrncmp( setting + ival, IVOA_STRING, nc, status ) ){
+ astSetXmlFormat( this, IVOA_FORMAT );
+
+ } else {
+ astError( AST__BADAT, "astSet(%s): Unknown XML format '%s' "
+ "requested for a %s.", status, astGetClass( this ), setting + ival,
+ astGetClass( this ) );
+ }
+
+/* XmlPrefix */
+/* ----------*/
+ } else if ( nc = 0, ( 0 == astSscanf( setting, "xmlprefix=%n%*[^\n]%n", &pr, &nc ) )
+ && ( nc >= len ) ) {
+ astSetXmlPrefix( this, setting + pr );
+
+/* 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 SinkWrap( void (* sink)( const char * ), const char *line, int *status ) {
+/*
+* Name:
+* SinkWrap
+
+* Purpose:
+* Wrapper function to invoke a C XmlChan sink function.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* void SinkWrap( void (* sink)( const char * ), const char *line, int *status )
+
+* Class Membership:
+* XmlChan 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 XmlChan 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 XmlChan source function.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* char *SourceWrap( const char *, int *status(* source)( void ) )
+
+* Class Membership:
+* XmlChan 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 XmlChan
+* 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 AstObject *SpaceFrameReader( AstXmlChan *this,
+ AstXmlElement *elem, int *status ) {
+/*
+* Name:
+* SpaceFrameReader
+
+* Purpose:
+* Make an AST Object from an IVOA SpaceFrame element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstObject *SpaceFrameReader( AstXmlChan *this, AstXmlElement *elem, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Object from the supplied IVOA
+* SpaceFrame element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA SpaceFrame element.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Object.
+
+*/
+
+/* Local Variables: */
+ AstObject *new; /* Pointer to returned Object */
+ AstXmlElement *el; /* Pointer to sub-element */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *dom; /* Domain string for returned SkyFrame */
+ const char *eq; /* Equinox string for returned SkyFrame */
+ const char *names[4]; /* Names of the subelements to be searched for */
+ const char *sys; /* System for returned Frame */
+ int ignore_h; /* Ignore 3rd spherical axis? */
+ int max[4]; /* Max allowed occurrences of each name */
+ int min[4]; /* Min allowed occurrences of each name */
+ int isgeod; /* Is the system geodetic lon/lat? */
+ int isgeoc; /* Is the system geocentric lon/lat? */
+ int need_eq; /* Does system need an equinox? */
+
+/* Initialise */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "ICRS|GALACTIC_II|SUPER_GALACTIC|HEE|FK4|FK5|ECLIPTIC|GEO_C|GEO_D";
+ names[ 1 ] = "TOPOCENTER";
+ names[ 2 ] = "Name";
+ names[ 3 ] = "SPHERICAL|CARTESIAN|UNITSPHERE|POLAR";
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ max[ 2 ] = 1;
+ max[ 3 ] = 1;
+ min[ 0 ] = 1;
+ min[ 1 ] = 1;
+ min[ 2 ] = 0;
+ min[ 3 ] = 1;
+ scan = ScanIVOAElement( this, elem, 4, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the sky coordinate system specified in the element. */
+ sys = astXmlGetName( scan->el[0][0] );
+ need_eq = 0;
+ dom = NULL;
+
+/* If the system is geodetic or geocentric, ignore height information if
+ supplied. This is so we can get an approximation to an observatory
+ position given in 3D, for use with SpecFrame. */
+ ignore_h = 0;
+ isgeod = sys && !strcmp( sys, "GEO_D" );
+ isgeoc = sys && !strcmp( sys, "GEO_C" );
+ if( isgeod || isgeoc ){
+ if( AttrValueI( this, scan->el[3][0], "coord_naxes", 2, status ) != 2 ) {
+ Report( this, elem, WARNING, "contains 3D spherical spatial "
+ "coords (unsupported by AST - height information will "
+ "be ignored)", status );
+ ignore_h = 1;
+ }
+
+/* If the system is geodetic ignore any attributes specifying a reference
+ spheroid. */
+ if( isgeod && astXmlGetNattr( scan->el[0][0] ) > 0 ) {
+ Report( this, elem, WARNING, "contains reference spheroid "
+ "(unsupported by AST - default values will be used)", status );
+ }
+ }
+
+/* Check that the spatial axes are longitude/latitude */
+ if( strcmp( "SPHERICAL", astXmlGetName( scan->el[3][0] ) ) ){
+ Report( this, elem, FAILURE, "contains non-spherical spatial "
+ "coords (currently unsupported by AST)", status );
+
+ } else if( !ignore_h && AttrValueI( this, scan->el[3][0], "coord_naxes", 2, status ) != 2 ) {
+ Report( this, elem, FAILURE, "contains 3D spherical spatial "
+ "coords (currently unsupported by AST)", status );
+
+ } else if( AttrValueB( this, scan->el[3][0], "coord_vel", 0, status ) ) {
+ Report( this, elem, FAILURE, "contains velocity coords", status );
+
+/* Now check for the supported sky coordinate systems and translate to the
+ equivalent AST value. Note if the system needs an equinox to qualify it. */
+ } else if( !strcmp( sys, "GALACTIC_II" ) ){
+ sys = "GALACTIC";
+ need_eq = 0;
+
+ } else if( !strcmp( sys, "SUPER_GALACTIC" ) ){
+ sys = "SUPERGALACTIC";
+ need_eq = 0;
+
+ } else if( !strcmp( sys, "HEE" ) ){
+ sys = "HELIOECLIPTIC";
+ need_eq = 0;
+
+ } else if( !strcmp( sys, "FK4" ) ) {
+ sys = "FK4";
+ need_eq = 1;
+
+ } else if( !strcmp( sys, "FK5" ) ) {
+ sys = "FK5";
+ need_eq = 1;
+
+ } else if( !strcmp( sys, "ECLIPTIC" ) ) {
+ sys = "ECLIPTIC";
+ need_eq = 1;
+
+ } else if( isgeoc ) {
+ dom = "GEO_C";
+ sys = "UNKNOWN";
+ need_eq = 0;
+
+ } else if( isgeod ) {
+ dom = "GEO_D";
+ sys = "UNKNOWN";
+ need_eq = 0;
+
+ } else {
+ sys = "ICRS";
+ need_eq = 0;
+ }
+
+/* Extract the equinox if required. */
+ if( need_eq ) {
+ el = FindElement( this, scan->el[0][0], "Equinox", status );
+ if( el ) {
+ eq = astXmlGetValue( el, 0 );
+ if( !eq ) Report( this, scan->el[0][0], WARNING, "contains an "
+ "<Equinox> element which is not simply "
+ "character data. The AST default (B1950 "
+ "or J2000) will be used", status );
+ } else {
+ eq = NULL;
+ Report( this, scan->el[0][0], WARNING, "contains no <Equinox> element. "
+ "The AST default (B1950 or J2000) will be used", status );
+ }
+
+ } else {
+ eq = NULL;
+ }
+
+/* Create a suitable SkyFrame. */
+ new = (AstObject *) astSkyFrame( "system=%s", status, sys);
+ if( eq ) astSetC( new, "Equinox", eq );
+ if( dom ) astSetDomain( new, dom );
+
+ if( isgeod ){
+ astSetLabel( new, 0, "Geodetic longitude" );
+ astSetLabel( new, 1, "Geodetic latitude" );
+
+ } else if( isgeoc ){
+ astSetLabel( new, 0, "Geocentric longitude" );
+ astSetLabel( new, 1, "Geocentric latitude" );
+ }
+
+/* If the SpaceFrame has a <Name> element use it as the Frame title. */
+ if( scan->count[2] ) astSetTitle( new, astXmlGetValue( scan->el[2][0], 0 ) );
+
+/* Free resources */
+ scan = FreeIVOAScan( scan, status );
+
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Object. */
+ return (AstObject *) new;
+}
+
+static AstSystemType SpecSys( AstXmlChan *this, AstXmlElement *elem,
+ const char *unit, int report, int *status ) {
+/*
+* Name:
+* SpecSys
+
+* Purpose:
+* Determine the spectral system described by a given units string.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstSystemType SpecSys( AstXmlChan *this, AstXmlElement *elem,
+* const char *unit, int report, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function determines the spectral system described by a given units
+* string. It optionally reports an error if the string is not
+* recognised.
+
+* Parameters:
+* this
+* Pointer to the XmlChan. Only used if "report" is non-zero.
+* elem
+* Pointer to the IVOA element to which the unit relates. Only used
+* if "report" is non-zero.
+* unit
+* Pointer to the units string.
+* report
+* If non-zero, then a failure is reported if the spectral system
+* cannot be determined from the supplied string.
+* status
+* Pointer to the inherited status variable.
+
+* Returned:
+* The spectral system, or AST__BADSYSTEM if an error occurs.
+
+*/
+
+/* Local Variables: */
+ AstMapping *map; /* Mapping from supplied unit to default unitl */
+ AstSystemType sys; /* System value corresponding to "unit" */
+ char buff[200]; /* Buffer for failure message */
+
+/* Initialise */
+ sys = AST__BADSYSTEM;
+
+/* Check inherited status */
+ if( !astOK ) return sys;
+
+/* See if a Mapping can be found from the supplied units to "Hz". If
+ so, the supplied units are assumed to describe frequency. */
+ map = astUnitMapper( unit, "Hz", NULL, NULL );
+ if( map ) {
+ sys = AST__FREQ;
+
+/* Otherwise, see if a Mapping can be found from the supplied units to
+ "m" (metre). If so, the supplied units are assumed to describe wavelength. */
+ } else {
+ map = astUnitMapper( unit, "m", NULL, NULL );
+ if( map ) {
+ sys = AST__WAVELEN;
+
+/* Otherwise, see if a Mapping can be found from the supplied units to
+ "J" (Joule). If so, the supplied units are assumed to describe energy. */
+ } else {
+ map = astUnitMapper( unit, "J", NULL, NULL );
+ if( map ) {
+ sys = AST__ENERGY;
+
+/* Otherwise, see if a Mapping can be found from the supplied units to
+ "m^-1" (per metre). If so, the supplied units are assumed to describe
+ wave number. */
+ } else {
+ map = astUnitMapper( unit, "m^-1", NULL, NULL );
+ if( map ) {
+ sys = AST__WAVENUM;
+
+/* Otherwise, report an error if requested. */
+ } else if( report ){
+ sprintf( buff, "contains unsupported spectral units \"%s\"", unit );
+ Report( this, elem, FAILURE, buff, status );
+ }
+ }
+ }
+ }
+
+/* Free resources */
+ if( map ) map = astAnnul( map );
+
+/* Return the result. */
+ return sys;
+}
+
+static AstRegion *SpectralReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, double *rf,
+ AstKeyMap **anc, int *status ){
+/*
+* Name:
+* SpectralReader
+
+* Purpose:
+* Modify a Frame to take account of an STC <Spectral> element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *SpectralReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, double *rf,
+* AstKeyMap **anc, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function reads the supplied STC <Spectral> element, and uses it,
+* if possible, to create the uncertainty associated with the spectral
+* axis in the supplied Frame.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Spectral element.
+* frm
+* Pointer to the 1D spectral Frame.
+* rf
+* Point to double in which to return the rest frequency to be used
+* with any redshift axis. Value is returned in Hz. AST__BAD will
+* be returned if no rest frequency is found.
+* anc
+* Address of a location at which to put a pointer to a newly
+* created KeyMap. This KeyMap will contain ancillary information
+* from the Spectral. The keys identify the item of ancillary
+* information (Name, Value, Error, Resolution, Size, Pixel Size).
+* The value associated with the Name key is string containing
+* the Name item from the Spectral. The value associated with each of
+* the other keys is a pointer to a 1D Region within the supplied
+* Frame, corresponding to the value, error, resolution, etc. Keys
+* will not be present in the returned KeyMap if the corresponding
+* item of ancillary information is not present in the Spectral. A
+* NULL pointer is returned if there is no ancillary information at all.
+* status
+* Pointer to the inherited status variable.
+
+* Returned:
+* The uncertainty Region, or NULL if the supplied Spectral element
+* does not specify an uncertainty.
+
+*/
+
+/* Local Variables: */
+ AstFrameSet *fs; /* FrameSet connecting "sf1" and "sf2" */
+ AstMapping *map; /* Mapping from <Spectral> Frame to supplied Frame */
+ AstRegion *r2; /* Region mapped into returned Frame */
+ AstRegion *r3; /* Simplified Region mapped into returned Frame */
+ AstRegion *r; /* Original Region */
+ AstRegion *result; /* Returned uncertainty Region */
+ AstSpecFrame *sf1; /* SpecFrame describing value element */
+ AstSpecFrame *sf2; /* SpecFrame describing returned "rf" value */
+ AstSystemType fsys; /* Spectral system from supplied Stc */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *name; /* Pointer to XML element name */
+ const char *names[6]; /* Names of the subelements to be searched for */
+ const char *unit; /* Pointer to Spectral's unit attribute string */
+ double lbnd[ 1 ] ; /* Lower interval bounds */
+ double ubnd[ 1 ] ; /* Upper interval bounds */
+ double tmp; /* Mapped value */
+ double v; /* Axis value */
+ int max[6]; /* Max allowed occurrences of each name */
+ int min[6]; /* Min allowed occurrences of each name */
+
+/* Initialise */
+ result = NULL;
+ *rf = AST__BAD;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "Name";
+ names[ 1 ] = "Error";
+ names[ 2 ] = "Value";
+ names[ 3 ] = "Resolution";
+ names[ 4 ] = "Size";
+ names[ 5 ] = "PixSize";
+ max[ 0 ] = 1;
+ max[ 1 ] = 2;
+ max[ 2 ] = 1;
+ max[ 3 ] = 2;
+ max[ 4 ] = 2;
+ max[ 5 ] = 2;
+ min[ 0 ] = 1;
+ min[ 1 ] = 0;
+ min[ 2 ] = 0;
+ min[ 3 ] = 0;
+ min[ 4 ] = 0;
+ min[ 5 ] = 0;
+ scan = ScanIVOAElement( this, elem, 6, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Create a KeyMap to return holding ancilary info, and put the Name into
+ it. */
+ *anc = astKeyMap( "", status );
+ if( scan->count[0] > 0 ) astMapPut0C( *anc, AST__STCNAME,
+ astXmlGetValue( scan->el[0][0], 0 ), NULL );
+
+/* The values represented by the <Spectral> element may not be in the same
+ system,units, etc as the supplied SpecFrame. We will need to be able to
+ convert from one to the other, so create a SpecFrame describing the
+ system and units used by the <Spectral> element. If the element does not
+ have a unit attribute, assume the values are in the supplied SpecFrame
+ system and units. */
+ unit = astXmlGetAttributeValue( elem, "unit" );
+ if( unit ) {
+ sf1 = astCopy( frm );
+ fsys = SpecSys( this, elem, unit, 1, status );
+ astSetSystem( sf1, fsys );
+ astSetUnit( sf1, 0, unit );
+
+/* If the supplied Frame did not have any set System, use the values from
+ the <Spectral> Frame. */
+ if( !astTestSystem( frm ) ) {
+ astSetSystem( frm, fsys );
+ astSetUnit( frm, 0, unit );
+ } else if( astGetSystem( frm ) == fsys && !astTestUnit( frm, 0 ) ) {
+ astSetUnit( frm, 0, unit );
+ }
+
+ } else {
+ sf1 = astClone( frm );
+ }
+
+/* Find the Mapping from Spectral value to the supplied SpecFrame value */
+ fs = astConvert( sf1, frm, "" );
+ if( fs ) {
+ map = astGetMapping( fs, AST__BASE, AST__CURRENT );
+ fs = astAnnul( fs );
+ } else {
+ map = NULL;
+ Report( this, elem, FAILURE, "connot convert AstroCoords "
+ "spectral values to the required spectral system", status );
+ }
+
+/* If this Spectral contains a frequency Value which can be read, obtain
+ it. We will use the value to calculate the returned rest frequency. */
+ if( scan->count[ 2 ] > 0 ) {
+ name = astXmlGetName( scan->el[ 2 ][ 0 ] );
+ if( name && !strcmp( name, "Value" ) ) {
+ v = ElemValueD( this, scan->el[ 2 ][ 0 ], AST__BAD, status );
+
+/* Convert the value into the supplied SpecFrame system. Create an
+ Interval describing it and store it in the returned ancillary keyMap.
+ Use an Interval rather than a PointList since an Interval can be used
+ within a Prism to extrude another Region, but a PointList cannot. */
+ astTran1( map, 1, &v, 1, &tmp );
+ r = (AstRegion *) astInterval( frm, &tmp, &tmp, NULL, "", status ) ;
+ astMapPut0A( *anc, AST__STCVALUE, r, NULL );
+ r = astAnnul( r );
+
+/* We also want the rest frequency in Hz. Create a SpecFrame describing Hz. */
+ sf2 = astCopy( sf1 );
+ astSet( sf2, "system=freq,unit=Hz", status );
+
+/* Find the Mapping from the supplied value to frequency in Hz. Use it to
+ convert the rf value into Hz. */
+ fs = astConvert( sf1, sf2, "" );
+ if( fs ) {
+ astTran1( fs, 1, &v, 1, rf );
+ fs = astAnnul( fs );
+ } else if( astOK ) {
+ Report( this, elem, FAILURE, "Cannot convert spectral value"
+ "to frequency in Hz.", status );
+ }
+ sf2 = astAnnul( sf2 );
+ }
+ }
+
+/* Check for Error values in the Spectral. */
+ if( scan->count[ 1 ] > 0 ) {
+
+/* Issue a warning if more than 1 Error value was found. */
+ if( scan->count[ 1 ] > 1 ) {
+ Report( this, elem, WARNING, "contains more than one <Error>"
+ " element. AST can only use the first", status );
+ }
+
+/* Get the first Error value. */
+ v = ElemValueD( this, scan->el[1][0], AST__BAD, status );
+ if( v != AST__BAD ) {
+
+/* Create the upper and lower limits of an error bar centred on zero. */
+ ubnd[ 0 ] = 0.5*fabs( v );
+ lbnd[ 0 ] = -ubnd[ 0 ];
+
+/* Create an Interval within the Frame represented by the Spectral element.
+ Map it into the supplied Frame. Simplify it. Store in the returned
+ ancillary KeyMap. */
+ r = (AstRegion *) astInterval( sf1, lbnd, ubnd, NULL, "", status );
+ r2 = astMapRegion( r, map, frm );
+ result = astSimplify( r2 );
+ astMapPut0A( *anc, AST__STCERROR, result, NULL );
+ r2 = astAnnul( r2 );
+ r = astAnnul( r );
+ }
+ }
+
+/* Check for Resolution values in the Spectral. */
+ if( scan->count[ 3 ] > 0 ) {
+
+/* Issue a warning if more than 1 value was found. */
+ if( scan->count[ 3 ] > 1 ) {
+ Report( this, elem, WARNING, "contains more than one <Resolution>"
+ " element. AST can only use the first", status );
+ }
+
+/* Get the first value. */
+ v = ElemValueD( this, scan->el[3][0], AST__BAD, status );
+ if( v != AST__BAD ) {
+
+/* Create the upper and lower limits of an interval centred on zero. */
+ ubnd[ 0 ] = 0.5*fabs( v );
+ lbnd[ 0 ] = -ubnd[ 0 ];
+
+/* Create an Interval within the Frame represented by the Spectral element.
+ Map it into the supplied Frame. Simplify it. Store in the returned
+ ancillary KeyMap. */
+ r = (AstRegion *) astInterval( sf1, lbnd, ubnd, NULL, "", status );
+ r2 = astMapRegion( r, map, frm );
+ r3 = astSimplify( r2 );
+ astMapPut0A( *anc, AST__STCRES, r3, NULL );
+ r3 = astAnnul( r3 );
+ r2 = astAnnul( r2 );
+ r = astAnnul( r );
+ }
+ }
+
+/* Check for Size values in the Spectral. */
+ if( scan->count[ 4 ] > 0 ) {
+
+/* Issue a warning if more than 1 value was found. */
+ if( scan->count[ 4 ] > 1 ) {
+ Report( this, elem, WARNING, "contains more than one <Size>"
+ " element. AST can only use the first", status );
+ }
+
+/* Get the first value. */
+ v = ElemValueD( this, scan->el[4][0], AST__BAD, status );
+ if( v != AST__BAD ) {
+
+/* Create the upper and lower limits of an interval centred on zero. */
+ ubnd[ 0 ] = 0.5*fabs( v );
+ lbnd[ 0 ] = -ubnd[ 0 ];
+
+/* Create an Interval within the Frame represented by the Spectral element.
+ Map it into the supplied Frame. Simplify it. Store in the returned
+ ancillary KeyMap. */
+ r = (AstRegion *) astInterval( sf1, lbnd, ubnd, NULL, "", status );
+ r2 = astMapRegion( r, map, frm );
+ r3 = astSimplify( r2 );
+ astMapPut0A( *anc, AST__STCSIZE, r3, NULL );
+ r3 = astAnnul( r3 );
+ r2 = astAnnul( r2 );
+ r = astAnnul( r );
+ }
+ }
+
+/* Check for PixSize values in the Spectral. */
+ if( scan->count[ 5] > 0 ) {
+
+/* Issue a warning if more than 1 value was found. */
+ if( scan->count[ 5 ] > 1 ) {
+ Report( this, elem, WARNING, "contains more than one <PixSize>"
+ " element. AST can only use the first", status );
+ }
+
+/* Get the first value. */
+ v = ElemValueD( this, scan->el[5][0], AST__BAD, status );
+ if( v != AST__BAD ) {
+
+/* Create the upper and lower limits of an interval centred on zero. */
+ ubnd[ 0 ] = 0.5*fabs( v );
+ lbnd[ 0 ] = -ubnd[ 0 ];
+
+/* Create an Interval within the Frame represented by the Spectral element.
+ Map it into the supplied Frame. Simplify it. Store in the returned
+ ancillary KeyMap. */
+ r = (AstRegion *) astInterval( sf1, lbnd, ubnd, NULL, "", status );
+ r2 = astMapRegion( r, map, frm );
+ r3 = astSimplify( r2 );
+ astMapPut0A( *anc, AST__STCPIXSZ, r3, NULL );
+ r3 = astAnnul( r3 );
+ r2 = astAnnul( r2 );
+ r = astAnnul( r );
+ }
+ }
+
+/* Free resources. */
+ scan = FreeIVOAScan( scan, status );
+ sf1 = astAnnul( sf1 );
+ map = astAnnul( map );
+ }
+
+/* Return NULL if an error occurred. */
+ if( !astOK ) result = astAnnul( result );
+
+/* Return the result */
+ return result;
+
+}
+
+static AstObject *SpectralFrameReader( AstXmlChan *this,
+ AstXmlElement *elem, int *status ) {
+/*
+* Name:
+* SpectralFrameReader
+
+* Purpose:
+* Make an AST Object from an IVOA SpectralFrame element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstObject *SpectralFrameReader( AstXmlChan *this, AstXmlElement *elem, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Object from the supplied IVOA
+* SpectralFrame element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA SpectralFrame element.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Object.
+
+*/
+
+/* Local Variables: */
+ AstSpecFrame *new; /* Pointer to returned Object */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *names[2]; /* Names of the subelements to be searched for */
+ const char *sor; /* StdOfRest for returned Frame */
+ int max[2]; /* Max allowed occurrences of each name */
+ int min[2]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return (AstObject *) new;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "TOPOCENTER|BARYCENTER|HELIOCENTER|GEOCENTER|LSR|"
+ "LSRK|GALACTIC_CENTER|LOCAL_GROUP|LSRD";
+ names[ 1 ] = "Name";
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ min[ 0 ] = 1;
+ min[ 1 ] = 0;
+ scan = ScanIVOAElement( this, elem, 2, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the name of the Element specifying the reference position and find
+ the corresponding AST name.*/
+ sor = astXmlGetName( scan->el[0][0] );
+ if( !strcmp( sor, "TOPOCENTER" ) ) {
+ sor = "Topo";
+ } else if( !strcmp( sor, "BARYCENTER" ) ){
+ sor = "Bary";
+ } else if( !strcmp( sor, "GEOCENTER" ) ){
+ sor = "Geo";
+ } else if( !strcmp( sor, "LSR" ) || !strcmp( sor, "LSRK" ) ) {
+ sor = "LSRK";
+ } else if( !strcmp( sor, "LSRD" ) ) {
+ sor = "LSRD";
+ } else if( !strcmp( sor, "GALACTIC_CENTER" ) ) {
+ sor = "Galactic";
+ } else if( !strcmp( sor, "LOCAL_GROUP" ) ) {
+ sor = "Local_group";
+ } else if( !strcmp( sor, "HELIOCENTER" ) ) {
+ sor = "Helio";
+ } else if( astOK ){
+ astError( AST__INTER, "SpectralFrameReader(XmlChan): Unknown "
+ "standard of rest %s (internal AST programming error).", status,
+ sor );
+ }
+
+/* Issue a warning if the reference position includes an ephemeris. */
+ if( FindElement( this, scan->el[0][0], "PlanetaryEphem", status ) ) {
+ Report( this, scan->el[0][0], WARNING, "contains a <PlanetaryEphem> "
+ "element which will be ignored", status );
+ }
+
+/* Create a suitable SpecFrame. */
+ new = astSpecFrame( "StdOfRest=%s", status, sor);
+
+/* If the SpectralFrame has a <Name> element use it as the SpecFrame title. */
+ if( scan->count[1] ) astSetTitle( new, astXmlGetValue( scan->el[1][0], 0 ) );
+
+/* Free resources */
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Object. */
+ return (AstObject *) new;
+}
+
+static AstRegion *SpectralIntervalReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, int *status ){
+/*
+* Name:
+* SpectralIntervalReader
+
+* Purpose:
+* Make an AST Region from an IVOA SpectralInterval element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *SpectralIntervalReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* SpectralInterval element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA SpectralInterval element.
+* frm
+* Pointer to the Frame in which the returned Region should be
+* defined. If the Unit or System attribute is not set, this
+* function will decide on the values to be used, and set these
+* values in the supplied Frame before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstFrame *cfrm; /* Frame used to define returned Region */
+ AstRegion *new; /* Pointer to returned Region */
+ AstSystemType fsys; /* System value from supplied Frame */
+ AstSystemType sys; /* System value corresponding to "unit" */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *funit; /* Unit string from supplied Frame */
+ const char *names[2]; /* Names of the subelements to be searched for */
+ char *title; /* Title string */
+ const char *unit; /* Unit string from supplied element */
+ double hilimit; /* Upper spectral limit */
+ double lolimit; /* Lower spectral limit */
+ int max[2]; /* Max allowed occurrences of each name */
+ int min[2]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "LoLimit";
+ names[ 1 ] = "HiLimit";
+ min[ 0 ] = 0;
+ min[ 1 ] = 0;
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ scan = ScanIVOAElement( this, elem, 2, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the limits. */
+ lolimit = scan->count[0] ? ElemValueD( this, scan->el[0][0], 0.0, status ) : AST__BAD;
+ hilimit = scan->count[1] ? ElemValueD( this, scan->el[1][0], 0.0, status ) : AST__BAD;
+
+/* Get the Unit attribute from the element. */
+ unit = astXmlGetAttributeValue( elem, "unit" );
+ if( !unit ) {
+ Report( this, elem, FAILURE, "contains no unit attribute", status );
+ unit = "";
+
+/* Find the spectral system corresponding to these units. */
+ } else {
+ sys = SpecSys( this, elem, unit, 1, status );
+
+/* Take a copy of the supplied Frame and set its System and Units to
+ these values. Ensure the title is preserved. */
+ cfrm = astCopy( frm );
+ if( astTestTitle( frm ) ) {
+ title = (char *) astGetTitle( frm );
+ if( title ) title = astStore( NULL, title, strlen( title ) + 1 );
+ } else {
+ title = NULL;
+ }
+ astSetSystem( cfrm, sys );
+ astSetUnit( cfrm, 0, unit );
+ if( title ) astSetTitle( cfrm, title );
+
+/* If at least one limit was found, create an Interval within this
+ modified Frame. Otherwise create a negated NullRegion. */
+ if( lolimit != AST__BAD || hilimit != AST__BAD ) {
+ new = (AstRegion *) astInterval( cfrm, &lolimit, &hilimit, NULL, "", status );
+ } else {
+ new = (AstRegion *) astNullRegion( cfrm, NULL, "negated=1", status );
+ }
+
+/* If the System of this Region differs from that of the supplied Frame,
+ set it to the System of the supplied Frame. This will cause the
+ encapsulated limits to be mapped into the new System. If the supplied
+ Frame had no set system, set it to the system implied by th eunits in the
+ supplied XML element. */
+ if( astTestSystem( frm ) ) {
+ fsys = astGetSystem( frm );
+ if( fsys != sys ) astSetSystem( new, fsys );
+ } else {
+ astSetSystem( frm, sys );
+ }
+
+/* Do the same with the Units. */
+ if( astTestUnit( frm, 0 ) ) {
+ funit = astGetUnit( frm, 0 );
+ if( strcmp( funit, unit ) ) astSetUnit( new, 0, funit );
+ } else {
+ astSetUnit( frm, 0, unit );
+ }
+
+/* Ensure the original titleis preserved. */
+ if( title ) {
+ astSetTitle( new, title );
+ astSetTitle( frm, title );
+ }
+
+/* Get any fill factor and lo/hi_include attributes from the element and
+ assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Free resources */
+ cfrm = astAnnul( cfrm );
+ title = astFree( title );
+ }
+
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static AstRegion *SphereReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, int *status ){
+/*
+* Name:
+* SphereReader
+
+* Purpose:
+* Make an AST Region from an IVOA Sphere element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *SphereReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* Sphere element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Sphere element.
+* frm
+* Pointer to the Frame in which the returned Region should be
+* defined. If the Unit or System attribute is not set, this
+* function will decide on the values to be used, and set these
+* values in the supplied Frame before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstFrame *cfrm; /* Frame used to define returned Region */
+ AstMapping *map; /* Mapping between units */
+ AstRegion *new; /* Pointer to returned Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ char buff[200]; /* Message buffer */
+ const char *funit; /* Unit string from supplied Frame */
+ const char *names[2]; /* Names of the subelements to be searched for */
+ const char *runit; /* Radius unit string from supplied element */
+ const char *unit; /* Centre unit string from supplied element */
+ double cen[3]; /* Centre */
+ double rad; /* Radius */
+ double tmp; /* New radius value */
+ int i; /* Axis count */
+ int max[2]; /* Max allowed occurrences of each name */
+ int min[2]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Check the supplied Frame has the correct number of axes. */
+ if( astGetNaxes( frm ) != 3 && astOK ) {
+ astError( AST__INTER, "SphereReader(XmlChan): Supplied "
+ "Frame does not have 3 axes (internal AST programming error )." , status);
+ }
+
+/* Scan the supplied element for the required sub-elements */
+ names[ 0 ] = "Radius";
+ names[ 1 ] = "Center";
+ min[ 0 ] = 1;
+ min[ 1 ] = 1;
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ scan = ScanIVOAElement( this, elem, 2, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the radius. */
+ rad = ElemValueD( this, scan->el[0][0], 0.0, status );
+
+/* Get the centre. */
+ cen[0] = 0.0;
+ cen[1] = 0.0;
+ cen[2] = 0.0;
+ ElemListD( this, scan->el[1][0], 3, cen, status );
+
+/* Get the units attribute from the supplied element. This applies to the
+ values describing the centre position. */
+ unit = astXmlGetAttributeValue( elem, "unit" );
+ if( !unit ) {
+ Report( this, elem, FAILURE, "contains no unit attribute", status );
+ unit = "";
+ }
+
+/* Get the radius units attribute from the supplied element. */
+ runit = astXmlGetAttributeValue( elem, "radius_unit" );
+
+/* If necessary, convert the radius to the same units as the centre. */
+ if( runit && strcmp( unit, runit ) ) {
+ map = astUnitMapper( runit, unit, NULL, NULL );
+ if( map ) {
+ astTran1( map, 1, &rad, 1, &tmp );
+ rad = tmp;
+ map = astAnnul( map );
+
+ } else if( astOK ) {
+ sprintf( buff, "has inconsistent units attributes \"%s\" and "
+ "\"%s\"", unit, runit );
+ Report( this, elem, FAILURE, buff, status );
+ }
+ }
+
+/* Take a copy of the supplied Frame and set its Units to the value
+ obtained from the supplied element. */
+ cfrm = astCopy( frm );
+ astSetUnit( cfrm, 0, unit );
+ astSetUnit( cfrm, 1, unit );
+ astSetUnit( cfrm, 2, unit );
+
+/* Create a Circle within this modified Frame. */
+ new = (AstRegion *) astCircle( cfrm, 1, cen, &rad, NULL, "", status );
+
+/* If the Unit of this Region differs from that of the supplied Frame,
+ set it to the Unit of the supplied Frame. This will cause the
+ encapsulated limits to be mapped into the new Unit. If the supplied
+ Frame had no set Unit, set it to the units obtained from the supplied
+ element. */
+ for( i = 0; i < 3; i++ ) {
+ if( astTestUnit( frm, i ) ) {
+ funit = astGetUnit( frm, i );
+ if( strcmp( funit, unit ) ) astSetUnit( new, i, funit );
+ } else {
+ astSetUnit( frm, i, unit );
+ }
+ }
+
+/* Get any fill factor and lo/hi_include attributes from the element and
+ assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Free resources */
+ cfrm = astAnnul( cfrm );
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static AstObject *StcMetadataReader( AstXmlChan *this,
+ AstXmlElement *elem, int *status ) {
+/*
+* Name:
+* StcMetadataReader
+
+* Purpose:
+* Make an AST Object from an IVOA STCMetadata element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstObject *StcMetadataReader( AstXmlChan *this,
+* AstXmlElement *elem, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Object from the supplied IVOA
+* STCMetadata element. The STCMetadata object can be of any subclass
+* (e.g. STCResourceProfile, SearchLocation, CatalogEntryLocation,
+* ObservationLocation, ObservatoryLocation).
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA STCMetadata element.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Object.
+*/
+
+/* Local Variables: */
+ AstFrame *frm; /* Frame representing the STC object */
+ AstKeyMap *anc; /* Map holding AstroCoords ancillary data */
+ AstKeyMap **ancs; /* List of KeyMaps holding ancillary data */
+ AstKeyMap *map1; /* Map holding AstroCoordSystem elements */
+ AstKeyMap *map2; /* Map holding AstroCoordArea elements */
+ AstKeyMap *map3; /* Map holding CoordSpec elements */
+ AstKeyMap *map; /* Map to use */
+ AstRegion *region; /* Region representing the STC object */
+ AstRegion *tuncs[ 4 ]; /* Temporary uncertainty Regions */
+ AstRegion *uncs[ 4 ]; /* Uncertainty Regions for returned STC */
+ AstStc *stc; /* Pointer to returned Object (an Stc) */
+ AstXmlContentItem *item; /* Pointer to content item */
+ AstXmlElement *aca; /* Pointer to AstroCoordArea element to use */
+ AstXmlElement *aco; /* Pointer to AstroCoords element to use */
+ AstXmlElement *acs; /* Pointer to AstroCoordSystem element to use */
+ char *text; /* Formatted item text */
+ char buff[ 200 ]; /* Message buffer */
+ const char *id; /* Value of ID attribute */
+ const char *ido; /* Value of ID attribute */
+ const char *name; /* Element name */
+ const char *stc_class; /* STC subclass name */
+ int gotunc; /* Have any uncertainty Regions been obtained? */
+ int i; /* Index of content item within element */
+ int j; /* Index into list of map keys */
+ int narea; /* Number of AstroCoordArea elements found */
+ int ncoord; /* Number of CoordSpec elements found */
+ int nanc; /* No.of KeyMaps in "ancs" array */
+ int nitem; /* No. of items of content in element */
+ int nsys; /* Number of AstroCoordSystem elements found */
+ int reported; /* Have multiple uncertainies been reported? */
+ int used; /* Was the content item used? */
+
+/* Initialise. */
+ stc = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return (AstObject *) stc;
+
+/* Avoid compiler warnings. */
+ id = "";
+
+/* Get name of the the STCMetadata subclass represented by the supplied
+ element. */
+ stc_class = astXmlGetName( elem );
+
+/* Create KeyMaps to hold the required sub-elements. We will store the
+ integer indices of the requried elements in these keymaps, using the
+ associated Xml ID attribute values as the keys. */
+ map1 = astKeyMap( "", status );
+ map2 = astKeyMap( "", status );
+ map3 = astKeyMap( "", status );
+
+/* Loop round all items in the elements contents. */
+ nitem = astXmlGetNitem( elem );
+ for( i = 0; i < nitem; i++ ) {
+ item = astXmlGetItem( elem, i );
+ used = 1;
+
+/* Ignore this item if it is not an element. */
+ if( astXmlCheckType( item, AST__XMLELEM ) ) {
+
+/* Choose the KeyMap in which to save this item. */
+ name = astXmlGetName( item );
+ if( !strcmp( name, ASTRO_COORD_SYSTEM ) ){
+ map = map1;
+
+ } else if( !strcmp( name, ASTRO_COORD_AREA ) ){
+ map = map2;
+
+ } else if( !strcmp( name, ASTRO_COORDS ) ){
+ map = map3;
+
+ } else {
+ map = NULL;
+ used = 0;
+ }
+
+/* If we are going to save the item, get the value of the ID attribute
+ and check it. */
+ if( map ) {
+ id = astXmlGetAttributeValue( (AstXmlElement *) item, "ID" );
+ if( !id ) {
+ id = "";
+ if( map != map3 ) {
+ Report( this, elem, WARNING, "has no ID attribute. Assuming"
+ "a null ID value", status );
+ }
+ }
+
+/* If the KeyMap already contains an object with this ID, issue a
+ warning and skip the element. */
+ if( astMapHasKey( map, id ) ) {
+ if( map != map3 ) {
+ sprintf( buff, "contains two or more %s elements with the "
+ "same ID (\"%s\"). Only the first one will be used",
+ name, id );
+ Report( this, elem, WARNING, buff, status );
+ } else {
+ Report( this, elem, WARNING, "contains two or more AstroCoords "
+ "elements. Only the first one will be used", status );
+ }
+
+/* Otherwise, save the index of the item in the KeyMap, using the ID as the
+ key. */
+ } else {
+ astMapPut0I( map, id, i, "" );
+ }
+ }
+
+ } else {
+ used = 0;
+ }
+
+/* If this content item was not used, issue a warning unless it is a comment
+ or white space. */
+ if( !used && !astXmlCheckType( item, AST__XMLCOM ) &&
+ !astXmlCheckType( item, AST__XMLWHITE ) ) {
+ text = (char *) astXmlFormat( item );
+ if( strlen( text ) > 30 ) text[ 30 ] = 0;
+ sprintf( buff, "contains the following which is being ignored: \"%s\"",
+ text );
+ text = astFree( text );
+ Report( this, elem, WARNING, buff, status );
+ }
+
+ }
+
+/* Note the number of each type of element found with unique ID values. */
+ nsys = astMapSize( map1 );
+ narea = astMapSize( map2 );
+ ncoord = astMapSize( map3 );
+
+/* If any CoordArea elements were found, find the first one for which the
+ coordesponding AstroCoordSystem is available. */
+ acs = NULL;
+ aca = NULL;
+ for( j = 0; j < narea; j++ ) {
+
+/* Get the j'th key from "map2" (the ID associated with the j'th
+ AstroCoordArea found in the supplied element) and retrieve the value
+ associated with this key (the index "i" into the content of the
+ supplied element at which the j'th AstroCoordArea is stored). */
+ astMapGet0I( map2, astMapKey( map2, j ), &i );
+
+/* Get the i'th element in the supplied element. This will be the j'th
+ AstroCoordArea. */
+ aca = (AstXmlElement *) astXmlGetItem( elem, i );
+
+/* Get the "coord_system_id" attribute from this AstroCoordArea. Use null
+ if not available. */
+ id = astXmlGetAttributeValue( aca, "coord_system_id" );
+ if( !id ) {
+ id = "";
+ Report( this, aca, WARNING, "has no coord_system_id attribute. "
+ "Assuming a null coord_system_id value", status );
+ }
+
+/* Get the index within the supplied element of the AstroCoordSystem with this
+ ID. Jump forward if no AstroCoordSystem with this id is available. */
+ if( astMapGet0I( map1, id, &i ) ) {
+
+/* Get a pointer to the AstroCoordSystem element with the required ID. */
+ acs = (AstXmlElement *) astXmlGetItem( elem, i );
+
+/* Leave the AstroCoordArea loop. */
+ break;
+
+/* Report a warning if no AstroCoordSystem with this id is available. */
+ } else {
+ sprintf( buff, "refers to an AstroCoordSystem with "
+ "ID \"%s\", but no such AstroCoordSystem is available "
+ "within the parent %s", id, stc_class );
+ Report( this, aca, WARNING, buff, status );
+ }
+ }
+
+/* If we did not find a corresponding pair of AstroCoordSystem and
+ AstroCoordArea, we either report a failure (if there were any
+ AstroCoordAreas), or get a pointer the AstroCoordSystem referred to by
+ the first AstroCoords element (we will create a Frame from this later). */
+ if( !acs ) {
+ aca = NULL;
+
+/* Report a warning if there were some AstroCoordArea tags but no matching
+ AstroCoordSystem was found. */
+ if( narea > 0 ) {
+ Report( this, elem, WARNING, "does not contain a pair of "
+ "matching AstroCoordArea and AstroCoordSystem tags", status );
+
+/* If there are no AstroCoordAreas in the supplied element, look for a
+ pair of matching AstroCoords and AstroCoordSystem. The returned Region
+ will represent a NullRegion within this system. */
+ } else if( ncoord > 0 ) {
+
+/* Get the 1st key from "map3" (the ID associated with the 1st
+ AstroCoords found in the supplied element) and retrieve the value
+ associated with this key (the index "i" into the content of the
+ supplied element at which the 1st AstroCoords is stored). */
+ astMapGet0I( map3, astMapKey( map3, 0 ), &i );
+
+/* Get the i'th element in the supplied element. This will be the 1st
+ AstroCoord. */
+ aco = (AstXmlElement *) astXmlGetItem( elem, i );
+
+/* Get the "coord_system_id" attribute from this AstroCoords. Use null
+ if not available. */
+ id = astXmlGetAttributeValue( aco, "coord_system_id" );
+ if( !id ) {
+ id = "";
+ Report( this, aco, WARNING, "has no coord_system_id attribute. "
+ "Assuming a null coord_system_id value", status );
+ }
+
+/* Get the index within the supplied element of the AstroCoordSystem with this
+ ID. Jump forward if no AstroCoordSystem with this id is available. */
+ if( astMapGet0I( map1, id, &i ) ) {
+
+/* Get a pointer to the AstroCoordSystem element with the required ID. */
+ acs = (AstXmlElement *) astXmlGetItem( elem, i );
+
+ } else {
+ Report( this, aco, FAILURE, "no corresponding AstroCoordSystem found", status );
+ }
+
+/* If there are no AstroCoords in the supplied element we create a
+ NullRegion within the first supplied AstroCoordSystem. */
+ } else if( nsys > 0 ) {
+ if( astMapGet0I( map1, astMapKey( map1, 0 ), &i ) ) {
+ acs = (AstXmlElement *) astXmlGetItem( elem, i );
+ }
+
+ } else {
+ Report( this, elem, FAILURE, "no usable content found", status );
+ }
+ }
+
+/* Report failure if we still have no AstroCoordSystem. */
+ if( !acs ) {
+ Report( this, elem, FAILURE, "does not contain a usable AstroCoordSystem", status );
+
+/* Issue a warning if more than one AstroCoordArea was found. */
+ } else {
+ if( narea > 1 ) Report( this, elem, WARNING, "contains more than one "
+ "AstroCoordArea. Only one will be used", status );
+
+/* Create a Frame from the ASTRO_COORD_SYSTEM. */
+ frm = (AstFrame *) AstroCoordSystemReader( this, acs, status );
+
+/* Loop round all AstroCoords elements in the supplied element. */
+ gotunc = 0;
+ reported = 0;
+ uncs[ 0 ] = NULL;
+ uncs[ 1 ] = NULL;
+ uncs[ 2 ] = NULL;
+ uncs[ 3 ] = NULL;
+ nanc = 0;
+ ancs = NULL;
+ for( j = 0; j < ncoord; j++ ) {
+
+/* Get the j'th key from "map3" (the ID associated with the j'th
+ AstroCoords found in the supplied element) and retrieve the value
+ associated with this key (the index "i" into the content of the
+ supplied element at which the j'th AstroCoords is stored). */
+ astMapGet0I( map3, astMapKey( map3, j ), &i );
+
+/* Get the i'th element in the supplied element. This will be the j'th
+ AstroCoords. */
+ aco = (AstXmlElement *) astXmlGetItem( elem, i );
+
+/* Get the "coord_system_id" attribute from this AstroCoords and compare it
+ with the ID of the AstrocCoordSys being used. If they match, incorporate
+ the effects of the AstroCoords into the "frm" Frame and get a set of 4
+ Regions representing the uncertainty within each of the 4 STC domains
+ (space, time, spectral, redshift). */
+ ido = astXmlGetAttributeValue( aco, "coord_system_id" );
+ if( ido && !strcmp( id, ido ) ) {
+ if( AstroCoordsReader( this, aco, frm, tuncs, &anc, status ) ) {
+ if( !gotunc ) {
+ uncs[ 0 ] = tuncs[ 0 ];
+ uncs[ 1 ] = tuncs[ 1 ];
+ uncs[ 2 ] = tuncs[ 2 ];
+ uncs[ 3 ] = tuncs[ 3 ];
+ gotunc = 1;
+ } else {
+ if( tuncs[ 0 ] ) tuncs[ 0 ] = astAnnul( tuncs[ 0 ] );
+ if( tuncs[ 1 ] ) tuncs[ 1 ] = astAnnul( tuncs[ 1 ] );
+ if( tuncs[ 2 ] ) tuncs[ 2 ] = astAnnul( tuncs[ 2 ] );
+ if( tuncs[ 3 ] ) tuncs[ 3 ] = astAnnul( tuncs[ 3 ] );
+ if( !reported ) {
+ Report( this, elem, WARNING, "contains more than one "
+ "specification of the coordinate uncertainties. "
+ "Only the first will be used", status );
+ reported= 1;
+ }
+ }
+ }
+
+/* If any ancillary information was read from the AstroCoords, add it to
+ the list of ancillary information to be stored in the Stc structure. */
+ if( anc ) {
+ ancs = astGrow( ancs, nanc + 1, sizeof( AstKeyMap * ) );
+ if( ancs ) ancs[ nanc++ ] = anc;
+ }
+ }
+ }
+
+/* Now create a Region from this Frame and the ASTRO_COORD_AREA. Note,
+ "aca" may be NULL in which case the returned Region will be NullRegion. */
+ region = AstroCoordAreaReader( this, aca, frm, uncs, nanc, ancs, status );
+
+/* Re-centre the Regions describing ancillary information extracted from
+ the AstroCoords elements. */
+ ReCentreAnc( region, nanc, ancs, status );
+
+/* Now create a Stc object of the appropriate sub-class. */
+ if( !strcmp( stc_class, STC_RESOURCE_PROFILE ) ) {
+ stc = (AstStc *) astStcResourceProfile( region, nanc, ancs, "", status );
+
+ } else if( !strcmp( stc_class, SEARCH_LOCATION ) ) {
+ stc = (AstStc *) astStcSearchLocation( region, nanc, ancs, "", status );
+
+ } else if( !strcmp( stc_class, CATALOG_ENTRY_LOCATION ) ) {
+ stc = (AstStc *) astStcCatalogEntryLocation( region, nanc, ancs, "", status );
+
+ } else if( !strcmp( stc_class, OBSERVATION_LOCATION ) ||
+ !strcmp( stc_class, OBSERVATORY_LOCATION ) ) {
+ stc = (AstStc *) astStcObsDataLocation( region, nanc, ancs, "", status );
+
+ } else if( astOK ){
+ astError( AST__INTER, "astRead(XmlChan): StcMetadataReader knows "
+ "nothing about the %s class (internal AST programming "
+ "error).", status, stc_class );
+ }
+
+/* Get the ID attribute from the supplied element and store in the
+ returned Object. */
+ id = astXmlGetAttributeValue( elem, "ID" );
+ if( id ) astSetIdent( stc, id );
+
+/* Free resources. */
+ if( uncs[ 0 ] ) uncs[ 0 ] = astAnnul( uncs[ 0 ] );
+ if( uncs[ 1 ] ) uncs[ 1 ] = astAnnul( uncs[ 1 ] );
+ if( uncs[ 2 ] ) uncs[ 2 ] = astAnnul( uncs[ 2 ] );
+ if( uncs[ 3 ] ) uncs[ 3 ] = astAnnul( uncs[ 3 ] );
+ frm = astAnnul( frm );
+ region = astAnnul( region );
+ if( ancs ) {
+ for( i = 0; i < nanc; i++ ) ancs[ i ] = astAnnul( ancs[ i ] );
+ ancs = astFree( ancs );
+ }
+ }
+
+ map1 = astAnnul( map1 );
+ map2 = astAnnul( map2 );
+ map3 = astAnnul( map3 );
+
+/* Return the pointer to the new Object. */
+ return (AstObject *) stc;
+}
+
+static AstRegion *StcRegionReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, int *status ){
+/*
+* Name:
+* StcRegionReader
+
+* Purpose:
+* Make an AST Region from an IVOA Region element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *StcRegionReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* Region element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Region element.
+* frm
+* Pointer to the 2D Frame in which the returned Region should be
+* defined. If the Unit attribute is not set, this function will
+* set it to the value supplied in "unit" before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstRegion *new; /* Pointer to returned Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *names[1]; /* Names of the subelements to be searched for */
+ int max[1]; /* Max allowed occurrences of each name */
+ int min[1]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Search the supplied element for a Region sub-element. */
+ names[ 0 ] = "Intersection|Union|Negation|AllSky|Circle|Ellipse|Polygon|"
+ "Convex|Box";
+ min[ 0 ] = 1;
+ max[ 0 ] = 1;
+ scan = ScanIVOAElement( this, elem, 1, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Create the Region */
+ new = RegionReader( this, scan->el[0][0], frm, status );
+
+/* Free resources */
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* TestAttrib
+
+* Purpose:
+* Test if a specified attribute value is set for a XmlChan.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* int TestAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* XmlChan member function (over-rides the astTestAttrib protected
+* method inherited from the Channel class).
+
+* Description:
+* This function returns a boolean result (0 or 1) to indicate whether
+* a value has been set for one of a XmlChan's attributes.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* 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: */
+ AstXmlChan *this; /* Pointer to the XmlChan structure */
+ int result; /* Result value to return */
+
+/* Initialise. */
+ result = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) this_object;
+
+/* Check the attribute name and test the appropriate attribute. */
+
+/* XmlLength */
+/* --------- */
+ if ( !strcmp( attrib, "xmllength" ) ) {
+ result = astTestXmlLength( this );
+
+/* XmlFormat */
+/* --------- */
+ } else if ( !strcmp( attrib, "xmlformat" ) ) {
+ result = astTestXmlFormat( this );
+
+/* XmlPrefix */
+/* --------- */
+ } else if ( !strcmp( attrib, "xmlprefix" ) ) {
+ result = astTestXmlPrefix( 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 AstObject *TimeFrameReader( AstXmlChan *this,
+ AstXmlElement *elem, int *status ) {
+/*
+* Name:
+* TimeFrameReader
+
+* Purpose:
+* Make an AST Object from an IVOA TimeFrame element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstObject *TimeFrameReader( AstXmlChan *this, AstXmlElement *elem, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Object from the supplied IVOA
+* TimeFrame element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA TimeFrame element.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Object.
+
+*/
+
+/* Local Variables: */
+ AstTimeFrame *new; /* Pointer to returned Frame */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *names[3]; /* Names of the subelements to be searched for */
+ const char *text; /* Pointer to Name value */
+ int max[3]; /* Max allowed occurrences of each name */
+ int min[3]; /* Min allowed occurrences of each name */
+
+/* Initialise */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return (AstObject *) new;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "Name";
+ names[ 1 ] = "TOPOCENTER";
+ names[ 2 ] = "TimeScale|Timescale";
+ min[ 0 ] = 0;
+ max[ 0 ] = 1;
+ min[ 1 ] = 0;
+ max[ 1 ] = 1;
+ min[ 2 ] = 1;
+ max[ 2 ] = 1;
+ scan = ScanIVOAElement( this, elem, 3, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Create a suitable TimeFrame. Set the timescale, but leave the other
+ attributes unset since they will not be known until an AstronTimeType
+ is read. Except for unit. We set unit to "d" (day) because all the
+ time form,ats supported by STC have "d" as the default unit. This
+ avoids bad publicity which arises from presentin (say) MJD values in
+ units of "s" - which people will think is wrong until they have it
+ explained. */
+ new = astTimeFrame( "unit=d", status );
+ astSetTimeScale( new, TimeScaleReader( this, scan->el[ 2 ][ 0 ], status ) );
+
+/* If the STC TimeFrame has a <Name> element use it as the AST TimeFrame title. */
+ if( scan->count[ 0 ] > 0 ) {
+ text = astXmlGetValue( scan->el[ 0 ][ 0 ], 0 );
+ if( text ) astSetTitle( new, text );
+ }
+
+/* Free resources. */
+ scan = FreeIVOAScan( scan, status );
+
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new= astAnnul( new );
+
+/* Return the pointer to the new Object. */
+ return (AstObject *) new;
+}
+
+static AstRegion *TimeIntervalReader( AstXmlChan *this, AstXmlElement *elem,
+ AstTimeFrame *frm, int *status ){
+/*
+* Name:
+* TimeIntervalReader
+
+* Purpose:
+* Make an AST Region from an IVOA TimeInterval element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *TimeIntervalReader( AstXmlChan *this, AstXmlElement *elem,
+* AstTimeFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* TimeInterval element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA TimeInterval element.
+* frm
+* Pointer to the TimeFrame in which the returned Region should be
+* defined.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstRegion *new; /* Pointer to returned Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *names[2]; /* Names of the subelements to be searched for */
+ double start; /* Start time */
+ double stop; /* Stop time */
+ int max[2]; /* Max allowed occurrences of each name */
+ int min[2]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "StartTime";
+ names[ 1 ] = "StopTime";
+ min[ 0 ] = 0;
+ min[ 1 ] = 0;
+ max[ 0 ] = 1;
+ max[ 1 ] = 1;
+ scan = ScanIVOAElement( this, elem, 2, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the limits. */
+ start = scan->count[0] ? AstronTimeReader( this, scan->el[0][0], frm, status ) : AST__BAD;
+ stop = scan->count[1] ? AstronTimeReader( this, scan->el[1][0], frm, status ) : AST__BAD;
+
+/* If at least one limit was found, create an Interval. Otherwise create
+ a negated NullRegion. */
+ if( start != AST__BAD || stop != AST__BAD ) {
+
+/* Use the stop or start time (converted to an MJD) as the Epoch within the
+ Frame. */
+ if( start != AST__BAD ) {
+ astSetEpoch( frm, MakeMJD( frm, start, status ) );
+ } else if( stop != AST__BAD ) {
+ astSetEpoch( frm, MakeMJD( frm, stop, status ) );
+ }
+ new = (AstRegion *) astInterval( frm, &start, &stop, NULL, "", status );
+ } else {
+ new = (AstRegion *) astNullRegion( frm, NULL, "negated=1", status );
+ }
+
+/* Get any fill factor and lo/hi_include attributes from the element and
+ assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Free resources. */
+ scan = FreeIVOAScan( scan, status );
+
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static AstRegion *TimeReader( AstXmlChan *this, AstXmlElement *elem,
+ AstTimeFrame *frm, double *epoch,
+ AstKeyMap **anc, int *status ){
+/*
+* Name:
+* TimeReader
+
+* Purpose:
+* Modify a Frame to take account of an STC <Time> element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *TimeReader( AstXmlChan *this, AstXmlElement *elem,
+* AstTimeFrame *frm, double *epoch,
+* AstKeyMap **anc, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function reads the supplied STC <Time> element, and uses it,
+* if possible, to create the uncertainty associated with the time
+* axis in the supplied Frame.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Time element.
+* frm
+* Pointer to the TimeFrame.
+* epoch
+* Pointer to double in which to return the epoch to be used
+* with other axes. Value is returned as an Modified Julian Date
+* in the barycentric dynamical timescale (TDB). AST__BAD will
+* be returned if the supplied Time element has no value.
+* anc
+* Address of a location at which to put a pointer to a newly
+* created KeyMap. This KeyMap will contain ancillary information
+* from the Time. The keys identify the item of ancillary
+* information (Name, Value, Error, Resolution, Size, Pixel Size).
+* The value associated with the Name key is string containing
+* the Name item from the Time. The value
+* associated with each of the other keys is a pointer to a 1D Region
+* within the supplied Frame, corresponding to the value, error,
+* resolution, etc. Keys will not be present in the returned KeyMap
+* if the corresponding item of ancillary information is not present
+* in the Time. A NULL pointer is returned if there is no
+* ancillary information at all.
+* status
+* Pointer to the inherited status variable.
+
+* Returned:
+* The uncertainty Region, or NULL if the supplied Time element
+* does not specify an uncertainty.
+
+*/
+
+/* Local Variables: */
+ AstTimeFrame *cfrm; /* Pointer to copy of time axis */
+ AstRegion *result; /* Returned uncertainty Region */
+ AstRegion *r; /* Ancillary Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *funit; /* Pointer to Frame's unit attribute string */
+ const char *names[6]; /* Names of the subelements to be searched for */
+ const char *title; /* Pointer to Frame title string */
+ const char *unit; /* Pointer to Time's unit attribute string */
+ double lbnd[ 1 ] ; /* Lower interval bounds */
+ double ubnd[ 1 ] ; /* Upper interval bounds */
+ double value; /* Time value */
+ double v; /* Ancillary value */
+ int max[6]; /* Max allowed occurrences of each name */
+ int min[6]; /* Min allowed occurrences of each name */
+
+/* Initialise */
+ result = NULL;
+ *epoch = AST__BAD;
+ *anc = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "Name";
+ names[ 1 ] = "Error";
+ names[ 2 ] = "TimeInstant";
+ names[ 3 ] = "Resolution";
+ names[ 4 ] = "Size";
+ names[ 5 ] = "PixSize";
+ max[ 0 ] = 1;
+ max[ 1 ] = 2;
+ max[ 2 ] = 1;
+ max[ 3 ] = 2;
+ max[ 4 ] = 2;
+ max[ 5 ] = 2;
+ min[ 0 ] = 1;
+ min[ 1 ] = 0;
+ min[ 2 ] = 0;
+ min[ 3 ] = 0;
+ min[ 4 ] = 0;
+ min[ 5 ] = 0;
+ scan = ScanIVOAElement( this, elem, 6, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Create a KeyMap to return holding ancilary info, and put the Name into
+ it. */
+ *anc = astKeyMap( "", status );
+ if( scan->count[0] > 0 ) astMapPut0C( *anc, AST__STCNAME,
+ astXmlGetValue( scan->el[0][0], 0 ), NULL );
+
+/* Get any Unit attribute from the Time element. */
+ unit = astXmlGetAttributeValue( elem, "unit" );
+
+/* We need to ensure the returned regions are mapped into units of "funit".
+ If this is NULL it means that the returned regions are already in the
+ required units. */
+ funit = NULL;
+
+/* If the Time element has a unit attribute, we use it in preference to any
+ units values in the supplied Frame. Take a copy of the time Frame and set
+ its Units to this values. Ensure the title is preserved. */
+ if( unit && astChrLen( unit ) ) {
+ cfrm = astCopy( frm );
+ if( astTestTitle( frm ) ) {
+ title = (char *) astGetTitle( frm );
+ if( title ) title = astStore( NULL, title, strlen( title ) + 1 );
+ } else {
+ title = NULL;
+ }
+ astSetUnit( cfrm, 0, unit );
+ if( title ) astSetTitle( cfrm, title );
+
+ if( astTestUnit( frm, 0 ) ) {
+ funit = astGetUnit( frm, 0 );
+ if( !strcmp( funit, unit ) ) {
+ funit = NULL;
+ } else {
+ funit = astStore( NULL, funit, strlen( funit ) + 1 );
+ }
+ } else {
+ astSetUnit( frm, 0, unit );
+ }
+
+ } else {
+ cfrm = astClone( frm );
+ title = NULL;
+ }
+
+/* If this Time contains a Value which can be read, obtain it. Otherwise,
+ issue a warning. We will use the value to calculate the returned epoch. */
+ if( scan->count[ 2 ] > 0 ) {
+ value = AstronTimeReader( this, scan->el[ 2 ][ 0 ], cfrm, status );
+ *epoch = MakeMJD( cfrm, value, status );
+
+/* Ensure any relevant attribute values which were set by AstronTimeReader
+ within "cfrm" are transferred to "frm". */
+ if( astTestTimeScale( cfrm ) ) astSetTimeScale( frm, astGetTimeScale( cfrm ) );
+ if( astTestSystem( cfrm ) ) astSetSystem( frm, astGetSystem( cfrm ) );
+ if( astTestUnit( cfrm, 0 ) ) astSetUnit( frm, 0, astGetUnit( cfrm, 0 ) );
+ if( astTestTimeOrigin( cfrm ) ) astSetTimeOrigin( frm, astGetTimeOrigin( cfrm ) );
+
+/* Create a Interval from it and store in the returned ancillary KeyMap. If
+ the units of this Frame differs from that of the supplied Frame, set it
+ to the units of the supplied Frame. This will cause the encapsulated
+ limits to be mapped into the new units. Ensure the original title is
+ preserved. Use an Interval rather than a PointList since an Interval
+ can be used within a Prism to extrude another Region, but a PointList
+ cannot. */
+ r = (AstRegion *) astInterval( cfrm, &value, &value, NULL, "", status ) ;
+ if( funit ) astSetUnit( r, 0, funit );
+ if( title ) astSetTitle( r, title );
+ astMapPut0A( *anc, AST__STCVALUE, r, NULL );
+ r = astAnnul( r );
+ }
+
+/* Does this Time contain any Error? */
+ if( scan->count[ 1 ] > 0 ) {
+
+/* Issue a warning if more than 1 Error value was found. */
+ if( scan->count[ 1 ] > 1 ) {
+ Report( this, elem, WARNING, "contains more than one <Error>"
+ " element. AST can only use the first", status );
+ }
+
+/* Get the first Error value. */
+ v = ElemValueD( this, scan->el[1][0], AST__BAD, status );
+ if( v != AST__BAD ) {
+
+/* Create the upper and lower limits of an error bar centred on zero. */
+ ubnd[ 0 ] = 0.5*fabs( v );
+ lbnd[ 0 ] = -ubnd[ 0 ];
+
+/* Create an Interval within the time Frame. */
+ result = (AstRegion *) astInterval( cfrm, lbnd, ubnd, NULL, "", status );
+
+/* If the units of this Frame differs from that of the supplied Frame,
+ set it to the units of the supplied Frame. This will cause the
+ encapsulated limits to be mapped into the new units. */
+ if( funit ) astSetUnit( result, 0, funit );
+
+/* Ensure the original title is preserved. */
+ if( title ) astSetTitle( result, title );
+
+/* Store in the returned ancillary KeyMap. */
+ astMapPut0A( *anc, AST__STCERROR, result, NULL );
+
+ }
+ }
+
+/* Does this Time contain any Resolution? */
+ if( scan->count[ 3 ] > 0 ) {
+
+/* Issue a warning if more than 1 Resolution value was found. */
+ if( scan->count[ 3 ] > 1 ) {
+ Report( this, elem, WARNING, "contains more than one <Resolution>"
+ " element. AST can only use the first", status );
+ }
+
+/* Get the first Resolution value. */
+ v = ElemValueD( this, scan->el[3][0], AST__BAD, status );
+ if( v != AST__BAD ) {
+
+/* Create the upper and lower limits of a bar centred on zero. */
+ ubnd[ 0 ] = 0.5*fabs( v );
+ lbnd[ 0 ] = -ubnd[ 0 ];
+
+/* Create an Interval within the time Frame. */
+ r = (AstRegion *) astInterval( cfrm, lbnd, ubnd, NULL, "", status );
+
+/* If the units of this Frame differs from that of the supplied Frame,
+ set it to the units of the supplied Frame. This will cause the
+ encapsulated limits to be mapped into the new units. */
+ if( funit ) astSetUnit( r, 0, funit );
+
+/* Ensure the original title is preserved. */
+ if( title ) astSetTitle( r, title );
+
+/* Store in the returned ancillary KeyMap. */
+ astMapPut0A( *anc, AST__STCRES, r, NULL );
+ r = astAnnul( r );
+ }
+ }
+
+/* Does this Time contain any Size? */
+ if( scan->count[ 4 ] > 0 ) {
+
+/* Issue a warning if more than 1 Size value was found. */
+ if( scan->count[ 4 ] > 1 ) {
+ Report( this, elem, WARNING, "contains more than one <Size>"
+ " element. AST can only use the first", status );
+ }
+
+/* Get the first Size value. */
+ v = ElemValueD( this, scan->el[4][0], AST__BAD, status );
+ if( v != AST__BAD ) {
+
+/* Create the upper and lower limits of a bar centred on zero. */
+ ubnd[ 0 ] = 0.5*fabs( v );
+ lbnd[ 0 ] = -ubnd[ 0 ];
+
+/* Create an Interval within the time Frame. */
+ r = (AstRegion *) astInterval( cfrm, lbnd, ubnd, NULL, "", status );
+
+/* If the units of this Frame differs from that of the supplied Frame,
+ set it to the units of the supplied Frame. This will cause the
+ encapsulated limits to be mapped into the new units. */
+ if( funit ) astSetUnit( r, 0, funit );
+
+/* Ensure the original title is preserved. */
+ if( title ) astSetTitle( r, title );
+
+/* Store in the returned ancillary KeyMap. */
+ astMapPut0A( *anc, AST__STCSIZE, r, NULL );
+ r = astAnnul( r );
+ }
+ }
+
+/* Does this Time contain any PixSize? */
+ if( scan->count[ 5 ] > 0 ) {
+
+/* Issue a warning if more than 1 PixSize value was found. */
+ if( scan->count[ 5 ] > 1 ) {
+ Report( this, elem, WARNING, "contains more than one <PixSize>"
+ " element. AST can only use the first", status );
+ }
+
+/* Get the first PixSize value. */
+ v = ElemValueD( this, scan->el[5][0], AST__BAD, status );
+ if( v != AST__BAD ) {
+
+/* Create the upper and lower limits of a bar centred on zero. */
+ ubnd[ 0 ] = 0.5*fabs( v );
+ lbnd[ 0 ] = -ubnd[ 0 ];
+
+/* Create an Interval within the time Frame. */
+ r = (AstRegion *) astInterval( cfrm, lbnd, ubnd, NULL, "", status );
+
+/* If the units of this Frame differs from that of the supplied Frame,
+ set it to the units of the supplied Frame. This will cause the
+ encapsulated limits to be mapped into the new units. */
+ if( funit ) astSetUnit( r, 0, funit );
+
+/* Ensure the original title is preserved. */
+ if( title ) astSetTitle( r, title );
+
+/* Store in the returned ancillary KeyMap. */
+ astMapPut0A( *anc, AST__STCPIXSZ, r, NULL );
+ r = astAnnul( r );
+ }
+ }
+
+/* Free resources */
+ if( funit ) funit = astFree( (void *) funit );
+ cfrm = astAnnul( cfrm );
+ if( title ) title = astFree( (void *) title );
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Return NULL if an error occurred. */
+ if( !astOK ) result = astAnnul( result );
+
+/* Return the result */
+ return result;
+
+}
+
+static AstTimeScaleType TimeScaleReader( AstXmlChan *this, AstXmlElement *elem, int *status ){
+/*
+* Name:
+* TimeScaleReader
+
+* Purpose:
+* Read a time value from an IVOA TimeScale element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstTimeScaleType TimeScaleReader( AstXmlChan *this, AstXmlElement *elem, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function returns a value representing the timescale specified by
+* the supplied IVOA TimeScale element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA TimeScale element.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The timescale value (values are defined in timeframe.h).
+
+*/
+
+/* Local Variables: */
+ AstTimeScaleType result;
+ char buff[ 80 ];
+ const char *tstxt;
+
+/* Initialise */
+ result = AST__BADTS;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Get the timescale string from the element, and find the corresponding
+ AST timescale value (if any). */
+ tstxt = astXmlGetValue( elem, 0 );
+ if( tstxt ) {
+
+ if( !strcmp( tstxt, "TT" ) ) {
+ result = AST__TT;
+
+ } else if( !strcmp( tstxt, "TDT" ) ) {
+ result = AST__TT;
+
+ } else if( !strcmp( tstxt, "ET" ) ) {
+ Report( this, elem, WARNING, "TT will be used in place of ET", status );
+ result = AST__TT;
+
+ } else if( !strcmp( tstxt, "TDB" ) ) {
+ result = AST__TDB;
+
+ } else if( !strcmp( tstxt, "TCG" ) ) {
+ result = AST__TCG;
+
+ } else if( !strcmp( tstxt, "TCB" ) ) {
+ result = AST__TCB;
+
+ } else if( !strcmp( tstxt, "TAI" ) ) {
+ result = AST__TAI;
+
+ } else if( !strcmp( tstxt, "IAT" ) ) {
+ result = AST__TAI;
+
+ } else if( !strcmp( tstxt, "UTC" ) ) {
+ result = AST__UTC;
+
+ } else if( !strcmp( tstxt, "LST" ) ) {
+ result = AST__LMST;
+
+ } else {
+ sprintf( buff, "contains unsupported timescale %s", tstxt );
+ Report( this, elem, FAILURE, buff, status );
+ result = AST__BADTS;
+ }
+ }
+
+/* Return the time value. */
+ return result;
+}
+
+static AstRegion *UnionReader( AstXmlChan *this, AstXmlElement *elem,
+ AstFrame *frm, int *status ){
+/*
+* Name:
+* UnionReader
+
+* Purpose:
+* Make an AST Region from an IVOA Union region element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstRegion *UnionReader( AstXmlChan *this, AstXmlElement *elem,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function makes a new AST Region from the supplied IVOA
+* Union region element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Union region element.
+* frm
+* Pointer to the 2D Frame in which the returned Region should be
+* defined. If the Unit attribute is not set, this function will
+* set it to the value supplied in "unit" before returning.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the new Region.
+
+*/
+
+/* Local Variables: */
+ AstRegion *new; /* Pointer to returned Region */
+ AstRegion *reg; /* Pointer to component Region */
+ AstRegion *tmp; /* Pointer to new Region */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *names[1]; /* Names of the subelements to be searched for */
+ int i; /* Loop count */
+ int max[1]; /* Max allowed occurrences of each name */
+ int min[1]; /* Min allowed occurrences of each name */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Search the supplied element for a Region sub-element. */
+ names[ 0 ] = "Intersection|Union|Negation|AllSky|Circle|Ellipse|Polygon|"
+ "Convex|Box";
+ min[ 0 ] = 2;
+ max[ 0 ] = INT_MAX;
+ scan = ScanIVOAElement( this, elem, 1, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Create Regions from all the component region elements, and combine
+ them into nested CmpRegions, using the boolean OR operator to combine
+ them. */
+ new = RegionReader( this, scan->el[0][0], frm, status );
+ for( i = 1; i < scan->count[0]; i++ ) {
+ reg = RegionReader( this, scan->el[0][i], frm, status );
+ tmp = (AstRegion *) astCmpRegion( new, reg, AST__OR, "", status );
+ reg = astAnnul( reg );
+ (void) astAnnul( new );
+ new = tmp;
+ }
+
+/* Get any fill factor from the element and assign to the returned Region. */
+ FillAndLims( this, elem, new, status );
+
+/* Free resources */
+ scan = FreeIVOAScan( scan, status );
+ }
+
+/* Annul any returned Frame if an error has occurred. */
+ if( !astOK ) new = astAnnul( new );
+
+/* Return the pointer to the new Region. */
+ return new;
+}
+
+static int Use( AstXmlChan *this, int set, int helpful, int *status ) {
+/*
+* Name:
+* Use
+
+* Purpose:
+* Decide whether to write a value to a data sink.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "channel.h"
+* int Use( AstXmlChan *this, int set, int helpful, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function decides whether a value supplied by a class "Dump"
+* function, via a call to one of the astWrite... protected
+* methods, should actually be written to the data sink associated
+* with a XmlChan.
+*
+* This decision is based on the settings of the "set" and
+* "helpful" flags supplied to the astWrite... method, plus the
+* attribute settings of the XmlChan.
+
+* Parameters:
+* this
+* A pointer to the XmlChan.
+* set
+* The "set" flag supplied.
+* helpful
+* The "helpful" value supplied.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* One if the value should be written out, otherwise zero.
+
+* Notes:
+* - A value of zero will be returned if this function is invoked
+* with the global error status set or if it should fail for any
+* reason.
+*/
+
+/* Local Variables: */
+ int full; /* Full attribute value */
+ int result; /* Result value to be returned */
+
+/* Check the global error status. */
+ if ( !astOK ) return 0;
+
+/* If "set" is non-zero, then so is the result ("set" values must
+ always be written out). */
+ result = ( set != 0 );
+
+/* Otherwise, obtain the value of the XmlChan's Full attribute. */
+ if ( !set ) {
+ full = astGetFull( this );
+
+/* If Full is positive, display all values, if zero, display only
+ "helpful" values, if negative, display no (un-"set") values. */
+ if ( astOK ) result = ( ( helpful && ( full > -1 ) ) || ( full > 0 ) );
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static int Ustrcmp( const char *a, const char *b, int *status ){
+/*
+* Name:
+* Ustrcmp
+
+* Purpose:
+* A case blind version of strcmp.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* int Ustrcmp( const char *a, const char *b, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* Returns 0 if there are no differences between the two strings, and 1
+* otherwise. Comparisons are case blind.
+
+* Parameters:
+* a
+* Pointer to first string.
+* b
+* Pointer to second string.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Zero if the strings match, otherwise one.
+
+* Notes:
+* - This function does not consider the sign of the difference between
+* the two strings, whereas "strcmp" does.
+* - This function attempts to execute even if an error has occurred.
+
+*/
+
+/* Local Variables: */
+ const char *aa; /* Pointer to next "a" character */
+ const char *bb; /* Pointer to next "b" character */
+ int ret; /* Returned value */
+
+/* Initialise the returned value to indicate that the strings match. */
+ ret = 0;
+
+/* Initialise pointers to the start of each string. */
+ aa = a;
+ bb = b;
+
+/* Loop round each character. */
+ while( 1 ){
+
+/* We leave the loop if either of the strings has been exhausted. */
+ if( !(*aa ) || !(*bb) ){
+
+/* If one of the strings has not been exhausted, indicate that the
+ strings are different. */
+ if( *aa || *bb ) ret = 1;
+
+/* Break out of the loop. */
+ break;
+
+/* If neither string has been exhausted, convert the next characters to
+ upper case and compare them, incrementing the pointers to the next
+ characters at the same time. If they are different, break out of the
+ loop. */
+ } else {
+
+ if( toupper( (int) *(aa++) ) != toupper( (int) *(bb++) ) ){
+ ret = 1;
+ break;
+ }
+
+ }
+
+ }
+
+/* Return the result. */
+ return ret;
+
+}
+
+static int Ustrncmp( const char *a, const char *b, size_t n, int *status ){
+/*
+* Name:
+* Ustrncmp
+
+* Purpose:
+* A case blind version of strncmp.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* int Ustrncmp( const char *a, const char *b, size_t n, int *status )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* Returns 0 if there are no differences between the first "n"
+* characters of the two strings, and 1 otherwise. Comparisons are
+* case blind.
+
+* Parameters:
+* a
+* Pointer to first string.
+* b
+* Pointer to second string.
+* n
+* The maximum number of characters to compare.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Zero if the strings match, otherwise one.
+
+* Notes:
+* - This function does not consider the sign of the difference between
+* the two strings, whereas "strncmp" does.
+* - This function attempts to execute even if an error has occurred.
+
+*/
+
+/* Local Variables: */
+ const char *aa; /* Pointer to next "a" character */
+ const char *bb; /* Pointer to next "b" character */
+ int i; /* Character index */
+ int ret; /* Returned value */
+
+/* Initialise the returned value to indicate that the strings match. */
+ ret = 0;
+
+/* Initialise pointers to the start of each string. */
+ aa = a;
+ bb = b;
+
+/* Compare up to "n" characters. */
+ for( i = 0; i < (int) n; i++ ){
+
+/* We leave the loop if either of the strings has been exhausted. */
+ if( !(*aa ) || !(*bb) ){
+
+/* If one of the strings has not been exhausted, indicate that the
+ strings are different. */
+ if( *aa || *bb ) ret = 1;
+
+/* Break out of the loop. */
+ break;
+
+/* If neither string has been exhausted, convert the next characters to
+ upper case and compare them, incrementing the pointers to the next
+ characters at the same time. If they are different, break out of the
+ loop. */
+ } else {
+
+ if( toupper( (int) *(aa++) ) != toupper( (int) *(bb++) ) ){
+ ret = 1;
+ break;
+ }
+
+ }
+
+ }
+
+/* Return the result. */
+ return ret;
+
+}
+
+static int VertexReader( AstXmlChan *this, AstXmlElement *elem, double *x,
+ double *y, int *status ){
+/*
+* Name:
+* VertexReader
+
+* Purpose:
+* Read a position from an IVOA Vertex element.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* int VertexReader( AstXmlChan *this, AstXmlElement *elem, double *x,
+* double *y )
+
+* Class Membership:
+* XmlChan member function.
+
+* Description:
+* This function reads a 2D position from the supplied IVOA Vertex
+* element.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* elem
+* Pointer to the IVOA Vertex element.
+* x
+* Pointer to the double in which to put the returned X value.
+* y
+* Pointer to the double in which to put the returned Y value.
+
+* Returned Value:
+* Non-zero if the <Vertex> contains a <pole> tag.
+
+*/
+
+/* Local Variables: */
+ IVOAScan *scan; /* Structure holding scan results */
+ const char *names[2]; /* Names of the subelements to be searched for */
+ double xy[ 2 ]; /* Axis values read from Position */
+ int max[2]; /* Max allowed occurrences of each name */
+ int min[2]; /* Min allowed occurrences of each name */
+ int result; /* Returned value */
+
+/* Check the global error status. */
+ if ( !astOK ) return 0;
+
+/* Initialise */
+ result = 0;
+ *x = AST__BAD;
+ *y = AST__BAD;
+
+/* Search the supplied element for the required sub-elements. */
+ names[ 0 ] = "Position";
+ max[ 0 ] = 1;
+ min[ 0 ] = 1;
+ names[ 1 ] = "SmallCircle";
+ max[ 1 ] = 1;
+ min[ 1 ] = 0;
+ scan = ScanIVOAElement( this, elem, 2, names, min, max, status );
+
+/* If succesfull.. */
+ if( scan ) {
+
+/* Get the axis values from the Position element. */
+ xy[ 0 ] = AST__BAD;
+ xy[ 1 ] = AST__BAD;
+ ElemListD( this, scan->el[0][0], 2, xy, status );
+ *x = xy[ 0 ];
+ *y = xy[ 1 ];
+
+/* Get any SmallCircle element. If it has a Pole issue a warning. */
+ result = scan->count[ 1 ];
+ if( result ) {
+ if( FindElement( this, scan->el[1][0], "Pole", status ) ) {
+ Report( this, scan->el[1][0], WARNING, "contains a <Pole> "
+ "tag (poles are not supported by AST)", status );
+ }
+ }
+
+/* Free resources */
+ scan = FreeIVOAScan( scan, status );
+ }
+
+ return result;
+}
+
+static void WriteBegin( AstChannel *this_channel, const char *class,
+ const char *comment, int *status ) {
+/*
+* Name:
+* WriteBegin
+
+* Purpose:
+* Write a "Begin" data item to a data sink.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* void WriteBegin( AstChannel *this_channel, const char *class,
+* const char *comment, int *status )
+
+* Class Membership:
+* XmlChan member function (over-rides the protected
+* astWriteBegin method inherited from the Channel class).
+
+* Description:
+* This function writes a "Begin" data item to the data sink
+* associated with a Channel, so as to begin the output of a new
+* Object definition.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* class
+* Pointer to a constant null-terminated string containing the
+* name of the class to which the Object belongs.
+* comment
+* Pointer to a constant null-terminated string containing a
+* textual comment to be associated with the "Begin"
+* item. Normally, this will describe the purpose of the Object.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - The comment supplied may not actually be used, depending on
+* the nature of the Channel supplied.
+*/
+
+/* Local Variables: */
+ AstXmlChan *this; /* A pointer to the XmlChan structure. */
+ AstXmlElement *elem; /* The XML element to hodl the new AST object */
+ const char *pref; /* XML namespace prefix to use */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) this_channel;
+
+/* If this is a top level object (i.e. if there is no container element),
+ reset all the other values in the XmlChan for safety. */
+ if( !this->container ) {
+ this->objectname = NULL;
+ this->objectset = 1;
+ this->objectcomment = NULL;
+ }
+
+/* Initialise a flag to indicate that the next "IsA" item should not be
+ written. This flag will be changed if and when an item is added which
+ related to the class described by the "IsA" item. Save the old value
+ first. */
+ this->write_isa = 0;
+
+/* Store the namespace prefix. */
+ pref = astGetXmlPrefix( this );
+
+/* Create a new XmlElement with a name equal to the AST class name of the
+ object being dumped (and no namespace prefix), and add it into the
+ current container (i.e. parent) element. */
+ elem = astXmlAddElement( this->container, class, pref );
+
+/* If this is a top level container, store the namespace URI for
+ the element, either default or named depending on the value of
+ XmlPrefix. */
+ if( !this->container ) astXmlAddURI( elem, pref, AST__XMLNS );
+
+/* If non-blank, append a "Label" atttribute to the element holding the
+ name of the object (stored in the XmlChan structure). */
+ if( this->objectname ) astXmlAddAttr( elem, LABEL, this->objectname, NULL );
+
+/* If the object has all default values, store a true value for the
+ DEFAULT attribute. */
+ if( !this->objectset ) astXmlAddAttr( elem, DEFAULT, TRUE, NULL );
+
+/* Add commments if required. */
+ if( astGetComment( this_channel ) ) {
+
+/* If we are adding comments, and if a comment was supplied as a
+ parameter to this function, then store the commment as an attribute of
+ the element. This comment describes the class function as a whole, not
+ the specific usage of this instance of the class (this is given by the
+ comment in this->objectcomment). */
+ if( comment && *comment ) astXmlAddComment( elem, 0, comment );
+
+/* If the object has a usage comment, add it to the content of the
+ element if required. */
+ if( this->objectcomment ) astXmlAddAttr( elem, DESC, this->objectcomment, NULL );
+ }
+
+/* Make the new element the current container. */
+ this->container = (AstXmlParent *) elem;
+
+/* If an error has occurred, annul the container element in the XmlChan. */
+ if( !astOK ) this->container = astXmlAnnulTree( this->container );
+
+}
+
+static void WriteDouble( AstChannel *this_channel, const char *name,
+ int set, int helpful,
+ double value, const char *comment, int *status ) {
+/*
+* Name:
+* WriteDouble
+
+* Purpose:
+* Write a double value to a data sink.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* void WriteDouble( AstChannel *this, const char *name,
+* int set, int helpful,
+* double value, const char *comment, int *status )
+
+* Class Membership:
+* XmlChan member function (over-rides the protected
+* astWriteDouble method inherited from the Channel class).
+
+* Description:
+* This function writes a named double value, representing the
+* value of a class instance variable, to the data sink associated
+* with a Channel. It is intended for use by class "Dump" functions
+* when writing out class information which will subsequently be
+* re-read.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* name
+* Pointer to a constant null-terminated string containing the
+* name to be used to identify the value in the external
+* representation. This will form the key for identifying it
+* again when it is re-read. The name supplied should be unique
+* within its class.
+*
+* Mixed case may be used and will be preserved in the external
+* representation (where possible) for cosmetic effect. However,
+* case is not significant when re-reading values.
+*
+* It is recommended that a maximum of 6 alphanumeric characters
+* (starting with an alphabetic character) be used. This permits
+* maximum flexibility in adapting to standard external data
+* representations (e.g. FITS).
+* set
+* If this is zero, it indicates that the value being written is
+* a default value (or can be re-generated from other values) so
+* need not necessarily be written out. Such values will
+* typically be included in the external representation with
+* (e.g.) a comment character so that they are available to
+* human readers but will be ignored when re-read. They may also
+* be completely omitted in some circumstances.
+*
+* If "set" is non-zero, the value will always be explicitly
+* included in the external representation so that it can be
+* re-read.
+* helpful
+* This flag provides a hint about whether a value whose "set"
+* flag is zero (above) should actually appear at all in the
+* external representaton.
+*
+* If the external representation allows values to be "commented
+* out" then, by default, values will be included in this form
+* only if their "helpful" flag is non-zero. Otherwise, they
+* will be omitted entirely. When possible, omitting the more
+* obscure values associated with a class is recommended in
+* order to improve readability.
+*
+* This default behaviour may be further modified if the
+* Channel's Full attribute is set - either to permit all values
+* to be shown, or to suppress non-essential information
+* entirely.
+* value
+* The value to be written.
+* comment
+* Pointer to a constant null-terminated string containing a
+* textual comment to be associated with the value.
+*
+* Note that this comment may not actually be used, depending on
+* the nature of the Channel supplied and the setting of its
+* Comment attribute.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Constants: */
+#define BUFF_LEN 100 /* Size of local formatting buffer */
+
+/* Local Variables: */
+ AstXmlChan *this; /* A pointer to the XmlChan structure. */
+ AstXmlElement *elem; /* Pointer to new element */
+ char buff[ BUFF_LEN + 1 ]; /* Local formatting buffer */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) this_channel;
+
+/* If the object to be written is a component of a default AST object (i.e.
+ an object which is "not set"), then we do not write out this item. */
+ if( this->objectset ) {
+
+/* Use the "set" and "helpful" flags, along with the Channel's
+ attributes to decide whether this value should actually be
+ written. */
+ if( Use( this, set, helpful, status ) ) {
+
+/* Create a new XmlElement with a name of ATTR (and no namespace
+ prefix), and add it into the current container (i.e. parent) element. */
+ elem = astXmlAddElement( this->container, ATTR,
+ astGetXmlPrefix( this ) );
+
+/* Add a NAME attribute to this element containing the item name. */
+ astXmlAddAttr( elem, NAME, name, NULL );
+
+/* Format the value as a string and store it as the VALUE attribute.
+ Make sure "-0" isn't produced. Use a magic string to represent bad
+ values. */
+ if( value != AST__BAD ) {
+ (void) sprintf( buff, "%.*g", AST__DBL_DIG, value );
+ if ( !strcmp( buff, "-0" ) ) {
+ buff[ 0 ] = '0';
+ buff[ 1 ] = '\0';
+ }
+ } else {
+ strcpy( buff, BAD_STRING );
+ }
+ astXmlAddAttr( elem, VALUE, buff, NULL );
+
+/* If we are adding comments, and if a comment was supplied as a
+ parameter to this function, then store the commment as an attribute of
+ the element. */
+ if( comment && *comment && astGetComment( this_channel ) ) {
+ astXmlAddAttr( elem, DESC, comment, NULL );
+ }
+
+/* If the object has all default values, store a true value for the
+ DEFAULT attribute. */
+ if( !set ) astXmlAddAttr( elem, DEFAULT, TRUE, NULL );
+
+/* Initialise a flag to indicate that the next "IsA" item should be
+ written. */
+ this->write_isa = 1;
+ }
+ }
+
+/* If an error has occurred, annul the container element in the XmlChan. */
+ if( !astOK ) this->container = astXmlAnnulTree( this->container );
+
+/* Undefine macros local to this function. */
+#undef BUFF_LEN
+}
+
+static void WriteEnd( AstChannel *this_channel, const char *class, int *status ) {
+/*
+* Name:
+* WriteEnd
+
+* Purpose:
+* Write an "End" data item to a data sink.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* void WriteEnd( AstChannel *this, const char *class, int *status )
+
+* Class Membership:
+* XmlChan member function (over-rides the protected
+* astWriteEnd method inherited from the Channel class).
+
+* Description:
+* This function writes an "End" data item to the data sink
+* associated with a Channel. This item delimits the end of an
+* Object definition.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* class
+* Pointer to a constant null-terminated string containing the
+* class name of the Object whose definition is being terminated
+* by the "End" item.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ AstXmlChan *this; /* Pointer to the XmlChan structure */
+ AstXmlParent *parent; /* Pointer to parent element */
+ char *d; /* Pointer to end of next sub-string */
+ char *c; /* Pointer to start of next sub-string */
+ char *text; /* Pointer to complete string */
+ int mxlen; /* Max allowed length of text */
+
+#ifdef DEBUG
+ int nobj; /* No. of XmlObjects in existence */
+#endif
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+#ifdef DEBUG
+/* Save the number of XmlObjects currently in existenece. */
+ nobj = astXmlTrace(3);
+#endif
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) this_channel;
+
+/* Get the parent of the current container element. */
+ if( this->container ) {
+ parent = astXmlGetParent( this->container );
+
+/* If the current container element has no parent, we have completed the
+ construction of the in-memory XML representation of the AST object being
+ written out. In this case, we convert the in-memory representation
+ into a set of strings and write them out using the supplied sink
+ function. */
+ if( !parent ) {
+
+/* First get a single string holding the complete formatted XML
+ representation of the AST object. */
+ if( astGetIndent( this ) ) {
+ text = (char *) astXmlShow( this->container );
+ } else {
+ text = (char *) astXmlFormat( this->container );
+ }
+
+/* Now, if we have any text, split it into separate lines. The end of a line
+ is indicated by a "\n" character in the text returned by astXmlFormat. */
+ if( text ) {
+
+/* Get the maximum allowed line length. */
+ mxlen = astGetXmlLength( this );
+
+/* Loop round locating each '\n' character in the string. Replace the
+ '\n' character by 0, so that the previous part of the string is then
+ null terminated, and write it out using the astPutNextText method
+ (splitting the text up into lines no longer than "mxlen"). */
+ c = text;
+ d = strchr( c, '\n' );
+ while( d ) {
+ *d = 0;
+ OutputText( this, c, mxlen, status );
+ c = d + 1;
+ d = strchr( c, '\n' );
+ }
+
+/* Write out any text following the last '\n' character. */
+ if( *c ) OutputText( this, c, mxlen, status );
+
+/* Free the memory holding the text and in-memory representations of the AST
+ Object. */
+ text = astFree( (void *) text );
+ astXmlRemoveItem( this->container );
+ this->container = astXmlAnnul( this->container );
+
+#ifdef DEBUG
+/* Report an error if there is a memory leak. */
+ if( astXmlTrace(3) > nobj && astOK ) {
+ astError( AST__INTER, "astWriteEnd(XmlChan): %d XmlObjects "
+ "remain in existence - should be %d (internal AST "
+ "programming error).", status, astXmlTrace(3), nobj );
+ }
+#endif
+
+ }
+ }
+
+/* Reset the current container element to be the parent found above. */
+ if( !parent || astXmlCheckType( parent, AST__XMLELEM ) ) {
+ this->container = parent;
+ } else if( astOK ) {
+ astError( AST__INTER, "astWriteEnd(XmlChan): Cannot update "
+ "container: parent is not an XmlElement (internal "
+ "AST programming error)." , status);
+ }
+ }
+
+/* If an error has occurred, annul the container element in the XmlChan. */
+ if( !astOK ) this->container = astXmlAnnulTree( this->container );
+
+}
+
+static void WriteInt( AstChannel *this_channel, const char *name, int set, int helpful,
+ int value, const char *comment, int *status ) {
+/*
+* Name:
+* WriteInt
+
+* Purpose:
+* Write an integer value to a data sink.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* void WriteInt( AstChannel *this, const char *name, int set, int helpful,
+* int value, const char *comment, int *status )
+
+* Class Membership:
+* XmlChan member function (over-rides the protected
+* astWriteInt method inherited from the Channel class).
+
+* Description:
+* This function writes a named integer value, representing the
+* value of a class instance variable, to the data sink associated
+* with a Channel. It is intended for use by class "Dump" functions
+* when writing out class information which will subsequently be
+* re-read.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* name
+* Pointer to a constant null-terminated string containing the
+* name to be used to identify the value in the external
+* representation. This will form the key for identifying it
+* again when it is re-read. The name supplied should be unique
+* within its class.
+*
+* Mixed case may be used and will be preserved in the external
+* representation (where possible) for cosmetic effect. However,
+* case is not significant when re-reading values.
+*
+* It is recommended that a maximum of 6 alphanumeric characters
+* (starting with an alphabetic character) be used. This permits
+* maximum flexibility in adapting to standard external data
+* representations (e.g. FITS).
+* set
+* If this is zero, it indicates that the value being written is
+* a default value (or can be re-generated from other values) so
+* need not necessarily be written out. Such values will
+* typically be included in the external representation with
+* (e.g.) a comment character so that they are available to
+* human readers but will be ignored when re-read. They may also
+* be completely omitted in some circumstances.
+*
+* If "set" is non-zero, the value will always be explicitly
+* included in the external representation so that it can be
+* re-read.
+* helpful
+* This flag provides a hint about whether a value whose "set"
+* flag is zero (above) should actually appear at all in the
+* external representaton.
+*
+* If the external representation allows values to be "commented
+* out" then, by default, values will be included in this form
+* only if their "helpful" flag is non-zero. Otherwise, they
+* will be omitted entirely. When possible, omitting the more
+* obscure values associated with a class is recommended in
+* order to improve readability.
+*
+* This default behaviour may be further modified if the
+* Channel's Full attribute is set - either to permit all values
+* to be shown, or to suppress non-essential information
+* entirely.
+* value
+* The value to be written.
+* comment
+* Pointer to a constant null-terminated string containing a
+* textual comment to be associated with the value.
+*
+* Note that this comment may not actually be used, depending on
+* the nature of the Channel supplied and the setting of its
+* Comment attribute.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Constants: */
+#define BUFF_LEN 50 /* Size of local formatting buffer */
+
+/* Local Variables: */
+ AstXmlChan *this; /* A pointer to the XmlChan structure. */
+ AstXmlElement *elem; /* Pointer to new element */
+ char buff[ BUFF_LEN + 1 ]; /* Local formatting buffer */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) this_channel;
+
+/* If the object to be written is a component of a default AST object (i.e.
+ an object which is "not set"), then we do not write out this item. */
+ if( this->objectset ) {
+
+/* Use the "set" and "helpful" flags, along with the Channel's
+ attributes to decide whether this value should actually be
+ written. */
+ if( Use( this, set, helpful, status ) ) {
+
+/* Create a new XmlElement with a name of ATTR (and no namespace
+ prefix), and add it into the current container (i.e. parent) element. */
+ elem = astXmlAddElement( this->container, ATTR,
+ astGetXmlPrefix( this ) );
+
+/* Add a NAME attribute to this element containing the item name. */
+ astXmlAddAttr( elem, NAME, name, NULL );
+
+/* Format the value as a decimal string and add it to the element as the
+ VALUE attribute. */
+ (void) sprintf( buff, "%d", value );
+ astXmlAddAttr( elem, VALUE, buff, NULL );
+
+/* If we are adding comments, and if a comment was supplied as a
+ parameter to this function, then store the commment as an attribute of
+ the element. */
+ if( comment && *comment && astGetComment( this_channel ) ) {
+ astXmlAddAttr( elem, DESC, comment, NULL );
+ }
+
+/* If the object has all default values, store a true value for the
+ DEFAULT attribute. */
+ if( !set ) astXmlAddAttr( elem, DEFAULT, TRUE, NULL );
+
+/* Initialise a flag to indicate that the next "IsA" item should be
+ written. */
+ this->write_isa = 1;
+ }
+ }
+
+/* If an error has occurred, annul the container element in the XmlChan. */
+ if( !astOK ) this->container = astXmlAnnulTree( this->container );
+
+/* Undefine macros local to this function. */
+#undef BUFF_LEN
+}
+
+static void WriteIsA( AstChannel *this_channel, const char *class,
+ const char *comment, int *status ) {
+/*
+* Name:
+* WriteIsA
+
+* Purpose:
+* Write an "IsA" data item to a data sink.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* void WriteIsA( AstChannel *this, const char *class,
+* const char *comment, int *status )
+
+* Class Membership:
+* XmlChan member function (over-rides the protected
+* astWriteIsA method inherited from the Channel class).
+
+* Description:
+* This function writes an "IsA" data item to the data sink
+* associated with a Channel. This item delimits the end of the
+* data associated with the instance variables of a class, as part
+* of an overall Object definition.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* class
+* Pointer to a constant null-terminated string containing the
+* name of the class whose data are terminated by the "IsA"
+* item.
+* comment
+* Pointer to a constant null-terminated string containing a
+* textual comment to be associated with the "IsA"
+* item. Normally, this will describe the purpose of the class
+* whose data are being terminated.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - The comment supplied may not actually be used, depending on
+* the nature of the Channel supplied.
+*/
+
+/* Local Variables: */
+ AstXmlChan *this; /* A pointer to the XmlChan structure. */
+ AstXmlElement *elem; /* Pointer to new element */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) this_channel;
+
+/* If the object to be written is a component of a default AST object (i.e.
+ an object which is "not set"), then we do not write out this item. */
+ if( this->objectset ) {
+
+/* Output an "IsA" item only if there has been at least one item
+ written since the last "Begin" or "IsA" item, or if the Full
+ attribute for the Channel is greater than zero (requesting maximum
+ information). */
+ if ( this->write_isa || astGetFull( this ) > 0 ) {
+
+/* Create a new XmlElement with a name of "_isa" (and no namespace prefix),
+ and add it into the current container (i.e. parent) element. */
+ elem = astXmlAddElement( this->container, ISA,
+ astGetXmlPrefix( this ) );
+
+/* Add a "class" attribute to this element containing the class name. */
+ astXmlAddAttr( elem, "class", class, NULL );
+
+/* If we are adding comments, and if a comment was supplied as a
+ parameter to this function, then store the commment as an attribute of
+ the element. This comment describes the class function as a whole, not
+ the specific usage of this instance of the class. */
+ if( comment && *comment && astGetComment( this_channel ) ) {
+ astXmlAddAttr( elem, DESC, comment, NULL );
+ }
+ }
+ }
+
+/* Initialise a flag to indicate that the next "IsA" item should not be
+ written. This flag will be changed if and when an item is added which
+ related to the class described by the "IsA" item. */
+ this->write_isa = 0;
+
+/* If an error has occurred, annul the container element in the XmlChan. */
+ if( !astOK ) this->container = astXmlAnnulTree( this->container );
+}
+
+static void WriteObject( AstChannel *this_channel, const char *name,
+ int set, int helpful,
+ AstObject *value, const char *comment, int *status ) {
+/*
+* Name:
+* WriteObject
+
+* Purpose:
+* Write an Object as a value to a data sink.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* void WriteObject( AstChannel *this_channel, const char *name,
+* int set, int helpful,
+* AstObject *value, const char *comment, int *status )
+
+* Class Membership:
+* XmlChan member function (over-rides the protected
+* astWriteObject method inherited from the Channel class).
+
+* Description:
+* This function writes an Object as a named value, representing
+* the value of a class instance variable, to the data sink
+* associated with an XmlChan. It is intended for use by class
+* "Dump" functions when writing out class information which will
+* subsequently be re-read.
+
+* Parameters:
+* this
+* Pointer to the XmlChan.
+* name
+* Pointer to a constant null-terminated string containing the
+* name to be used to identify the value in the external
+* representation. This will form the key for identifying it
+* again when it is re-read. The name supplied should be unique
+* within its class.
+*
+* Mixed case may be used and will be preserved in the external
+* representation (where possible) for cosmetic effect. However,
+* case is not significant when re-reading values.
+*
+* It is recommended that a maximum of 6 alphanumeric characters
+* (starting with an alphabetic character) be used. This permits
+* maximum flexibility in adapting to standard external data
+* representations.
+* set
+* If this is zero, it indicates that the value being written is
+* a default value (or can be re-generated from other values) so
+* need not necessarily be written out. Such values will
+* typically be included in the external representation with
+* (e.g.) a comment character so that they are available to
+* human readers but will be ignored when re-read. They may also
+* be completely omitted in some circumstances.
+*
+* If "set" is non-zero, the value will always be explicitly
+* included in the external representation so that it can be
+* re-read.
+* helpful
+* This flag provides a hint about whether a value whose "set"
+* flag is zero (above) should actually appear at all in the
+* external representaton.
+*
+* If the external representation allows values to be "commented
+* out" then, by default, values will be included in this form
+* only if their "helpful" flag is non-zero. Otherwise, they
+* will be omitted entirely. When possible, omitting the more
+* obscure values associated with a class is recommended in
+* order to improve readability.
+*
+* This default behaviour may be further modified if the
+* Channel's Full attribute is set - either to permit all values
+* to be shown, or to suppress non-essential information
+* entirely.
+* value
+* A Pointer to the Object to be written.
+* comment
+* Pointer to a constant null-terminated string containing a
+* textual comment to be associated with the value.
+*
+* Note that this comment may not actually be used, depending on
+* the nature of the Channel supplied and the setting of its
+* Comment attribute.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ AstXmlChan *this; /* A pointer to the XmlChan structure. */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) this_channel;
+
+/* If the object to be written is a component of a default AST object (i.e.
+ an object which is "not set"), then we do not write out the object. */
+ if( this->objectset ) {
+
+/* Use the "set" and "helpful" flags, along with the Channel's
+ attributes to decide whether this value should actually be
+ written. */
+ if ( Use( this, set, helpful, status ) ) {
+
+/* Save the supplied name associated with the object being written so
+ that it is available for use within the following invocation of the
+ WriteBegin method. The name is stored within the XmlChan structure
+ (NULL is used to indicate "no name supplied"). */
+ this->objectname = ( name && strlen( name ) ) ? name : NULL;
+
+/* Also save the supplied comment and a flag indicating if the object is
+ set. These will be used by the WriteBegin method. They are stored within
+ the XmlChan structure. */
+ this->objectset = set;
+ this->objectcomment = comment;
+
+/* Write the object to the XmlChan. */
+ (void) astWrite( this, value );
+
+/* Nullify the components of the XmlChan set above. */
+ this->objectname = NULL;
+ this->objectset = 1;
+ this->objectcomment = NULL;
+
+/* Initialise a flag to indicate that the next "IsA" item should be
+ written. */
+ this->write_isa = 1;
+ }
+ }
+
+/* If an error has occurred, annul the container element in the XmlChan. */
+ if( !astOK ) this->container = astXmlAnnulTree( this->container );
+
+}
+
+static void WriteString( AstChannel *this_channel, const char *name, int set,
+ int helpful, const char *value, const char *comment, int *status ){
+/*
+* Name:
+* WriteString
+
+* Purpose:
+* Write a string value to a data sink.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* void WriteString( AstChannel *this, const char *name, int set, int helpful,
+* const char *value, const char *comment, int *status )
+
+* Class Membership:
+* XmlChan member function (over-rides the protected
+* astWriteString method inherited from the Channel class).
+
+* Description:
+* This function writes a named string value, representing the
+* value of a class instance variable, to the data sink associated
+* with a Channel. It is intended for use by class "Dump" functions
+* when writing out class information which will subsequently be
+* re-read.
+
+* Parameters:
+* this
+* Pointer to the Channel.
+* name
+* Pointer to a constant null-terminated string containing the
+* name to be used to identify the value in the external
+* representation. This will form the key for identifying it
+* again when it is re-read. The name supplied should be unique
+* within its class.
+*
+* Mixed case may be used and will be preserved in the external
+* representation (where possible) for cosmetic effect. However,
+* case is not significant when re-reading values.
+*
+* It is recommended that a maximum of 6 alphanumeric characters
+* (starting with an alphabetic character) be used. This permits
+* maximum flexibility in adapting to standard external data
+* representations (e.g. FITS).
+* set
+* If this is zero, it indicates that the value being written is
+* a default value (or can be re-generated from other values) so
+* need not necessarily be written out. Such values will
+* typically be included in the external representation with
+* (e.g.) a comment character so that they are available to
+* human readers but will be ignored when re-read. They may also
+* be completely omitted in some circumstances.
+*
+* If "set" is non-zero, the value will always be explicitly
+* included in the external representation so that it can be
+* re-read.
+* helpful
+* This flag provides a hint about whether a value whose "set"
+* flag is zero (above) should actually appear at all in the
+* external representaton.
+*
+* If the external representation allows values to be "commented
+* out" then, by default, values will be included in this form
+* only if their "helpful" flag is non-zero. Otherwise, they
+* will be omitted entirely. When possible, omitting the more
+* obscure values associated with a class is recommended in
+* order to improve readability.
+*
+* This default behaviour may be further modified if the
+* Channel's Full attribute is set - either to permit all values
+* to be shown, or to suppress non-essential information
+* entirely.
+* value
+* Pointer to a constant null-terminated string containing the
+* value to be written.
+* comment
+* Pointer to a constant null-terminated string containing a
+* textual comment to be associated with the value.
+*
+* Note that this comment may not actually be used, depending on
+* the nature of the Channel supplied and the setting of its
+* Comment attribute.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ AstXmlChan *this; /* A pointer to the XmlChan structure. */
+ AstXmlElement *elem; /* Pointer to new element */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) this_channel;
+
+/* If the object to be written is a component of a default AST object (i.e.
+ an object which is "not set"), then we do not write out this item. */
+ if( this->objectset ) {
+
+/* Use the "set" and "helpful" flags, along with the Channel's
+ attributes to decide whether this value should actually be
+ written. */
+ if( Use( this, set, helpful, status ) ) {
+
+/* Create a new XmlElement with a name of ATTR (and no namespace
+ prefix), and add it into the current container (i.e. parent) element. */
+ elem = astXmlAddElement( this->container, ATTR,
+ astGetXmlPrefix( this ) );
+
+/* Add a NAME attribute to this element containing the item name. */
+ astXmlAddAttr( elem, NAME, name, NULL );
+
+/* If we are using QUOTED format, add an attribute to indicate that this is a
+ string value (mainly included for compatibility with JNIAST). */
+ if( astGetXmlFormat( this ) == QUOTED_FORMAT ) {
+ astXmlAddAttr( elem, QUOTED, TRUE, NULL );
+ }
+
+/* Add it the value to the element as the VALUE attribute. */
+ astXmlAddAttr( elem, VALUE, value, NULL );
+
+/* If we are adding comments, and if a comment was supplied as a
+ parameter to this function, then store the commment as an attribute of
+ the element. */
+ if( comment && *comment && astGetComment( this_channel ) ) {
+ astXmlAddAttr( elem, DESC, comment, NULL );
+ }
+
+/* If the object has all default values, store a true value for the
+ DEFAULT attribute. */
+ if( !set ) astXmlAddAttr( elem, DEFAULT, TRUE, NULL );
+
+/* Initialise a flag to indicate that the next "IsA" item should be
+ written. */
+ this->write_isa = 1;
+ }
+ }
+
+/* If an error has occurred, annul the container element in the XmlChan. */
+ if( !astOK ) this->container = astXmlAnnulTree( this->container );
+
+}
+
+
+/* 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). */
+
+/* XmlFormat */
+/* ========= */
+/*
+*att++
+* Name:
+* XmlFormat
+
+* Purpose:
+* System for formatting Objects as XML.
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* String.
+
+* Description:
+* This attribute specifies the formatting system to use when AST
+* Objects are written out as XML through an XmlChan. It
+c affects the behaviour of the astWrite function when
+f affects the behaviour of the AST_WRITE routine when
+* they are used to transfer any AST Object to or from an external
+* XML representation.
+*
+* The XmlChan class allows AST objects to be represented in the form
+* of XML in several ways (conventions) and the XmlFormat attribute is
+* used to specify which of these should be used. The formatting options
+* available are outlined in the "Formats Available" section below.
+*
+* By default, an XmlChan will attempt to determine which format system
+* is already in use, and will set the default XmlFormat value
+* accordingly (so that subsequent I/O operations adopt the same
+* conventions). It does this by looking for certain critical items
+* which only occur in particular formats. For details of how this
+* works, see the "Choice of Default Format" section below. If you wish
+* to ensure that a particular format system is used, independently of
+* any XML already read, you should set an explicit XmlFormat value
+* yourself.
+*
+* Formats Available:
+* The XmlFormat attribute can take any of the following (case
+* insensitive) string values to select the corresponding formatting
+* system:
+*
+* - "NATIVE": This is a direct conversion to XML of the heirarchical
+* format used by a standard XML channel (and also by the NATIVE
+* encoding of a FitsChan).
+*
+* - "QUOTED": This is the same as NATIVE format except that extra
+* information is included which allows client code to convert the
+* XML into a form which can be read by a standard AST Channel. This
+* extra information indicates which AST attribute values should be
+* enclosed in quotes before being passed to a Channel.
+*
+* - "IVOA": This is a format that uses an early draft of the STC-X schema
+* developed by the International Virtual Observatory Alliance (IVOA -
+* see "http://www.ivoa.net/") to describe coordinate systems, regions,
+* mappings, etc. Support is limited to V1.20 described at
+* "http://www.ivoa.net/Documents/WD/STC/STC-20050225.html". Since the
+* version of STC-X finally adopted by the IVOA differs in several
+* significant respects from V1.20, this format is now mainly of
+* historical interest. Note, the alternative "STC-S" format (a
+* simpler non-XML encoding of the STC metadata) is supported by the
+* StcsChan class.
+
+* Choice of Default Format;
+* If the XmlFormat attribute of an XmlChan is not set, the default
+* value it takes is determined by the presence of certain critical
+* items within the document most recently read using
+c astRead.
+f AST_READ.
+* The sequence of decision used to arrive at the default value is as
+* follows:
+*
+* - If the previous document read contained any elements in any of the STC
+* namespaces ("urn:nvo-stc", "urn:nvo-coords" or "urn:nvo-region"), then
+* the default value is IVOA.
+* - If the previous document read contained any elements in the AST
+* namespace which had an associated XML attribute called "quoted", then
+* the default value is QUOTED.
+* - Otherwise, if none of these conditions is met (as would be the
+* case if no document had yet been read), then NATIVE format is
+* used.
+*
+* Setting an explicit value for the XmlFormat attribute always
+* over-rides this default behaviour.
+
+* The IVOA Format:
+* The IVOA support caters only for certain parts of V1.20 of the
+* draft Space-Time Coordinate (STC) schema (see
+* http://www.ivoa.net/Documents/WD/STC/STC-20050225.html). Note, this
+* draft has now been superceded by an officially adopted version that
+* differs in several significant respects from V1.20. Consequently,
+* the "IVOA" XmlChan format is of historical interest only.
+*
+* The following points should be noted when using an XmlChan to read
+* or write STC information (note, this list is currently incomplete):
+*
+* - Objects can currently only be read using this format, not written.
+* - The AST object generated by reading an <STCMetadata> element will
+* be an instance of one of the AST "Stc" classes: StcResourceProfile,
+* StcSearchLocation, StcCatalogEntryLocation, StcObsDataLocation.
+* - When reading an <STCMetadata> element, the axes in the returned
+* AST Object will be in the order space, time, spectral, redshift,
+* irrespective of the order in which the axes occur in the <STCMetadata>
+* element. If the supplied <STCMetadata> element does not contain all of
+* these axes, the returned AST Object will also omit them, but the
+* ordering of those axes which are present will be as stated above. If
+* the spatial frame represents a celestial coordinate system the
+* spatial axes will be in the order (longitude, latitude).
+* - Until such time as the AST TimeFrame is complete, a simple
+* 1-dimensional Frame (with Domain set to TIME) will be used to
+* represent the STC <TimeFrame> element. Consequently, most of the
+* information within a <TimeFrame> element is currently ignored.
+* - <SpaceFrame> elements can only be read if they describe a celestial
+* longitude and latitude axes supported by the AST SkyFrame class. The
+* space axes will be returned in the order (longitude, latitude).
+* - Velocities associated with SpaceFrames cannot be read.
+* - Any <GenericCoordFrame> elements within an <AstroCoordSystem> element
+* are currently ignored.
+* - Any second or subsequent <AstroCoordSystem> found within an
+* STCMetaData element is ignored.
+* - Any second or subsequent <AstroCoordArea> found within an
+* STCMetaData element is ignored.
+* - Any <OffsetCenter> found within a <SpaceFrame> is ignored.
+* - Any CoordFlavor element found within a <SpaceFrame> is ignored.
+* - <SpaceFrame> elements can only be read if they refer to
+* one of the following space reference frames: ICRS, GALACTIC_II,
+* SUPER_GALACTIC, HEE, FK4, FK5, ECLIPTIC.
+* - <SpaceFrame> elements can only be read if the reference
+* position is TOPOCENTER. Also, any planetary ephemeris is ignored.
+* - Regions: there is currently no support for STC regions of type
+* Sector, ConvexHull or SkyIndex.
+* - The AST Region read from a CoordInterval element is considered to
+* be open if either the lo_include or the hi_include attribute is
+* set to false.
+* - <RegionFile> elements are not supported.
+* - Vertices within <Polygon> elements are always considered to be
+* joined using great circles (that is, <SmallCircle> elements are
+* ignored).
+
+* Applicability:
+* XmlChan
+* All XmlChans have this attribute.
+*att--
+*/
+astMAKE_CLEAR(XmlChan,XmlFormat,xmlformat,UNKNOWN_FORMAT)
+astMAKE_SET(XmlChan,XmlFormat,int,xmlformat,(
+ value == NATIVE_FORMAT ||
+ value == IVOA_FORMAT ||
+ value == QUOTED_FORMAT ? value :
+ (astError( AST__BADAT, "astSetXmlFormat: Unknown XML formatting system %d "
+ "supplied.", status, value ), UNKNOWN_FORMAT )))
+astMAKE_TEST(XmlChan,XmlFormat,( this->xmlformat != UNKNOWN_FORMAT ))
+astMAKE_GET(XmlChan,XmlFormat,int,0,(this->xmlformat == UNKNOWN_FORMAT ?
+ this->formatdef : this->xmlformat))
+
+/*
+*att++
+* Name:
+* XmlLength
+
+* Purpose:
+* Controls output buffer 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 XmlChan was created.
+*
+* The number of characters in each string written out through the sink
+* function will not be greater than the value of this attribute (but
+* may be less). A value of zero (the default) means there is no limit -
+* each string can be of any length.
+*
+f Note, the default value of zero is unlikely to be appropriate when
+f an XmlChan is used within Fortran code. In this case, XmlLength
+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 This avoids the possibility of long lines being truncated invisibly
+f within AST_GETLINE.
+
+* Applicability:
+* XmlChan
+* All XmlChans have this attribute.
+*att--
+*/
+astMAKE_CLEAR(XmlChan,XmlLength,xmllength,-INT_MAX)
+astMAKE_GET(XmlChan,XmlLength,int,0,( ( this->xmllength != -INT_MAX ) ? this->xmllength : 0 ))
+astMAKE_SET(XmlChan,XmlLength,int,xmllength,(value<0?0:value))
+astMAKE_TEST(XmlChan,XmlLength,( this->xmllength != -INT_MAX ))
+
+/*
+*att++
+* Name:
+* XmlPrefix
+
+* Purpose:
+* The namespace prefix to use when writing.
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* String.
+
+* Description:
+* This attribute is a string which is to be used as the namespace
+* prefix for all XML elements created as a result of writing an AST
+* Object out through an XmlChan. The URI associated with the namespace
+* prefix is given by the symbolic constant AST__XMLNS defined in
+f AST_PAR.
+c ast.h.
+* A definition of the namespace prefix is included in each top-level
+* element produced by the XmlChan.
+*
+* The default value is a blank string which causes no prefix to be
+* used. In this case each top-level element will set the default
+* namespace to be the value of AST__XMLNS.
+
+* Applicability:
+* Object
+* All Objects have this attribute.
+
+*att--
+*/
+astMAKE_CLEAR(XmlChan,XmlPrefix,xmlprefix,astFree( this->xmlprefix ))
+astMAKE_GET(XmlChan,XmlPrefix,const char *,NULL,( this->xmlprefix ? this->xmlprefix : "" ))
+astMAKE_SET(XmlChan,XmlPrefix,const char *,xmlprefix,astStore( this->xmlprefix, value,
+ strlen( value ) + (size_t) 1 ))
+astMAKE_TEST(XmlChan,XmlPrefix,( this->xmlprefix != NULL ))
+
+
+/* Copy constructor. */
+/* ----------------- */
+static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
+/*
+* Name:
+* Copy
+
+* Purpose:
+* Copy constructor for XmlChan objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* void Copy( const AstObject *objin, AstObject *objout, int *status )
+
+* Description:
+* This function implements the copy constructor for XmlChan objects.
+
+* Parameters:
+* objin
+* Pointer to the object to be copied.
+* objout
+* Pointer to the object being constructed.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - This constructor makes a deep copy.
+*/
+
+/* Local Variables: */
+ AstXmlChan *in; /* Pointer to input XmlChan */
+ AstXmlChan *out; /* Pointer to output XmlChan */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain pointers to the input and output XmlChans. */
+ in = (AstXmlChan *) objin;
+ out = (AstXmlChan *) objout;
+
+/* Clear the non-persistent values in the new XmlChan. */
+ out->objectname = NULL; /* Name of object being written */
+ out->objectset = 1; /* Is object being written set?*/
+ out->objectcomment = NULL;/* Comment for object class being written */
+ out->readcontext = NULL; /* XmlElement giving context for current read */
+ out->container = NULL; /* XmlElement to which content will be added */
+ out->write_isa = 0; /* Write out the next "IsA" item? */
+ out->reset_source = 1; /* A new line should be read from the source */
+ out->isa_class = NULL; /* Class being loaded */
+
+/* Store a copy of the prefix string.*/
+ if ( in->xmlprefix ) out->xmlprefix = astStore( NULL, in->xmlprefix,
+ strlen( in->xmlprefix ) + (size_t) 1 );
+}
+
+
+/* Destructor. */
+/* ----------- */
+static void Delete( AstObject *obj, int *status ) {
+/*
+* Name:
+* Delete
+
+* Purpose:
+* Destructor for XmlChan objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* void Delete( AstObject *obj, int *status )
+
+* Description:
+* This function implements the destructor for XmlChan objects.
+
+* Parameters:
+* obj
+* Pointer to the object to be deleted.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* This function attempts to execute even if the global error status is
+* set.
+*/
+
+/* Local Variables: */
+ AstXmlChan *this; /* Pointer to XmlChan */
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) obj;
+
+/* Free any unread part of the document. */
+ this->readcontext = astXmlAnnul( this->readcontext );
+
+/* Free the memory used for the XmlPrefix string if necessary. */
+ this->xmlprefix = astFree( this->xmlprefix );
+
+/* Free any memory used to store text read from the source */
+ GetNextChar( NULL, status );
+
+}
+
+/* Dump function. */
+/* -------------- */
+static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
+/*
+* Name:
+* Dump
+
+* Purpose:
+* Dump function for XmlChan 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 XmlChan class to an output Channel.
+
+* Parameters:
+* this
+* Pointer to the XmlChan 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: */
+ AstXmlChan *this; /* Pointer to the XmlChan structure */
+ const char *sval; /* String attribute value */
+ int ival; /* Integer attribute value */
+ int set; /* Has the attribute got a set value? */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the XmlChan structure. */
+ this = (AstXmlChan *) this_object;
+
+/* Write out values representing the instance variables for the
+ XmlChan class. Accompany these with appropriate comment strings,
+ possibly depending on the values being written.*/
+
+
+/* Now do instance variables which are not attributes. */
+/* =================================================== */
+
+/* XmlLength */
+/* --------- */
+ set = TestXmlLength( this, status );
+ ival = set ? GetXmlLength( this, status ) : astGetXmlLength( this );
+ astWriteInt( channel, "XmlLen", set, 0, ival, "XML buffer length" );
+
+/* XmlFormat. */
+/* --------- */
+ set = TestXmlFormat( this, status );
+ ival = set ? GetXmlFormat( this, status ) : astGetXmlFormat( this );
+ if( ival > UNKNOWN_FORMAT && ival <= MAX_FORMAT ) {
+ astWriteString( channel, "XmlFmt", set, 1, xformat[ival], "Formatting system" );
+ } else {
+ astWriteString( channel, "XmlFmt", set, 1, UNKNOWN_STRING, "Formatting system" );
+ }
+
+/* XmlPrefix */
+/* --------- */
+ set = TestXmlPrefix( this, status );
+ sval = set ? GetXmlPrefix( this, status ) : astGetXmlPrefix( this );
+ astWriteString( channel, "XmlPrf", set, 1, sval,
+ "Namespace prefix" );
+}
+
+
+/* Standard class functions. */
+/* ========================= */
+/* Implement the astIsAXmlChan and astCheckXmlChan functions using the macros
+ defined for this purpose in the "object.h" header file. */
+astMAKE_ISA(XmlChan,Channel)
+astMAKE_CHECK(XmlChan)
+
+AstXmlChan *astXmlChan_( const char *(* source)( void ),
+ void (* sink)( const char * ),
+ const char *options, int *status, ...) {
+/*
+*++
+* Name:
+c astXmlChan
+f AST_XMLCHAN
+
+* Purpose:
+* Create an XmlChan.
+
+* Type:
+* Public function.
+
+* Synopsis:
+c #include "xmlchan.h"
+c AstXmlChan *astXmlChan( const char *(* source)( void ),
+c void (* sink)( const char * ),
+c const char *options, ... )
+f RESULT = AST_XMLCHAN( SOURCE, SINK, OPTIONS, STATUS )
+
+* Class Membership:
+* XmlChan constructor.
+
+* Description:
+* This function creates a new XmlChan and optionally initialises
+* its attributes.
+*
+* A XmlChan is a specialised form of Channel which supports XML I/O
+* operations. Writing an Object to an XmlChan (using
+c astWrite) will, if the Object is suitable, generate an
+f AST_WRITE) will, if the Object is suitable, generate an
+* XML description of that Object, and reading from an XmlChan will
+* create a new Object from its XML description.
+*
+* Normally, when you use an XmlChan, you should provide "source"
+c and "sink" functions which connect it to an external data store
+f and "sink" routines which connect it to an external data store
+* by reading and writing the resulting XML text. By default, however,
+* an XmlChan 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 XmlChan 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 XmlChan 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 XmlChan 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 XmlChan 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 XmlChan 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 XmlChan 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 XmlChan 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 XmlChan 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 XmlChan. 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 XmlChan. 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 astXmlChan()
+f AST_XMLCHAN = INTEGER
+* A pointer to the new XmlChan.
+
+* 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_XMLCHAN. 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 */
+ AstXmlChan *new; /* Pointer to new XmlChan */
+ 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 XmlChan, 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 = astInitXmlChan( NULL, sizeof( AstXmlChan ), !class_init,
+ &class_vtab, "XmlChan", 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
+ XmlChan'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 XmlChan. */
+ return new;
+}
+
+AstXmlChan *astXmlChanId_( const char *(* source)( void ),
+ void (* sink)( const char * ),
+ const char *options, ... ) {
+/*
+* Name:
+* astXmlChanId_
+
+* Purpose:
+* Create an XmlChan.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstXmlChan *astXmlChanId_( const char *(* source)( void ),
+* void (* sink)( const char * ),
+* const char *options, ... )
+
+* Class Membership:
+* XmlChan constructor.
+
+* Description:
+* This function implements the external (public) C interface to the
+* astXmlChan constructor function. Another function (astXmlChanForId)
+* should be called to create an XmlChan 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 astXmlChan_ 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 astXmlChan_ 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 astXmlChan_.
+
+* Returned Value:
+* The ID value associated with the new XmlChan.
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstXmlChan *new; /* Pointer to new XmlChan */
+ 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 XmlChan, 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 = astInitXmlChan( NULL, sizeof( AstXmlChan ), !class_init,
+ &class_vtab, "XmlChan", 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
+ XmlChan'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 XmlChan. */
+ return astMakeId( new );
+}
+
+AstXmlChan *astXmlChanForId_( 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:
+* astXmlChanFor
+
+* Purpose:
+* Initialise an XmlChan from a foreign language interface.
+
+* Type:
+* Public function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstXmlChan *astXmlChanFor( 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:
+* XmlChan constructor.
+
+* Description:
+* This function creates a new XmlChan from a foreign language
+* interface and optionally initialises its attributes.
+*
+* A XmlChan implements low-level XML input/output for the AST library.
+* Writing an Object to an XmlChan (using astWrite) will generate a
+* XML representation of that Object, and reading from a
+* XmlChan (using astRead) will create a new Object from its
+* XML representation.
+*
+* Normally, when you use an XmlChan, you should provide "source"
+* and "sink" functions which connect it to an external data store
+* by reading and writing the resulting text. This function also
+* requires you to provide "wrapper" functions which will invoke
+* the source and sink functions. By default, however, an XmlChan
+* 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 XmlChan 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 XmlChan 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 XmlChan 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 XmlChan. 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:
+* astXmlChanFor()
+* A pointer to the new XmlChan.
+
+* 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 XmlChan 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 astXmlChanId_, 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 */
+ AstXmlChan *new; /* Pointer to new XmlChan */
+ 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 XmlChan, allocating memory and initialising the
+ virtual function table as well if necessary. */
+ new = astInitXmlChan( NULL, sizeof( AstXmlChan ), !class_init,
+ &class_vtab, "XmlChan", 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
+ XmlChan'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 XmlChan. */
+ return astMakeId( new );
+}
+
+AstXmlChan *astInitXmlChan_( void *mem, size_t size, int init,
+ AstXmlChanVtab *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:
+* astInitXmlChan
+
+* Purpose:
+* Initialise an XmlChan.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstXmlChan *astInitXmlChan( void *mem, size_t size, int init,
+* AstXmlChanVtab *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:
+* XmlChan initialiser.
+
+* Description:
+* This function is provided for use by class implementations to
+* initialise a new XmlChan object. It allocates memory (if
+* necessary) to accommodate the XmlChan plus any additional data
+* associated with the derived class. It then initialises a
+* XmlChan 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 XmlChan at the start of the memory passed
+* via the "vtab" parameter.
+
+* Parameters:
+* mem
+* A pointer to the memory in which the XmlChan is to be
+* initialised. This must be of sufficient size to accommodate
+* the XmlChan data (sizeof(XmlChan)) 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 XmlChan (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 XmlChan structure, so a valid value must be
+* supplied even if not required for allocating memory.
+* init
+* A boolean flag indicating if the XmlChan'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 XmlChan.
+* 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 XmlChan 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 XmlChan.
+
+* 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: */
+ AstXmlChan *new; /* Pointer to new XmlChan */
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* If necessary, initialise the virtual function table. */
+ if ( init ) astInitXmlChanVtab( vtab, name );
+
+/* Initialise a Channel structure (the parent class) as the first
+ component within the XmlChan structure, allocating memory if
+ necessary. */
+ new = (AstXmlChan *) astInitChannel( mem, size, 0,
+ (AstChannelVtab *) vtab, name,
+ source, source_wrap, sink,
+ sink_wrap );
+
+ if ( astOK ) {
+
+/* Initialise the XmlChan data. */
+/* ---------------------------- */
+ new->objectname = NULL; /* Name of object being written */
+ new->objectset = 1; /* Is object being written set?*/
+ new->objectcomment = NULL;/* Comment for object class being written */
+ new->container = NULL; /* XmlElement to which content will be added */
+ new->readcontext = NULL; /* XmlElement giving context for current read */
+ new->write_isa = 0; /* Write out the next "IsA" item? */
+ new->xmllength = -INT_MAX;/* Buffer length */
+ new->xmlprefix = NULL; /* Xml prefix */
+ new->xmlformat = UNKNOWN_FORMAT; /* Xml format */
+ new->formatdef = NATIVE_FORMAT; /* Default Xml format */
+ new->reset_source = 1; /* A new line should be read from the source */
+ new->isa_class = NULL; /* Class being loaded */
+
+/* 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;
+}
+
+AstXmlChan *astLoadXmlChan_( void *mem, size_t size,
+ AstXmlChanVtab *vtab, const char *name,
+ AstChannel *channel, int *status ) {
+/*
+*+
+* Name:
+* astLoadXmlChan
+
+* Purpose:
+* Load an XmlChan.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "xmlchan.h"
+* AstXmlChan *astLoadXmlChan( void *mem, size_t size,
+* AstXmlChanVtab *vtab, const char *name,
+* AstChannel *channel )
+
+* Class Membership:
+* XmlChan loader.
+
+* Description:
+* This function is provided to load a new XmlChan 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
+* XmlChan 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 XmlChan at the start of the memory
+* passed via the "vtab" parameter.
+
+
+* Parameters:
+* mem
+* A pointer to the memory into which the XmlChan is to be
+* loaded. This must be of sufficient size to accommodate the
+* XmlChan data (sizeof(XmlChan)) 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 XmlChan (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 XmlChan 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(AstXmlChan) is used instead.
+* vtab
+* Pointer to the start of the virtual function table to be
+* associated with the new XmlChan. If this is NULL, a pointer
+* to the (static) virtual function table for the XmlChan 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 "XmlChan" is used instead.
+
+* Returned Value:
+* A pointer to the new XmlChan.
+
+* 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 */
+ AstXmlChan *new; /* Pointer to the new XmlChan */
+ char *text; /* Textual version of integer value */
+
+/* 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 XmlChan. In this case the
+ XmlChan belongs to this class, so supply appropriate values to be
+ passed to the parent class loader (and its parent, etc.). */
+ if ( !vtab ) {
+ size = sizeof( AstXmlChan );
+ vtab = &class_vtab;
+ name = "XmlChan";
+
+/* If required, initialise the virtual function table for this class. */
+ if ( !class_init ) {
+ astInitXmlChanVtab( 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 XmlChan. */
+ 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, "XmlChan" );
+
+/* Now read each individual data item from this list and use it to
+ initialise the appropriate instance variable(s) for this class. */
+
+/* Ensure other items in the XmlChan structure are initialised properly. */
+ new->objectname = NULL; /* Name of object being written */
+ new->objectset = 1; /* Is object being written set?*/
+ new->objectcomment = NULL;/* Comment for object class being written */
+ new->container = NULL; /* XmlElement to which content will be added */
+ new->readcontext = NULL; /* XmlElement giving context for current read */
+ new->write_isa = 0; /* Write out the next "IsA" item? */
+ new->xmllength = -INT_MAX;/* Buffer length */
+ new->xmlprefix = NULL; /* Xml prefix */
+ new->reset_source = 1; /* A new line should be read from the source */
+ new->isa_class = NULL; /* Class being loaded */
+ new->formatdef = NATIVE_FORMAT; /* Default Xml format */
+
+/* Now restore presistent values. */
+
+/* XmlLength */
+/* --------- */
+ new->xmllength = astReadInt( channel, "xmllen", -INT_MAX );
+
+/* XmlPrefix */
+/* --------- */
+ new->xmlprefix = astReadString( channel, "xmlprf", NULL );
+
+/* XmlFormat. */
+/* --------- */
+ text = astReadString( channel, "xmlfmt", UNKNOWN_STRING );
+ if( strcmp( text, UNKNOWN_STRING ) ) {
+ new->xmlformat = FindString( MAX_FORMAT + 1, xformat, text,
+ "the XmlChan component 'XmlFmt'",
+ "astRead", astGetClass( channel ), status );
+ } else {
+ new->xmlformat = UNKNOWN_FORMAT;
+ }
+ if ( TestXmlFormat( new, status ) ) SetXmlFormat( new, new->xmlformat, status );
+ text = astFree( text );
+ }
+
+/* If an error occurred, clean up by deleting the new XmlChan. */
+ if ( !astOK ) new = astDelete( new );
+
+/* Return the new XmlChan 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. */
+
+
+
+
+
+
+
+
+
+
+
+
+