ast-8.0.7/ 0000775 0001750 0001750 00000000000 12610415260 007315 5 0000000 0000000 ast-8.0.7/plot.h 0000664 0001750 0001750 00000153176 12610415012 010374 0000000 0000000 #if !defined( PLOT_INCLUDED ) /* Include this file only once */
#define PLOT_INCLUDED
/*
*+
* Name:
* plot.h
* Type:
* C include file.
* Purpose:
* Define the interface to the Plot class.
* Invocation:
* #include "plot.h"
* Description:
* This include file defines the interface to the Plot class and
* provides the type definitions, function prototypes and macros, etc.
* needed to use this class.
*
* The Plot class provides facilities for producing graphical information
* describing positions within coordinate systems. These include the
* creation of annotated coordinate axes, the plotting of markers at given
* physical positions, etc.
* Inheritance:
* The Plot class inherits from the FrameSet class.
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* 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
* .
* Authors:
* DSB: D.S. Berry (Starlink)
* History:
* 18-SEP-1996 (DSB):
* Original version.
* 28-OCT-1998 (DSB):
* Added method astPolyCurve.
* 12-OCT-1999 (DSB):
* Allow tick marks to be specified separately for both axes.
* 9-JAN-2001 (DSB):
* Change argument "in" for astMark and astPolyCurve from type
* "const double (*)[]" to "const double *".
* 13-JUN-2001 (DSB):
* Added methods astGenCurve, astGrfSet, astGrfPop, astGrfPush and
* attribute Grf.
* 8-JAN-2003 (DSB):
* Added protected astInitPlotVtab method.
* 13-JAN-2004 (DSB):
* Added bbox, logticks and logplot to the AstPlot structure. Added
* LogPlot and LogTicks accessor methods.
* 19-JAN-2004 (DSB):
* Added loggap and loglabel to the AstPlot structure. Added
* LogGap and LogLabel accessor methods.
* 21-MAR-2005 (DSB):
* - Added the Clip attribute.
* 24-OCT-2006 (DSB):
* - Remove duplicated documentation from prologue.
* - Add ForceExterior attribute.
*-
*/
/* Include files. */
/* ============== */
/* Interface definitions. */
/* ---------------------- */
#include "frameset.h" /* Parent FrameSet class */
#include "keymap.h"
#include "region.h"
#if defined(astCLASS) /* Protected */
#include "grf.h"
#endif
/* C header files. */
/* --------------- */
#include
/* Macros. */
/* ======= */
#if defined(astCLASS) || defined(astFORTRAN77)
#define STATUS_PTR status
#else
#define STATUS_PTR astGetStatusPtr
#endif
#define AST__NPID 20 /* No. of different genuine plot object id's */
#define AST__GATTR 0 /* Identifiers for GRF functions */
#define AST__GFLUSH 1 /* Note, if any items are added or changed here, */
#define AST__GLINE 2 /* make sure that the astGrfFunID function is */
#define AST__GMARK 3 /* updated in plot.c */
#define AST__GTEXT 4
#define AST__GTXEXT 5
#define AST__GSCALES 6
#define AST__GQCH 7
#define AST__GCAP 8
#define AST__GBBUF 9
#define AST__GEBUF 10
#define AST__NGRFFUN 11 /* No. of Grf functions used by Plot */
#if defined(astCLASS) /* Protected */
#define AST__MXBRK 100 /* Max. no. of breaks in a drawn curve */
/* Identifiers for the graphical elements of an annotated coord grid.
"Pseudo-elements" (i.e. values used to indicate a group of other
genuine elements) should come at the end of the list. The number of
genuine elements should be stored in AST__NPID. */
#define AST__BORDER_ID 0 /* Id for astBorder curves */
#define AST__CURVE_ID 1 /* Id for astCurve, astGenCurve or astPolyCurve curves */
#define AST__TITLE_ID 2 /* Id for textual title */
#define AST__MARKS_ID 3 /* Id for marks drawn by astMark */
#define AST__TEXT_ID 4 /* Id for text strings drawn by astText */
#define AST__AXIS1_ID 5 /* Id for axis 1 through interior tick marks */
#define AST__AXIS2_ID 6 /* Id for axis 2 through interior tick marks */
#define AST__AXIS3_ID 7 /* Id for axis 2 through interior tick marks */
#define AST__NUMLAB1_ID 8 /* Id for numerical labels */
#define AST__NUMLAB2_ID 9 /* Id for numerical labels */
#define AST__NUMLAB3_ID 10 /* Id for numerical labels */
#define AST__TEXTLAB1_ID 11 /* Id for textual axis labels */
#define AST__TEXTLAB2_ID 12 /* Id for textual axis labels */
#define AST__TEXTLAB3_ID 13 /* Id for textual axis labels */
#define AST__TICKS1_ID 14 /* Id for major and minor tick marks */
#define AST__TICKS2_ID 15 /* Id for major and minor tick marks */
#define AST__TICKS3_ID 16 /* Id for major and minor tick marks */
#define AST__GRIDLINE1_ID 17 /* Id for axis 1 astGridLine AST__curves */
#define AST__GRIDLINE2_ID 18 /* Id for axis 2 astGridLine AST__curves */
#define AST__GRIDLINE3_ID 19 /* Id for axis 2 astGridLine AST__curves */
#define AST__AXES_ID 20 /* Id for axes through interior tick marks */
#define AST__NUMLABS_ID 21 /* Id for numerical labels */
#define AST__TEXTLABS_ID 22 /* Id for textual axis labels */
#define AST__GRIDLINE_ID 23 /* Id for astGridLine AST__curves */
#define AST__TICKS_ID 24 /* Id for major and minor tick marks */
/* Define constants used to size global arrays in this module. */
#define AST__PLOT_CRV_MXBRK 1000 /* Max. no. of breaks allowed in a plotted curve */
#define AST__PLOT_STRIPESCAPES_BUFF_LEN 50 /* Length of string returned by astStripEscapes */
#endif
/* Define a dummy __attribute__ macro for use on non-GNU compilers. */
#ifndef __GNUC__
# define __attribute__(x) /*NOTHING*/
#endif
/* Type Definitions */
/* ================ */
/* Pre-declare the AstPlot structure so that it can be used within the
GRF function typedefs. */
struct AstPlot;
/* Interfaces for GRF functions */
/* ---------------------------- */
/* A general interface into which actual Grf functions should be cast
before being passed as an argument to astGrfSet. */
typedef void (* AstGrfFun)( void );
/* Interfaces for specific Grf funstions implemented in C (other languages
may have different interfaces). */
typedef int (* AstGAttrFun)( AstKeyMap *, int, double, double *, int );
typedef int (* AstGFlushFun)( AstKeyMap * );
typedef int (* AstGBBufFun)( AstKeyMap * );
typedef int (* AstGEBufFun)( AstKeyMap * );
typedef int (* AstGLineFun)( AstKeyMap *, int, const float *, const float * );
typedef int (* AstGMarkFun)( AstKeyMap *, int, const float *, const float *, int );
typedef int (* AstGTextFun)( AstKeyMap *, const char *, float, float, const char *, float, float );
typedef int (* AstGCapFun)( AstKeyMap *, int, int );
typedef int (* AstGTxExtFun)( AstKeyMap *, const char *, float, float, const char *, float, float, float *, float * );
typedef int (* AstGScalesFun)( AstKeyMap *, float *, float * );
typedef int (* AstGQchFun)( AstKeyMap *, float *, float * );
/* A general interface into which Grf Wrapper functions should be cast
before being passed as an argument to astGrfWrapper. */
typedef void (* AstGrfWrap)( void );
/* Interfaces for the wrapper functions which wrap specific Grf funstions. */
typedef int (* AstGAttrWrap)( struct AstPlot *, int, double, double *, int, int * );
typedef int (* AstGBBufWrap)( struct AstPlot *, int * );
typedef int (* AstGEBufWrap)( struct AstPlot *, int * );
typedef int (* AstGFlushWrap)( struct AstPlot *, int * );
typedef int (* AstGLineWrap)( struct AstPlot *, int, const float *, const float *, int * );
typedef int (* AstGMarkWrap)( struct AstPlot *, int, const float *, const float *, int, int * );
typedef int (* AstGTextWrap)( struct AstPlot *, const char *, float, float, const char *, float, float, int * );
typedef int (* AstGCapWrap)( struct AstPlot *, int, int, int * );
typedef int (* AstGTxExtWrap)( struct AstPlot *, const char *, float, float, const char *, float, float, float *, float *, int * );
typedef int (* AstGScalesWrap)( struct AstPlot *, float *, float *, int * );
typedef int (* AstGQchWrap)( struct AstPlot *, float *, float *, int * );
/* A structure in which a collection of Grf function pointers can be
stored. */
typedef struct AstGrfPtrs {
AstGrfFun grffun[AST__NGRFFUN];
AstGAttrWrap GAttr;
AstGBBufWrap GBBuf;
AstGEBufWrap GEBuf;
AstGFlushWrap GFlush;
AstGLineWrap GLine;
AstGMarkWrap GMark;
AstGTextWrap GText;
AstGCapWrap GCap;
AstGTxExtWrap GTxExt;
AstGScalesWrap GScales;
AstGQchWrap GQch;
} AstGrfPtrs;
/* Structure holding current graphical attribute values for text. */
typedef struct AstGat {
float rise;
double size;
double width;
double col;
double font;
double style;
} AstGat;
/* Plot structure. */
/* ------------------- */
/* This structure contains all information that is unique to each object in
the class (e.g. its instance variables). */
typedef struct AstPlot {
/* Attributes inherited from the parent class. */
AstFrameSet parent; /* Parent class structure */
/* Attributes specific to objects in this class. */
double *clip_lbnd;
double *clip_ubnd;
double centre[ 3 ];
double gap[ 3 ];
double loggap[ 3 ];
double labelat[ 3 ];
double majticklen[ 3 ];
double minticklen[ 3 ];
double numlabgap[ 3 ];
double size[ AST__NPID ];
double textlabgap[ 3 ];
double titlegap;
double tol;
double ucentre[ 3 ];
double ugap[ 3 ];
double uloggap[ 3 ];
double ulblat[ 3 ];
double umjtkln[ 3 ];
double width[ AST__NPID ];
double *majtickgx[ 3 ];
double *majtickgy[ 3 ];
double *mintickgx[ 3 ];
double *mintickgy[ 3 ];
int majtickcount[ 3 ];
int mintickcount[ 3 ];
int nmajtickval[ 3 ];
double *majtickval[ 3 ];
int nmintickval[ 3 ];
double *mintickval[ 3 ];
double xhi;
double xlo;
double yhi;
double ylo;
double bbox[ 4 ];
int border;
int clip_axes;
int clip_frame;
int clip;
int clipop;
int colour[ AST__NPID ];
int drawaxes[ 3 ];
int abbrev[ 3 ];
int escape;
int drawtitle;
int edge[ 3 ];
int font[ AST__NPID ];
int grf;
int grid;
int invisible;
int labelling;
int labelunits[ 3 ];
int labelup[ 3 ];
int mintick[ 3 ];
int numlab[ 3 ];
int style[ AST__NPID ];
int textlab[ 3 ];
int tickall;
int forceexterior;
int uborder;
int uedge[ 3 ];
int ugrid;
int ulbling;
int ulbunit[ 3 ];
int ulgtk[ 3 ];
int ulglb[ 3 ];
int umintk[ 3 ];
int utxtlb[ 3 ];
int xrev;
int yrev;
int ink;
int logplot[ 3 ];
int logticks[ 3 ];
int loglabel[ 3 ];
AstGrfFun grffun[AST__NGRFFUN];
AstGAttrWrap GAttr;
AstGBBufWrap GBBuf;
AstGEBufWrap GEBuf;
AstGFlushWrap GFlush;
AstGLineWrap GLine;
AstGMarkWrap GMark;
AstGTextWrap GText;
AstGCapWrap GCap;
AstGTxExtWrap GTxExt;
AstGScalesWrap GScales;
AstGQchWrap GQch;
AstGrfPtrs *grfstack;
int grfnstack;
AstGat **gat;
int ngat;
AstKeyMap *grfcontext;
AstKeyMap *grfcontextID;
float hmarkx;
float hmarky;
} AstPlot;
/* Virtual function table. */
/* ----------------------- */
/* This table contains all information that is the same for all
objects in the class (e.g. pointers to its virtual functions). */
#if defined(astCLASS) /* Protected */
typedef struct AstPlotVtab {
/* Properties (e.g. methods) inherited from the parent class. */
AstFrameSetVtab FrameSet_vtab;/* Parent class virtual function table */
/* A Unique identifier to determine class membership. */
AstClassIdentifier id;
/* Properties (e.g. methods) specific to this class. */
AstKeyMap *(* GetGrfContext)( AstPlot *, int * );
AstPointSet *(* GetDrawnTicks)( AstPlot *, int, int, int * );
int (* Border)( AstPlot *, int * );
int (* CvBrk)( AstPlot *, int, double *, double *, double *, int * );
void (* BBuf)( AstPlot *, int * );
void (* BoundingBox)( AstPlot *, float[2], float[2], int * );
void (* Clip)( AstPlot *, int, const double [], const double [], int * );
void (* CopyPlotDefaults)( AstPlot *, int, AstPlot *, int, int * );
void (* Curve)( AstPlot *, const double [], const double [], int * );
void (* DrawExtraTicks)( AstPlot *, int, AstPointSet *, int * );
void (* EBuf)( AstPlot *, int * );
void (* GenCurve)( AstPlot *, AstMapping *, int * );
void (* GrfPop)( AstPlot *, int * );
void (* GrfPush)( AstPlot *, int * );
void (* GrfSet)( AstPlot *, const char *, AstGrfFun, int * );
void (* GrfWrapper)( AstPlot *, const char *, AstGrfWrap, int * );
void (* Grid)( AstPlot *, int * );
void (* GridLine)( AstPlot *, int, const double [], double, int * );
void (* Mark)( AstPlot *, int, int, int, const double *, int, int * );
void (* Mirror)( AstPlot *, int, int * );
void (* PolyCurve)( AstPlot *, int, int, int, const double *, int * );
void (* RegionOutline)( AstPlot *, AstRegion *, int * );
void (* SetTickValues)( AstPlot *, int, int, double *, int, double *, int * );
void (* Text)( AstPlot *, const char *, const double [], const float [], const char *, int * );
double (* GetTol)( AstPlot *, int * );
int (* TestTol)( AstPlot *, int * );
void (* SetTol)( AstPlot *, double, int * );
void (* ClearTol)( AstPlot *, int * );
int (* GetGrid)( AstPlot *, int * );
int (* TestGrid)( AstPlot *, int * );
void (* SetGrid)( AstPlot *, int, int * );
void (* ClearGrid)( AstPlot *, int * );
int (* GetTickAll)( AstPlot *, int * );
int (* TestTickAll)( AstPlot *, int * );
void (* SetTickAll)( AstPlot *, int, int * );
void (* ClearTickAll)( AstPlot *, int * );
int (* GetForceExterior)( AstPlot *, int * );
int (* TestForceExterior)( AstPlot *, int * );
void (* SetForceExterior)( AstPlot *, int, int * );
void (* ClearForceExterior)( AstPlot *, int * );
int (* GetInvisible)( AstPlot *, int * );
int (* TestInvisible)( AstPlot *, int * );
void (* SetInvisible)( AstPlot *, int, int * );
void (* ClearInvisible)( AstPlot *, int * );
int (* GetBorder)( AstPlot *, int * );
int (* TestBorder)( AstPlot *, int * );
void (* SetBorder)( AstPlot *, int, int * );
void (* ClearBorder)( AstPlot *, int * );
int (* GetClipOp)( AstPlot *, int * );
int (* TestClipOp)( AstPlot *, int * );
void (* SetClipOp)( AstPlot *, int, int * );
void (* ClearClipOp)( AstPlot *, int * );
int (* GetClip)( AstPlot *, int * );
int (* TestClip)( AstPlot *, int * );
void (* SetClip)( AstPlot *, int, int * );
void (* ClearClip)( AstPlot *, int * );
int (* GetGrf)( AstPlot *, int * );
int (* TestGrf)( AstPlot *, int * );
void (* SetGrf)( AstPlot *, int, int * );
void (* ClearGrf)( AstPlot *, int * );
int (* GetDrawTitle)( AstPlot *, int * );
int (* TestDrawTitle)( AstPlot *, int * );
void (* SetDrawTitle)( AstPlot *, int, int * );
void (* ClearDrawTitle)( AstPlot *, int * );
int (* GetLabelUp)( AstPlot *, int, int * );
int (* TestLabelUp)( AstPlot *, int, int * );
void (* SetLabelUp)( AstPlot *, int, int, int * );
void (* ClearLabelUp)( AstPlot *, int, int * );
int (* GetLogPlot)( AstPlot *, int, int * );
int (* TestLogPlot)( AstPlot *, int, int * );
void (* SetLogPlot)( AstPlot *, int, int, int * );
void (* ClearLogPlot)( AstPlot *, int, int * );
int (* GetLogTicks)( AstPlot *, int, int * );
int (* TestLogTicks)( AstPlot *, int, int * );
void (* SetLogTicks)( AstPlot *, int, int, int * );
void (* ClearLogTicks)( AstPlot *, int, int * );
int (* GetLogLabel)( AstPlot *, int, int * );
int (* TestLogLabel)( AstPlot *, int, int * );
void (* SetLogLabel)( AstPlot *, int, int, int * );
void (* ClearLogLabel)( AstPlot *, int, int * );
int (* GetDrawAxes)( AstPlot *, int, int * );
int (* TestDrawAxes)( AstPlot *, int, int * );
void (* SetDrawAxes)( AstPlot *, int, int, int * );
void (* ClearDrawAxes)( AstPlot *, int, int * );
int (* GetAbbrev)( AstPlot *, int, int * );
int (* TestAbbrev)( AstPlot *, int, int * );
void (* SetAbbrev)( AstPlot *, int, int, int * );
void (* ClearAbbrev)( AstPlot *, int, int * );
int (* GetEscape)( AstPlot *, int * );
int (* TestEscape)( AstPlot *, int * );
void (* SetEscape)( AstPlot *, int, int * );
void (* ClearEscape)( AstPlot *, int * );
int (* GetLabelling)( AstPlot *, int * );
int (* TestLabelling)( AstPlot *, int * );
void (* SetLabelling)( AstPlot *, int, int * );
void (* ClearLabelling)( AstPlot *, int * );
double (* GetMajTickLen)( AstPlot *, int, int * );
int (* TestMajTickLen)( AstPlot *, int, int * );
void (* SetMajTickLen)( AstPlot *, int, double, int * );
void (* ClearMajTickLen)( AstPlot *, int, int * );
double (* GetMinTickLen)( AstPlot *, int, int * );
int (* TestMinTickLen)( AstPlot *, int, int * );
void (* SetMinTickLen)( AstPlot *, int, double, int * );
void (* ClearMinTickLen)( AstPlot *, int, int * );
double (* GetNumLabGap)( AstPlot *, int, int * );
int (* TestNumLabGap)( AstPlot *, int, int * );
void (* SetNumLabGap)( AstPlot *, int, double, int * );
void (* ClearNumLabGap)( AstPlot *, int, int * );
double (* GetTextLabGap)( AstPlot *, int, int * );
int (* TestTextLabGap)( AstPlot *, int, int * );
void (* SetTextLabGap)( AstPlot *, int, double, int * );
void (* ClearTextLabGap)( AstPlot *, int, int * );
double (* GetTitleGap)( AstPlot *, int * );
int (* TestTitleGap)( AstPlot *, int * );
void (* SetTitleGap)( AstPlot *, double, int * );
void (* ClearTitleGap)( AstPlot *, int * );
double (* GetLabelAt)( AstPlot *, int, int * );
int (* TestLabelAt)( AstPlot *, int, int * );
void (* SetLabelAt)( AstPlot *, int, double, int * );
void (* ClearLabelAt)( AstPlot *, int, int * );
double (* GetGap)( AstPlot *, int, int * );
int (* TestGap)( AstPlot *, int, int * );
void (* SetGap)( AstPlot *, int, double, int * );
void (* ClearGap)( AstPlot *, int, int * );
double (* GetLogGap)( AstPlot *, int, int * );
int (* TestLogGap)( AstPlot *, int, int * );
void (* SetLogGap)( AstPlot *, int, double, int * );
void (* ClearLogGap)( AstPlot *, int, int * );
double (* GetCentre)( AstPlot *, int, int * );
int (* TestCentre)( AstPlot *, int, int * );
void (* SetCentre)( AstPlot *, int, double, int * );
void (* ClearCentre)( AstPlot *, int, int * );
int (* GetEdge)( AstPlot *, int, int * );
int (* TestEdge)( AstPlot *, int, int * );
void (* SetEdge)( AstPlot *, int, int, int * );
void (* ClearEdge)( AstPlot *, int, int * );
int (* GetNumLab)( AstPlot *, int, int * );
int (* TestNumLab)( AstPlot *, int, int * );
void (* SetNumLab)( AstPlot *, int, int, int * );
void (* ClearNumLab)( AstPlot *, int, int * );
int (* GetMinTick)( AstPlot *, int, int * );
int (* TestMinTick)( AstPlot *, int, int * );
void (* SetMinTick)( AstPlot *, int, int, int * );
void (* ClearMinTick)( AstPlot *, int, int * );
int (* GetTextLab)( AstPlot *, int, int * );
int (* TestTextLab)( AstPlot *, int, int * );
void (* SetTextLab)( AstPlot *, int, int, int * );
void (* ClearTextLab)( AstPlot *, int, int * );
int (* GetLabelUnits)( AstPlot *, int, int * );
int (* TestLabelUnits)( AstPlot *, int, int * );
void (* SetLabelUnits)( AstPlot *, int, int, int * );
void (* ClearLabelUnits)( AstPlot *, int, int * );
int (* GetStyle)( AstPlot *, int, int * );
int (* TestStyle)( AstPlot *, int, int * );
void (* SetStyle)( AstPlot *, int, int, int * );
void (* ClearStyle)( AstPlot *, int, int * );
int (* GetFont)( AstPlot *, int, int * );
int (* TestFont)( AstPlot *, int, int * );
void (* SetFont)( AstPlot *, int, int, int * );
void (* ClearFont)( AstPlot *, int, int * );
int (* GetColour)( AstPlot *, int, int * );
int (* TestColour)( AstPlot *, int, int * );
void (* SetColour)( AstPlot *, int, int, int * );
void (* ClearColour)( AstPlot *, int, int * );
double (* GetWidth)( AstPlot *, int, int * );
int (* TestWidth)( AstPlot *, int, int * );
void (* SetWidth)( AstPlot *, int, double, int * );
void (* ClearWidth)( AstPlot *, int, int * );
double (* GetSize)( AstPlot *, int, int * );
int (* TestSize)( AstPlot *, int, int * );
void (* SetSize)( AstPlot *, int, double, int * );
void (* ClearSize)( AstPlot *, int, int * );
int (* GetInk)( AstPlot *, int * );
int (* TestInk)( AstPlot *, int * );
void (* SetInk)( AstPlot *, int, int * );
void (* ClearInk)( AstPlot *, int * );
} AstPlotVtab;
/* Structure holding information about curves drawn by astGridLine and
astCurve. */
typedef struct AstPlotCurveData{
int out; /* Was the curve completely outside the clipping area? */
int nbrk; /* The number of breaks in the curve. */
float xbrk[ AST__PLOT_CRV_MXBRK ]; /* Graphics X coordinate at each break. */
float ybrk[ AST__PLOT_CRV_MXBRK ]; /* Graphics Y coordinate at each break. */
float vxbrk[ AST__PLOT_CRV_MXBRK ]; /* X comp. of unit tangent vector */
float vybrk[ AST__PLOT_CRV_MXBRK ]; /* Y comp. of unit tangent vector */
float length; /* Drawn length of the curve in graphics coordinates */
} AstPlotCurveData;
#if defined(THREAD_SAFE)
/* Define a structure holding all data items that are global within this
class. */
typedef struct AstPlotGlobals {
AstPlotVtab Class_Vtab;
int Class_Init;
double GrfAttrs_attrs_t[ GRF__NATTR ];
int GrfAttrs_nesting_t;
double Crv_limit_t;
double Crv_scerr_t;
double Crv_tol_t;
double Crv_ux0_t;
double Crv_uy0_t;
double Crv_vxl_t;
double Crv_vyl_t;
double Crv_xhi_t;
double Crv_xl_t;
double Crv_xlo_t;
double Crv_yhi_t;
double Crv_yl_t;
double Crv_ylo_t;
float *Crv_vxbrk_t;
float *Crv_vybrk_t;
float *Crv_xbrk_t;
float *Crv_ybrk_t;
float Crv_len_t;
int Crv_ink_t;
int Crv_nbrk_t;
int Crv_nent_t;
int Crv_out_t;
int Crv_clip_t;
void (*Crv_map_t)( int, double *, double *, double *, const char *, const char *, int *, struct AstGlobals * );
void *Crv_mapstatics_t;
float Box_lbnd_t[ 2 ];
float Box_ubnd_t[ 2 ];
float Boxp_lbnd_t[ 2 ];
float Boxp_ubnd_t[ 2 ];
int Boxp_freeze_t;
float *Poly_x_t;
float *Poly_y_t;
int Poly_n_t;
float **Poly_xp_t;
float **Poly_yp_t;
int *Poly_np_t;
int Poly_npoly_t;
int Map1_ncoord_t;
AstPlot *Map1_plot_t;
AstMapping *Map1_map_t;
AstFrame *Map1_frame_t;
const double *Map1_origin_t;
double Map1_length_t;
int Map1_axis_t;
void *Map1_statics_t;
int Map1_norm_t;
int Map1_log_t;
int Map2_ncoord_t;
AstPlot *Map2_plot_t;
AstMapping *Map2_map_t;
double Map2_x0_t;
double Map2_y0_t;
double Map2_deltax_t;
double Map2_deltay_t;
void *Map2_statics_t;
int Map3_ncoord_t;
AstPlot *Map3_plot_t;
AstMapping *Map3_map_t;
AstFrame *Map3_frame_t;
const double *Map3_origin_t;
const double *Map3_end_t;
double Map3_scale_t;
void *Map3_statics_t;
int Map4_ncoord_t;
AstPlot *Map4_plot_t;
AstMapping *Map4_map_t;
AstMapping *Map4_umap_t;
void *Map4_statics_t;
int Map5_ncoord_t;
AstPlot *Map5_plot_t;
AstMapping *Map5_map_t;
AstRegion *Map5_region_t;
void *Map5_statics_t;
AstPlotCurveData Curve_data_t;
char GetAttrib_Buff[ 200 ];
char SplitValue_Buff[ 200 ];
char StripEscapes_Buff[ AST__PLOT_STRIPESCAPES_BUFF_LEN + 1 ];
double Grf_chv_t;
double Grf_chh_t;
float Grf_alpha_t;
float Grf_beta_t;
} AstPlotGlobals;
#endif
#endif
/* Function prototypes. */
/* ==================== */
/* Prototypes for standard class functions. */
/* ---------------------------------------- */
astPROTO_CHECK(Plot) /* Check class membership */
astPROTO_ISA(Plot) /* Test class membership */
/* Constructor. */
#if defined(astCLASS) /* Protected. */
AstPlot *astPlot_( void *, const float *, const double *, const char *, int *, ...);
#else
AstPlot *astPlotId_( void *, const float [], const double [], const char *, ... )__attribute__((format(printf,4,5)));
#endif
#if defined(astCLASS) /* Protected */
/* Initialiser. */
AstPlot *astInitPlot_( void *, size_t, int, AstPlotVtab *,
const char *, AstFrame *, const float *, const double *, int * );
/* Vtab initialiser. */
void astInitPlotVtab_( AstPlotVtab *, const char *, int * );
/* Loader. */
AstPlot *astLoadPlot_( void *, size_t, AstPlotVtab *,
const char *, AstChannel *channel, int * );
/* Thread-safe initialiser for all global data used by this module. */
#if defined(THREAD_SAFE)
void astInitPlotGlobals_( AstPlotGlobals * );
#endif
#endif
/* Prototypes for member functions. */
/* -------------------------------- */
AstKeyMap *astGetGrfContext_( AstPlot *, int * );
AstKeyMap *astGrfConID_( AstPlot *, int * );
const char *astStripEscapes_( const char *, int * );
int astBorder_( AstPlot *, int * );
int astFindEscape_( const char *, int *, int *, int *, int * );
void astBBuf_( AstPlot *, int * );
void astBoundingBox_( AstPlot *, float[2], float[2], int * );
void astClip_( AstPlot *, int, const double [], const double [], int * );
void astCurve_( AstPlot *, const double [], const double [], int * );
void astEBuf_( AstPlot *, int * );
void astGenCurve_( AstPlot *, AstMapping *, int * );
void astGrfPop_( AstPlot *, int * );
void astGrfPush_( AstPlot *, int * );
void astGrfSet_( AstPlot *, const char *, AstGrfFun, int * );
void astGridLine_( AstPlot *, int, const double [], double, int * );
void astGrid_( AstPlot *, int * );
void astMark_( AstPlot *, int, int, int, const double *, int, int * );
void astPolyCurve_( AstPlot *, int, int, int, const double *, int * );
void astRegionOutline_( AstPlot *, AstRegion *, int * );
void astText_( AstPlot *, const char *, const double [], const float [], const char *, int * );
void astGrfWrapper_( AstPlot *, const char *, AstGrfWrap, int * );
int astGrfFunID_( const char *, const char *, const char *, int * );
#if defined(astCLASS) /* Protected */
int astCvBrk_( AstPlot *, int, double *, double *, double *, int * );
void astCopyPlotDefaults_( AstPlot *, int, AstPlot *, int, int * );
void astMirror_( AstPlot *, int, int * );
AstPointSet *astGetDrawnTicks_( AstPlot *, int, int, int * );
void astSetTickValues_( AstPlot *, int, int, double *, int, double *, int * );
void astDrawExtraTicks_( AstPlot *, int, AstPointSet *, int * );
void astGrfAttrs_( AstPlot *, int, int, int, const char *, const char *, int * );
double astGetTol_( AstPlot *, int * );
int astTestTol_( AstPlot *, int * );
void astSetTol_( AstPlot *, double, int * );
void astClearTol_( AstPlot *, int * );
int astGetGrid_( AstPlot *, int * );
int astTestGrid_( AstPlot *, int * );
void astSetGrid_( AstPlot *, int, int * );
void astClearGrid_( AstPlot *, int * );
int astGetTickAll_( AstPlot *, int * );
int astTestTickAll_( AstPlot *, int * );
void astSetTickAll_( AstPlot *, int, int * );
void astClearTickAll_( AstPlot *, int * );
int astGetForceExterior_( AstPlot *, int * );
int astTestForceExterior_( AstPlot *, int * );
void astSetForceExterior_( AstPlot *, int, int * );
void astClearForceExterior_( AstPlot *, int * );
int astGetInvisible_( AstPlot *, int * );
int astTestInvisible_( AstPlot *, int * );
void astSetInvisible_( AstPlot *, int, int * );
void astClearInvisible_( AstPlot *, int * );
int astGetBorder_( AstPlot *, int * );
int astTestBorder_( AstPlot *, int * );
void astSetBorder_( AstPlot *, int, int * );
void astClearBorder_( AstPlot *, int * );
int astGetClip_( AstPlot *, int * );
int astTestClip_( AstPlot *, int * );
void astSetClip_( AstPlot *, int, int * );
void astClearClip_( AstPlot *, int * );
int astGetClipOp_( AstPlot *, int * );
int astTestClipOp_( AstPlot *, int * );
void astSetClipOp_( AstPlot *, int, int * );
void astClearClipOp_( AstPlot *, int * );
int astGetGrf_( AstPlot *, int * );
int astTestGrf_( AstPlot *, int * );
void astSetGrf_( AstPlot *, int, int * );
void astClearGrf_( AstPlot *, int * );
int astGetDrawTitle_( AstPlot *, int * );
int astTestDrawTitle_( AstPlot *, int * );
void astSetDrawTitle_( AstPlot *, int, int * );
void astClearDrawTitle_( AstPlot *, int * );
int astGetLabelUp_( AstPlot *, int, int * );
int astTestLabelUp_( AstPlot *, int, int * );
void astSetLabelUp_( AstPlot *, int, int, int * );
void astClearLabelUp_( AstPlot *, int, int * );
int astGetLogPlot_( AstPlot *, int, int * );
int astTestLogPlot_( AstPlot *, int, int * );
void astSetLogPlot_( AstPlot *, int, int, int * );
void astClearLogPlot_( AstPlot *, int, int * );
int astGetLogTicks_( AstPlot *, int, int * );
int astTestLogTicks_( AstPlot *, int, int * );
void astSetLogTicks_( AstPlot *, int, int, int * );
void astClearLogTicks_( AstPlot *, int, int * );
int astGetLogLabel_( AstPlot *, int, int * );
int astTestLogLabel_( AstPlot *, int, int * );
void astSetLogLabel_( AstPlot *, int, int, int * );
void astClearLogLabel_( AstPlot *, int, int * );
int astGetDrawAxes_( AstPlot *, int, int * );
int astTestDrawAxes_( AstPlot *, int, int * );
void astSetDrawAxes_( AstPlot *, int, int, int * );
void astClearDrawAxes_( AstPlot *, int, int * );
int astGetAbbrev_( AstPlot *, int, int * );
int astTestAbbrev_( AstPlot *, int, int * );
void astSetAbbrev_( AstPlot *, int, int, int * );
void astClearAbbrev_( AstPlot *, int, int * );
int astGetEscape_( AstPlot *, int * );
int astTestEscape_( AstPlot *, int * );
void astSetEscape_( AstPlot *, int, int * );
void astClearEscape_( AstPlot *, int * );
double astGetLabelAt_( AstPlot *, int, int * );
int astTestLabelAt_( AstPlot *, int, int * );
void astSetLabelAt_( AstPlot *, int, double, int * );
void astClearLabelAt_( AstPlot *, int, int * );
double astGetGap_( AstPlot *, int, int * );
int astTestGap_( AstPlot *, int, int * );
void astSetGap_( AstPlot *, int, double, int * );
void astClearGap_( AstPlot *, int, int * );
double astGetLogGap_( AstPlot *, int, int * );
int astTestLogGap_( AstPlot *, int, int * );
void astSetLogGap_( AstPlot *, int, double, int * );
void astClearLogGap_( AstPlot *, int, int * );
double astGetCentre_( AstPlot *, int, int * );
int astTestCentre_( AstPlot *, int, int * );
void astSetCentre_( AstPlot *, int, double, int * );
void astClearCentre_( AstPlot *, int, int * );
int astGetLabelling_( AstPlot *, int * );
int astTestLabelling_( AstPlot *, int * );
void astSetLabelling_( AstPlot *, int, int * );
void astClearLabelling_( AstPlot *, int * );
double astGetMajTickLen_( AstPlot *, int, int * );
int astTestMajTickLen_( AstPlot *, int, int * );
void astSetMajTickLen_( AstPlot *, int, double, int * );
void astClearMajTickLen_( AstPlot *, int, int * );
double astGetMinTickLen_( AstPlot *, int, int * );
int astTestMinTickLen_( AstPlot *, int, int * );
void astSetMinTickLen_( AstPlot *, int, double, int * );
void astClearMinTickLen_( AstPlot *, int, int * );
double astGetNumLabGap_( AstPlot *, int, int * );
int astTestNumLabGap_( AstPlot *, int, int * );
void astSetNumLabGap_( AstPlot *, int, double, int * );
void astClearNumLabGap_( AstPlot *, int, int * );
double astGetTextLabGap_( AstPlot *, int, int * );
int astTestTextLabGap_( AstPlot *, int, int * );
void astSetTextLabGap_( AstPlot *, int, double, int * );
void astClearTextLabGap_( AstPlot *, int, int * );
double astGetTitleGap_( AstPlot *, int * );
int astTestTitleGap_( AstPlot *, int * );
void astSetTitleGap_( AstPlot *, double, int * );
void astClearTitleGap_( AstPlot *, int * );
int astGetEdge_( AstPlot *, int, int * );
int astTestEdge_( AstPlot *, int, int * );
void astSetEdge_( AstPlot *, int, int, int * );
void astClearEdge_( AstPlot *, int, int * );
int astGetMinTick_( AstPlot *, int, int * );
int astTestMinTick_( AstPlot *, int, int * );
void astSetMinTick_( AstPlot *, int, int, int * );
void astClearMinTick_( AstPlot *, int, int * );
int astGetNumLab_( AstPlot *, int, int * );
int astTestNumLab_( AstPlot *, int, int * );
void astSetNumLab_( AstPlot *, int, int, int * );
void astClearNumLab_( AstPlot *, int, int * );
int astGetTextLab_( AstPlot *, int, int * );
int astTestTextLab_( AstPlot *, int, int * );
void astSetTextLab_( AstPlot *, int, int, int * );
void astClearTextLab_( AstPlot *, int, int * );
int astGetLabelUnits_( AstPlot *, int, int * );
int astTestLabelUnits_( AstPlot *, int, int * );
void astSetLabelUnits_( AstPlot *, int, int, int * );
void astClearLabelUnits_( AstPlot *, int, int * );
int astGetStyle_( AstPlot *, int, int * );
int astTestStyle_( AstPlot *, int, int * );
void astSetStyle_( AstPlot *, int, int, int * );
void astClearStyle_( AstPlot *, int, int * );
int astGetFont_( AstPlot *, int, int * );
int astTestFont_( AstPlot *, int, int * );
void astSetFont_( AstPlot *, int, int, int * );
void astClearFont_( AstPlot *, int, int * );
int astGetColour_( AstPlot *, int, int * );
int astTestColour_( AstPlot *, int, int * );
void astSetColour_( AstPlot *, int, int, int * );
void astClearColour_( AstPlot *, int, int * );
double astGetWidth_( AstPlot *, int, int * );
int astTestWidth_( AstPlot *, int, int * );
void astSetWidth_( AstPlot *, int, double, int * );
void astClearWidth_( AstPlot *, int, int * );
double astGetSize_( AstPlot *, int, int * );
int astTestSize_( AstPlot *, int, int * );
void astSetSize_( AstPlot *, int, double, int * );
void astClearSize_( AstPlot *, int, int * );
int astGetInk_( AstPlot *, int * );
int astTestInk_( AstPlot *, int * );
void astSetInk_( AstPlot *, int, int * );
void astClearInk_( AstPlot *, int * );
#endif
/* Function interfaces. */
/* ==================== */
/* These macros are wrap-ups for the functions defined by this class to make
them easier to invoke (e.g. to avoid type mis-matches when passing pointers
to objects from derived classes). */
/* Interfaces to standard class functions. */
/* --------------------------------------- */
/* Some of these functions provide validation, so we cannot use them to
validate their own arguments. We must use a cast when passing object
pointers (so that they can accept objects from derived classes). */
/* Check class membership. */
#define astCheckPlot(this) astINVOKE_CHECK(Plot,this,0)
#define astVerifyPlot(this) astINVOKE_CHECK(Plot,this,1)
/* Test class membership. */
#define astIsAPlot(this) astINVOKE_ISA(Plot,this)
#if defined(astCLASS) /* Protected */
#define astPlot astINVOKE(F,astPlot_)
#else
#define astPlot astINVOKE(F,astPlotId_)
#endif
#if defined(astCLASS) /* Protected */
/* Initialiser. */
#define astInitPlot(mem,size,init,vtab,name,frame,graph,base) \
astINVOKE(O,astInitPlot_(mem,size,init,vtab,name,frame,graph,base,STATUS_PTR))
/* Vtab Initialiser. */
#define astInitPlotVtab(vtab,name) astINVOKE(V,astInitPlotVtab_(vtab,name,STATUS_PTR))
/* Loader. */
#define astLoadPlot(mem,size,vtab,name,channel) \
astINVOKE(O,astLoadPlot_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR))
#endif
/* Interfaces to member functions. */
/* ------------------------------- */
/* Here we make use of astCheckPlot (et al.) to validate Plot
pointers before use. This provides a contextual error report if a
pointer to the wrong sort of object is supplied. */
#define astGetGrfContext(this) \
astINVOKE(O,astGetGrfContext_(astCheckPlot(this),STATUS_PTR))
#define astBorder(this) \
astINVOKE(V,astBorder_(astCheckPlot(this),STATUS_PTR))
#define astBoundingBox(this,lbnd,ubnd) \
astINVOKE(V,astBoundingBox_(astCheckPlot(this),lbnd,ubnd,STATUS_PTR))
#define astClip(this,iframe,lbnd,ubnd) \
astINVOKE(V,astClip_(astCheckPlot(this),iframe,lbnd,ubnd,STATUS_PTR))
#define astMark(this,nmark,ncoord,indim,in,type) \
astINVOKE(V,astMark_(astCheckPlot(this),nmark,ncoord,indim,in,type,STATUS_PTR))
#define astText(this,text,pos,up,just) \
astINVOKE(V,astText_(astCheckPlot(this),text,pos,up,just,STATUS_PTR))
#define astGrid(this) \
astINVOKE(V,astGrid_(astCheckPlot(this),STATUS_PTR))
#define astGridLine(this,axis,start,length) \
astINVOKE(V,astGridLine_(astCheckPlot(this),axis,start,length,STATUS_PTR))
#define astCurve(this,start,finish) \
astINVOKE(V,astCurve_(astCheckPlot(this),start,finish,STATUS_PTR))
#define astGenCurve(this,map) \
astINVOKE(V,astGenCurve_(astCheckPlot(this),astCheckMapping(map),STATUS_PTR))
#define astPolyCurve(this,npoint,ncoord,dim,in) \
astINVOKE(V,astPolyCurve_(astCheckPlot(this),npoint,ncoord,dim,in,STATUS_PTR))
#define astRegionOutline(this,region) \
astINVOKE(V,astRegionOutline_(astCheckPlot(this),astCheckRegion(region),STATUS_PTR))
#define astGrfSet(this,name,fun) \
astINVOKE(V,astGrfSet_(astCheckPlot(this),name,fun,STATUS_PTR))
#define astGrfPush(this) \
astINVOKE(V,astGrfPush_(astCheckPlot(this),STATUS_PTR))
#define astGrfPop(this) \
astINVOKE(V,astGrfPop_(astCheckPlot(this),STATUS_PTR))
#define astBBuf(this) \
astINVOKE(V,astBBuf_(astCheckPlot(this),STATUS_PTR))
#define astEBuf(this) \
astINVOKE(V,astEBuf_(astCheckPlot(this),STATUS_PTR))
#define astGrfFunID(name,method,class) astGrfFunID_(name,method,class,STATUS_PTR)
#define astFindEscape(text,type,value,nc) astFindEscape_(text,type,value,nc,STATUS_PTR)
#define astStripEscapes(text) astStripEscapes_(text,STATUS_PTR)
#define astGrfConID(this) astGrfConID_(this,STATUS_PTR)
#define astGrfWrapper(this,name,wrapper) \
astINVOKE(V,astGrfWrapper_(astCheckPlot(this),name,wrapper,STATUS_PTR))
#if defined(astCLASS) /* Protected */
#define astGrfAttrs(this,id,set,prim,method,class) \
astGrfAttrs_(astCheckPlot(this),id,set,prim,method,class,STATUS_PTR)
#define astCvBrk(this,ibrk,brk,vbrk,len) \
astINVOKE(V,astCvBrk_(astCheckPlot(this),ibrk,brk,vbrk,len,STATUS_PTR))
#define astCopyPlotDefaults(this,axis,dplot,daxis) \
astINVOKE(V,astCopyPlotDefaults_(astCheckPlot(this),axis,astCheckPlot(dplot),daxis,STATUS_PTR))
#define astMirror(this,axis) \
astINVOKE(V,astMirror_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetDrawnTicks(this,axis,major) \
astINVOKE(O,astGetDrawnTicks_(astCheckPlot(this),axis,major,STATUS_PTR))
#define astSetTickValues(this,axis,nmajor,major,nminor,minor) \
astINVOKE(V,astSetTickValues_(astCheckPlot(this),axis,nmajor,major,nminor,minor,STATUS_PTR))
#define astDrawExtraTicks(this,axis,pset) \
astINVOKE(V,astDrawExtraTicks_(astCheckPlot(this),axis,astCheckPointSet(pset),STATUS_PTR))
#define astClearTol(this) \
astINVOKE(V,astClearTol_(astCheckPlot(this),STATUS_PTR))
#define astGetTol(this) \
astINVOKE(V,astGetTol_(astCheckPlot(this),STATUS_PTR))
#define astSetTol(this,tol) \
astINVOKE(V,astSetTol_(astCheckPlot(this),tol,STATUS_PTR))
#define astTestTol(this) \
astINVOKE(V,astTestTol_(astCheckPlot(this),STATUS_PTR))
#define astClearGrid(this) \
astINVOKE(V,astClearGrid_(astCheckPlot(this),STATUS_PTR))
#define astGetGrid(this) \
astINVOKE(V,astGetGrid_(astCheckPlot(this),STATUS_PTR))
#define astSetGrid(this,grid) \
astINVOKE(V,astSetGrid_(astCheckPlot(this),grid,STATUS_PTR))
#define astTestGrid(this) \
astINVOKE(V,astTestGrid_(astCheckPlot(this),STATUS_PTR))
#define astClearInk(this) \
astINVOKE(V,astClearInk_(astCheckPlot(this),STATUS_PTR))
#define astGetInk(this) \
astINVOKE(V,astGetInk_(astCheckPlot(this),STATUS_PTR))
#define astSetInk(this,ink) \
astINVOKE(V,astSetInk_(astCheckPlot(this),ink,STATUS_PTR))
#define astTestInk(this) \
astINVOKE(V,astTestInk_(astCheckPlot(this),STATUS_PTR))
#define astClearTickAll(this) \
astINVOKE(V,astClearTickAll_(astCheckPlot(this),STATUS_PTR))
#define astGetTickAll(this) \
astINVOKE(V,astGetTickAll_(astCheckPlot(this),STATUS_PTR))
#define astSetTickAll(this,tickall) \
astINVOKE(V,astSetTickAll_(astCheckPlot(this),tickall,STATUS_PTR))
#define astTestTickAll(this) \
astINVOKE(V,astTestTickAll_(astCheckPlot(this),STATUS_PTR))
#define astClearForceExterior(this) \
astINVOKE(V,astClearForceExterior_(astCheckPlot(this),STATUS_PTR))
#define astGetForceExterior(this) \
astINVOKE(V,astGetForceExterior_(astCheckPlot(this),STATUS_PTR))
#define astSetForceExterior(this,frcext) \
astINVOKE(V,astSetForceExterior_(astCheckPlot(this),frcext,STATUS_PTR))
#define astTestForceExterior(this) \
astINVOKE(V,astTestForceExterior_(astCheckPlot(this),STATUS_PTR))
#define astClearBorder(this) \
astINVOKE(V,astClearBorder_(astCheckPlot(this),STATUS_PTR))
#define astGetBorder(this) \
astINVOKE(V,astGetBorder_(astCheckPlot(this),STATUS_PTR))
#define astSetBorder(this,border) \
astINVOKE(V,astSetBorder_(astCheckPlot(this),border,STATUS_PTR))
#define astTestBorder(this) \
astINVOKE(V,astTestBorder_(astCheckPlot(this),STATUS_PTR))
#define astClearClip(this) \
astINVOKE(V,astClearClip_(astCheckPlot(this),STATUS_PTR))
#define astGetClip(this) \
astINVOKE(V,astGetClip_(astCheckPlot(this),STATUS_PTR))
#define astSetClip(this,clip) \
astINVOKE(V,astSetClip_(astCheckPlot(this),clip,STATUS_PTR))
#define astTestClip(this) \
astINVOKE(V,astTestClip_(astCheckPlot(this),STATUS_PTR))
#define astClearClipOp(this) \
astINVOKE(V,astClearClipOp_(astCheckPlot(this),STATUS_PTR))
#define astGetClipOp(this) \
astINVOKE(V,astGetClipOp_(astCheckPlot(this),STATUS_PTR))
#define astSetClipOp(this,clipop) \
astINVOKE(V,astSetClipOp_(astCheckPlot(this),clipop,STATUS_PTR))
#define astTestClipOp(this) \
astINVOKE(V,astTestClipOp_(astCheckPlot(this),STATUS_PTR))
#define astClearInvisible(this) \
astINVOKE(V,astClearInvisible_(astCheckPlot(this),STATUS_PTR))
#define astGetInvisible(this) \
astINVOKE(V,astGetInvisible_(astCheckPlot(this),STATUS_PTR))
#define astSetInvisible(this,invisible) \
astINVOKE(V,astSetInvisible_(astCheckPlot(this),invisible,STATUS_PTR))
#define astTestInvisible(this) \
astINVOKE(V,astTestInvisible_(astCheckPlot(this),STATUS_PTR))
#define astClearGrf(this) \
astINVOKE(V,astClearGrf_(astCheckPlot(this),STATUS_PTR))
#define astGetGrf(this) \
astINVOKE(V,astGetGrf_(astCheckPlot(this),STATUS_PTR))
#define astSetGrf(this,grf) \
astINVOKE(V,astSetGrf_(astCheckPlot(this),grf,STATUS_PTR))
#define astTestGrf(this) \
astINVOKE(V,astTestGrf_(astCheckPlot(this),STATUS_PTR))
#define astClearDrawTitle(this) \
astINVOKE(V,astClearDrawTitle_(astCheckPlot(this),STATUS_PTR))
#define astGetDrawTitle(this) \
astINVOKE(V,astGetDrawTitle_(astCheckPlot(this),STATUS_PTR))
#define astSetDrawTitle(this,drawtitle) \
astINVOKE(V,astSetDrawTitle_(astCheckPlot(this),drawtitle,STATUS_PTR))
#define astTestDrawTitle(this) \
astINVOKE(V,astTestDrawTitle_(astCheckPlot(this),STATUS_PTR))
#define astClearDrawAxes(this,axis) \
astINVOKE(V,astClearDrawAxes_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetDrawAxes(this,axis) \
astINVOKE(V,astGetDrawAxes_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetDrawAxes(this,axis,drawaxes) \
astINVOKE(V,astSetDrawAxes_(astCheckPlot(this),axis,drawaxes,STATUS_PTR))
#define astTestDrawAxes(this,axis) \
astINVOKE(V,astTestDrawAxes_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearAbbrev(this,axis) \
astINVOKE(V,astClearAbbrev_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetAbbrev(this,axis) \
astINVOKE(V,astGetAbbrev_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetAbbrev(this,axis,abbrev) \
astINVOKE(V,astSetAbbrev_(astCheckPlot(this),axis,abbrev,STATUS_PTR))
#define astTestAbbrev(this,axis) \
astINVOKE(V,astTestAbbrev_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearEscape(this) \
astINVOKE(V,astClearEscape_(astCheckPlot(this),STATUS_PTR))
#define astGetEscape(this) \
astINVOKE(V,astGetEscape_(astCheckPlot(this),STATUS_PTR))
#define astSetEscape(this,escape) \
astINVOKE(V,astSetEscape_(astCheckPlot(this),escape,STATUS_PTR))
#define astTestEscape(this) \
astINVOKE(V,astTestEscape_(astCheckPlot(this),STATUS_PTR))
#define astClearLabelAt(this,axis) \
astINVOKE(V,astClearLabelAt_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetLabelAt(this,axis) \
astINVOKE(V,astGetLabelAt_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetLabelAt(this,axis,labelat) \
astINVOKE(V,astSetLabelAt_(astCheckPlot(this),axis,labelat,STATUS_PTR))
#define astTestLabelAt(this,axis) \
astINVOKE(V,astTestLabelAt_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearGap(this,axis) \
astINVOKE(V,astClearGap_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetGap(this,axis) \
astINVOKE(V,astGetGap_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetGap(this,axis,gap) \
astINVOKE(V,astSetGap_(astCheckPlot(this),axis,gap,STATUS_PTR))
#define astTestGap(this,axis) \
astINVOKE(V,astTestGap_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearLogGap(this,axis) \
astINVOKE(V,astClearLogGap_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetLogGap(this,axis) \
astINVOKE(V,astGetLogGap_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetLogGap(this,axis,gap) \
astINVOKE(V,astSetLogGap_(astCheckPlot(this),axis,gap,STATUS_PTR))
#define astTestLogGap(this,axis) \
astINVOKE(V,astTestLogGap_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearCentre(this,axis) \
astINVOKE(V,astClearCentre_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetCentre(this,axis) \
astINVOKE(V,astGetCentre_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetCentre(this,axis,centre) \
astINVOKE(V,astSetCentre_(astCheckPlot(this),axis,centre,STATUS_PTR))
#define astTestCentre(this,axis) \
astINVOKE(V,astTestCentre_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearMajTickLen(this,axis) \
astINVOKE(V,astClearMajTickLen_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetMajTickLen(this,axis) \
astINVOKE(V,astGetMajTickLen_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetMajTickLen(this,axis,majticklen) \
astINVOKE(V,astSetMajTickLen_(astCheckPlot(this),axis,majticklen,STATUS_PTR))
#define astTestMajTickLen(this,axis) \
astINVOKE(V,astTestMajTickLen_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearMinTickLen(this,axis) \
astINVOKE(V,astClearMinTickLen_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetMinTickLen(this,axis) \
astINVOKE(V,astGetMinTickLen_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetMinTickLen(this,axis,minticklen) \
astINVOKE(V,astSetMinTickLen_(astCheckPlot(this),axis,minticklen,STATUS_PTR))
#define astTestMinTickLen(this,axis) \
astINVOKE(V,astTestMinTickLen_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearNumLabGap(this,axis) \
astINVOKE(V,astClearNumLabGap_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetNumLabGap(this,axis) \
astINVOKE(V,astGetNumLabGap_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetNumLabGap(this,axis,numlabgap) \
astINVOKE(V,astSetNumLabGap_(astCheckPlot(this),axis,numlabgap,STATUS_PTR))
#define astTestNumLabGap(this,axis) \
astINVOKE(V,astTestNumLabGap_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearTextLabGap(this,axis) \
astINVOKE(V,astClearTextLabGap_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetTextLabGap(this,axis) \
astINVOKE(V,astGetTextLabGap_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetTextLabGap(this,axis,textlabgap) \
astINVOKE(V,astSetTextLabGap_(astCheckPlot(this),axis,textlabgap,STATUS_PTR))
#define astTestTextLabGap(this,axis) \
astINVOKE(V,astTestTextLabGap_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearTitleGap(this) \
astINVOKE(V,astClearTitleGap_(astCheckPlot(this),STATUS_PTR))
#define astGetTitleGap(this) \
astINVOKE(V,astGetTitleGap_(astCheckPlot(this),STATUS_PTR))
#define astSetTitleGap(this,titlegap) \
astINVOKE(V,astSetTitleGap_(astCheckPlot(this),titlegap,STATUS_PTR))
#define astTestTitleGap(this) \
astINVOKE(V,astTestTitleGap_(astCheckPlot(this),STATUS_PTR))
#define astClearLabelling(this) \
astINVOKE(V,astClearLabelling_(astCheckPlot(this),STATUS_PTR))
#define astGetLabelling(this) \
astINVOKE(V,astGetLabelling_(astCheckPlot(this),STATUS_PTR))
#define astSetLabelling(this,labelling) \
astINVOKE(V,astSetLabelling_(astCheckPlot(this),labelling,STATUS_PTR))
#define astTestLabelling(this) \
astINVOKE(V,astTestLabelling_(astCheckPlot(this),STATUS_PTR))
#define astClearEdge(this,axis) \
astINVOKE(V,astClearEdge_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetEdge(this,axis) \
astINVOKE(V,astGetEdge_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetEdge(this,axis,edge) \
astINVOKE(V,astSetEdge_(astCheckPlot(this),axis,edge,STATUS_PTR))
#define astTestEdge(this,axis) \
astINVOKE(V,astTestEdge_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearMinTick(this,axis) \
astINVOKE(V,astClearMinTick_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetMinTick(this,axis) \
astINVOKE(V,astGetMinTick_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetMinTick(this,axis,mintick) \
astINVOKE(V,astSetMinTick_(astCheckPlot(this),axis,mintick,STATUS_PTR))
#define astTestMinTick(this,axis) \
astINVOKE(V,astTestMinTick_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearNumLab(this,axis) \
astINVOKE(V,astClearNumLab_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetNumLab(this,axis) \
astINVOKE(V,astGetNumLab_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetNumLab(this,axis,numlab) \
astINVOKE(V,astSetNumLab_(astCheckPlot(this),axis,numlab,STATUS_PTR))
#define astTestNumLab(this,axis) \
astINVOKE(V,astTestNumLab_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearLabelUp(this,axis) \
astINVOKE(V,astClearLabelUp_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetLabelUp(this,axis) \
astINVOKE(V,astGetLabelUp_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetLabelUp(this,axis,labelup) \
astINVOKE(V,astSetLabelUp_(astCheckPlot(this),axis,labelup,STATUS_PTR))
#define astTestLabelUp(this,axis) \
astINVOKE(V,astTestLabelUp_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearLogPlot(this,axis) \
astINVOKE(V,astClearLogPlot_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetLogPlot(this,axis) \
astINVOKE(V,astGetLogPlot_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetLogPlot(this,axis,logplot) \
astINVOKE(V,astSetLogPlot_(astCheckPlot(this),axis,logplot,STATUS_PTR))
#define astTestLogPlot(this,axis) \
astINVOKE(V,astTestLogPlot_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearLogTicks(this,axis) \
astINVOKE(V,astClearLogTicks_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetLogTicks(this,axis) \
astINVOKE(V,astGetLogTicks_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetLogTicks(this,axis,logticks) \
astINVOKE(V,astSetLogTicks_(astCheckPlot(this),axis,logticks,STATUS_PTR))
#define astTestLogTicks(this,axis) \
astINVOKE(V,astTestLogTicks_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearLogLabel(this,axis) \
astINVOKE(V,astClearLogLabel_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetLogLabel(this,axis) \
astINVOKE(V,astGetLogLabel_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetLogLabel(this,axis,loglabel) \
astINVOKE(V,astSetLogLabel_(astCheckPlot(this),axis,loglabel,STATUS_PTR))
#define astTestLogLabel(this,axis) \
astINVOKE(V,astTestLogLabel_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearTextLab(this,axis) \
astINVOKE(V,astClearTextLab_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetTextLab(this,axis) \
astINVOKE(V,astGetTextLab_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetTextLab(this,axis,textlab) \
astINVOKE(V,astSetTextLab_(astCheckPlot(this),axis,textlab,STATUS_PTR))
#define astTestTextLab(this,axis) \
astINVOKE(V,astTestTextLab_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearLabelUnits(this,axis) \
astINVOKE(V,astClearLabelUnits_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetLabelUnits(this,axis) \
astINVOKE(V,astGetLabelUnits_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetLabelUnits(this,axis,labelunits) \
astINVOKE(V,astSetLabelUnits_(astCheckPlot(this),axis,labelunits,STATUS_PTR))
#define astTestLabelUnits(this,axis) \
astINVOKE(V,astTestLabelUnits_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearStyle(this,axis) \
astINVOKE(V,astClearStyle_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetStyle(this,axis) \
astINVOKE(V,astGetStyle_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetStyle(this,axis,style) \
astINVOKE(V,astSetStyle_(astCheckPlot(this),axis,style,STATUS_PTR))
#define astTestStyle(this,axis) \
astINVOKE(V,astTestStyle_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearFont(this,axis) \
astINVOKE(V,astClearFont_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetFont(this,axis) \
astINVOKE(V,astGetFont_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetFont(this,axis,font) \
astINVOKE(V,astSetFont_(astCheckPlot(this),axis,font,STATUS_PTR))
#define astTestFont(this,axis) \
astINVOKE(V,astTestFont_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearColour(this,axis) \
astINVOKE(V,astClearColour_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetColour(this,axis) \
astINVOKE(V,astGetColour_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetColour(this,axis,colour) \
astINVOKE(V,astSetColour_(astCheckPlot(this),axis,colour,STATUS_PTR))
#define astTestColour(this,axis) \
astINVOKE(V,astTestColour_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearWidth(this,axis) \
astINVOKE(V,astClearWidth_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetWidth(this,axis) \
astINVOKE(V,astGetWidth_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetWidth(this,axis,width) \
astINVOKE(V,astSetWidth_(astCheckPlot(this),axis,width,STATUS_PTR))
#define astTestWidth(this,axis) \
astINVOKE(V,astTestWidth_(astCheckPlot(this),axis,STATUS_PTR))
#define astClearSize(this,axis) \
astINVOKE(V,astClearSize_(astCheckPlot(this),axis,STATUS_PTR))
#define astGetSize(this,axis) \
astINVOKE(V,astGetSize_(astCheckPlot(this),axis,STATUS_PTR))
#define astSetSize(this,axis,size) \
astINVOKE(V,astSetSize_(astCheckPlot(this),axis,size,STATUS_PTR))
#define astTestSize(this,axis) \
astINVOKE(V,astTestSize_(astCheckPlot(this),axis,STATUS_PTR))
#endif
#endif
ast-8.0.7/grf_2.0.c 0000664 0001750 0001750 00000005432 12610415012 010535 0000000 0000000 /*
* Name:
* grf_2.0.c
* Purpose:
* Implement the grf module required by AST V2.0 if no graphics system
* is available.
* Description:
* This file implements the low level graphics functions required
* by the rest of AST V2.0, by reporting errors when called.
* Inheritance:
* This module is not a class and does not inherit.
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* 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
* .
* Authors:
* DSB: David S. Berry (Starlink)
* History:
* 23-OCT-1996 (DSB):
* Original version.
* 13-NOV-1996 (DSB):
* Modified to issue error messages using astError instead of printf.
* 23-NOV-2004 (DSB):
* Renamed from grf_null.c
*/
/* Header files */
/* ============ */
#include "grf.h" /* Declare the functions in this module */
#include "error.h" /* AST error reporting facilities */
#include "ast_err.h" /* AST error codes */
/* Function Prototypes */
/* =================== */
static void Report( const char * );
/* Function definitions */
/* ==================== */
int astGFlush( void ){
Report( "astGFlush");
return 0;
}
int astGLine( int n, const float *x, const float *y ){
Report( "astGLine" );
return 0;
}
int astGQch( float *chv, float *chh ){
Report( "astGQch" );
return 0;
}
int astGMark( int n, const float *x, const float *y, int type ){
Report( "astGMark" );
return 0;
}
int astGText( const char *text, float x, float y, const char *just,
float upx, float upy ){
Report( "astGText" );
return 0;
}
int astGTxExt( const char *text, float x, float y, const char *just,
float upx, float upy, float *xb, float *yb ){
Report( "astGTxExt" );
return 0;
}
int astGAttr( int attr, double value, double *old_value, int prim ){
Report( "astGAttr" );
return 0;
}
static void Report( const char *name ){
astError( AST__GRFER, "%s: No graphics facilities are available.", name );
astError( AST__GRFER, "Re-link using an option such as '-pgplot' with "
"the ast_link script." );
}
ast-8.0.7/frameset.h 0000664 0001750 0001750 00000067512 12610415012 011222 0000000 0000000 /*
*+
* Name:
* frameset.h
* Type:
* C include file.
* Purpose:
* Define the interface to the FrameSet class.
* Invocation:
* #include "frameset.h"
* Description:
* This include file defines the interface to the FrameSet class and
* provides the type definitions, function prototypes and macros, etc.
* needed to use this class.
*
* A FrameSet consists of a set of one or more Frames, which are
* inter-related by Mappings in such a way that it is possible to
* obtain a Mapping between any pair of the Frames. The Frames are
* identified by an integer index, with Frames being numbered
* consecutively from one as they are added to the FrameSet.
*
* At any time, there is a "base" Frame and a "current" Frame
* (which are allowed to be the same). Any of the Frames may be
* nominated to hold these positions, and the choice is determined
* by the values of the FrameSet's Base and Current attributes
* which hold the indices of the relevant Frames. By default, the
* first Frame added to a FrameSet is its base Frame, and the last
* one added is its current Frame.
*
* The base Frame describes the "native" coordinate system of
* whatever the FrameSet is used to calibrate (e.g. the pixel
* coordinates of an image) and the current Frame describes the
* "apparent" coordinate system in which it should be viewed
* (e.g. displayed, etc.). The other Frames represent alternative
* coordinate systems which may be selected by making them current.
*
* When Frame methods are invoked on a FrameSet (e.g. to obtain a
* Title value or to determine the number of axes), they are
* applied to the current Frame. Thus, a FrameSet may be used in
* place of its current Frame in most situations.
*
* When Mapping methods are invoked on a FrameSet, the Mapping used
* is the one between its base Frame and its current Frame. Thus, a
* FrameSet may be used to convert "native" coordinates into
* "apparent" ones, and vice versa. A FrameSet may also be
* inverted, which has the effect of interchanging its base and
* current Frames (and hence of reversing the Mapping between
* them).
*
* The FrameSet class also defines methods of its own, which are
* used to manage the Frames and Mappings that it contains and to
* convert between coordinate systems described by different
* FrameSets.
* Inheritance:
* The FrameSet class inherits from the Frame class.
* Attributes Over-Ridden:
* Digits (integer)
* Direction(axis) (integer)
* Domain (string)
* Format(axis) (string)
* Label(axis) (string)
* MatchEnd (integer)
* MaxAxes (integer)
* MinAxes (integer)
* Naxes (integer)
* Permute (integer)
* PreserveAxes (integer)
* Symbol(axis) (string)
* Title (string)
* Unit(axis) (string)
* The FrameSet acquires all of these attributes from its
* current Frame, so their meanings, values and defaults are
* determined by this Frame and may change if a different
* current Frame is selected.
* Nin (integer)
* Nout (integer)
* TranForward (integer)
* TranInverse (integer)
* The FrameSet interprets all of these as applying to the
* Mapping that converts coordinates between its base Frame and
* its current Frame, so their values may change if a different
* base or current Frame is selected.
* Invert (integer)
* Report (integer)
* The FrameSet interprets these as applying to the Mapping that
* converts coordinates between its base Frame and its current
* Frame, but their values are not affected by selecing a
* different base or current Frame.
* New Attributes Defined:
* Base (integer)
* The (one-based) index of the Frame which is to be regarded as
* the base Frame in the FrameSet. By default, this is the first
* Frame added to the FrameSet (i.e. when it was created),
* unless the Frameset has been inverted, in which case it is
* the last Frame added. Inverting a FrameSet interchanges the
* values of its Base and Current attributes.
* Current (integer)
* The (one-based) index of the Frame which is to be regarded as
* the current Frame in the FrameSet. By default, this is the
* last Frame added to the FrameSet, unless the Frameset has
* been inverted, in which case it is the first Frame added
* (i.e. when the FrameSet was created). Inverting a FrameSet
* interchanges the values of its Base and Current attributes.
* Nframe (integer)
* A read-only value giving the number of Frames in a
* FrameSet. This value will change as Frames are added or
* removed.
* Methods Over-Ridden:
* Public:
* astClear
* Clear attribute values for a FrameSet.
* astConvert
* Determine how to convert between two coordinate systems.
* astDistance
* Calculate the distance between two points.
* astFindFrame
* Find a coordinate system with specified characteristics
* astFormat
* Format a coordinate value for a FrameSet axis.
* astGetAxis
* Obtain a pointer to a specified Axis from a FrameSet.
* astGetNaxes
* Determine how many axes a FrameSet has.
* astGetNin
* Get the number of input coordinates for a FrameSet.
* astGetNout
* Get the number of output coordinates for a FrameSet.
* astNorm
* Normalise a set of FrameSet coordinates.
* astOffset
* Calculate an offset along a geodesic curve.
* astPermAxes
* Permute the order of a FrameSet's axes.
* astPickAxes
* Create a new Frame by picking axes from a FrameSet.
* astSetAxis
* Set a new Axis for a FrameSet.
* astSimplify
* Simplify the Mappings in a FrameSet.
* astTransform
* Transform a set of points.
* astUnformat
* Read a formatted coordinate value for a FrameSet axis.
*
* Protected:
* astAbbrev
* Abbreviate a formatted FrameSet axis value by skipping leading
* fields.
* astClearDigits
* Clear the value of the Digits attribute for a FrameSet.
* astClearDirection
* Clear the value of the Direction attribute for a FrameSet axis.
* astClearDomain
* Clear the value of the Domain attribute for a FrameSet.
* astClearFormat
* Clear the value of the Format attribute for a FrameSet axis.
* astClearLabel
* Clear the value of the Label attribute for a FrameSet axis.
* astClearMatchEnd
* Clear the value of the MatchEnd attribute for a FrameSet.
* astClearMaxAxes
* Clear the value of the MaxAxes attribute for a FrameSet.
* astClearMinAxes
* Clear the value of the MinAxes attribute for a FrameSet.
* astClearPermute
* Clear the value of the Permute attribute for a FrameSet.
* astClearPreserveAxes
* Clear the value of the PreserveAxes attribute for a FrameSet.
* astClearSymbol
* Clear the value of the Symbol attribute for a FrameSet axis.
* astClearTitle
* Clear the value of the Title attribute for a FrameSet.
* astClearUnit
* Clear the value of the Unit attribute for a FrameSet axis.
* astConvertX
* Determine how to convert between two coordinate systems.
* astGap
* Find a "nice" gap for tabulating FrameSet axis values.
* astGetDigits
* Get the value of the Digits attribute for a FrameSet.
* astGetDirection
* Get the value of the Direction attribute for a FrameSet axis.
* astGetDomain
* Get the value of the Domain attribute for a FrameSet.
* astGetFormat
* Get the value of the Format attribute for a FrameSet axis.
* astGetLabel
* Get the value of the Label attribute for a FrameSet axis.
* astGetMatchEnd
* Get the value of the MatchEnd attribute for a FrameSet.
* astGetMaxAxes
* Get the value of the MaxAxes attribute for a FrameSet.
* astGetMinAxes
* Get the value of the MinAxes attribute for a FrameSet.
* astGetPerm
* Access the axis permutation array for the current Frame of
* a FrameSet.
* astGetPermute
* Get the value of the Permute attribute for a FrameSet.
* astGetPreserveAxes
* Get the value of the PreserveAxes attribute for a FrameSet.
* astGetSymbol
* Get the value of the Symbol attribute for a FrameSet axis.
* astGetTitle
* Get the value of the Title attribute for a FrameSet.
* astGetTranForward
* Determine if a Mapping can perform a "forward" coordinate
* transformation.
* astGetTranInverse
* Determine if a Mapping can perform an "inverse" coordinate
* transformation.
* astGetUnit
* Get the value of the Unit attribute for a FrameSet axis.
* astMatch
* Determine if conversion is possible between two coordinate systems.
* astOverlay
* Overlay the attributes of a template FrameSet on to another Frame.
* astPrimaryFrame
* Uniquely identify a primary Frame and one of its axes.
* astReportPoints
* Report the effect of transforming a set of points using a FrameSet.
* astSetAttrib
* Set an attribute value for a FrameSet.
* astSetDigits
* Set the value of the Digits attribute for a FrameSet.
* astSetDirection
* Set the value of the Direction attribute for a FrameSet axis.
* astSetDomain
* Set the value of the Domain attribute for a FrameSet.
* astSetFormat
* Set the value of the Format attribute for a FrameSet axis.
* astSetLabel
* Set the value of the Label attribute for a FrameSet axis.
* astSetMatchEnd
* Set the value of the MatchEnd attribute for a FrameSet.
* astSetMaxAxes
* Set the value of the MaxAxes attribute for a FrameSet.
* astSetMinAxes
* Set the value of the MinAxes attribute for a FrameSet.
* astSetPermute
* Set the value of the Permute attribute for a FrameSet.
* astSetPreserveAxes
* Set the value of the PreserveAxes attribute for a FrameSet.
* astSetSymbol
* Set the value of the Symbol attribute for a FrameSet axis.
* astSetTitle
* Set the value of the Title attribute for a FrameSet.
* astSetUnit
* Set the value of the Unit attribute for a FrameSet axis.
* astSubFrame
* Select axes from a FrameSet and convert to the new coordinate
* system.
* astTestDigits
* Test if a value has been set for the Digits attribute of a
* FrameSet.
* astTestDirection
* Test if a value has been set for the Direction attribute of a
* FrameSet axis.
* astTestDomain
* Test if a value has been set for the Domain attribute of a
* FrameSet.
* astTestFormat
* Test if a value has been set for the Format attribute of a
* FrameSet axis.
* astTestLabel
* Test if a value has been set for the Label attribute of a
* FrameSet axis.
* astTestMatchEnd
* Test if a value has been set for the MatchEnd attribute of a
* FrameSet.
* astTestMaxAxes
* Test if a value has been set for the MaxAxes attribute of a
* FrameSet.
* astTestMinAxes
* Test if a value has been set for the MinAxes attribute of a
* FrameSet.
* astTestPermute
* Test if a value has been set for the Permute attribute of a
* FrameSet.
* astTestPreserveAxes
* Test if a value has been set for the PreserveAxes attribute of a
* FrameSet.
* astTestSymbol
* Test if a value has been set for the Symbol attribute of a
* FrameSet axis.
* astTestTitle
* Test if a value has been set for the Title attribute of a FrameSet.
* astTestUnit
* Test if a value has been set for the Unit attribute of a FrameSet
* axis.
* astValidateAxis
* Validate and permute a FrameSet's axis index.
* astVSet
* Set values for a FrameSet's attributes.
* New Methods Defined:
* Public:
* astAddFrame
* Add a Frame to a FrameSet to define a new coordinate system.
* astGetFrame
* Obtain a pointer to a specified Frame in a FrameSet.
* astGetMapping
* Obtain a Mapping between two Frames in a FrameSet.
* astRemapFrame
* Modify a Frame's relationshp to the other Frames in a FrameSet.
* astRemoveFrame
* Remove a Frame from a FrameSet.
*
* Protected:
* astClearBase
* Clear the value of the Base attribute for a FrameSet.
* astClearCurrent
* Clear the value of the Current attribute for a FrameSet.
* astGetBase
* Obtain the value of the Base attribute for a FrameSet.
* astGetCurrent
* Obtain the value of the Current attribute for a FrameSet.
* astGetNframe
* Determine the number of Frames in a FrameSet.
* astSetBase
* Set the value of the Base attribute for a FrameSet.
* astSetCurrent
* Set the value of the Current attribute for a FrameSet.
* astTestBase
* Test if a value has been set for the Base attribute of a FrameSet.
* astTestCurrent
* Test if a value has been set for the Current attribute of a
* FrameSet.
* astValidateFrameIndex
* Validate a FrameSet Frame index number.
* Other Class Functions:
* Public:
* astFrameSet
* Create a FrameSet.
* astIsAFrameSet
* Test class membership.
*
* Protected:
* astCheckFrameSet
* Validate class membership.
* astInitFrameSet
* Initialise a FrameSet.
* astInitFrameSetVtab
* Initialise the virtual function table for the FrameSet class.
* astLoadFrameSet
* Load a FrameSet.
* Macros:
* None.
* Type Definitions:
* Public:
* AstFrameSet
* FrameSet object type.
* Protected:
* AstFrameSetVtab
* FrameSet virtual function table type.
* Macros:
* Public:
* AST__BASE
* Expands to a constant int that may be used as a Frame index to
* refer to a FrameSet's base Frame.
* AST__CURRENT
* Expands to a constant int that may be used as a Frame index to
* refer to a FrameSet's current Frame.
* AST__NOFRAME
* Expands to a constant int that is guaranteed not to be valid when
* used as a Frame index for a FrameSet.
*
* Protected:
* None.
* Feature Test Macros:
* astCLASS
* If the astCLASS macro is undefined, only public symbols are
* made available, otherwise protected symbols (for use in other
* class implementations) are defined. This macro also affects
* the reporting of error context information, which is only
* provided for external calls to the AST library.
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* 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
* .
* Authors:
* RFWS: R.F. Warren-Smith (Starlink)
* History:
* 16-FEB-1996 (RFWS):
* Original version.
* 5-JUN-1996 (RFWS):
* Tidied up, etc.
* 12-AUG-1996 (RFWS):
* Added support for the public interface.
* 25-SEP-1996 (RFWS):
* Added I/O facilities.
* 20-JAN-1998 (RFWS):
* Implemented preservation of FrameSet integrity when attribute
* values associated with the current Frame are modified.
* 25-FEB-1998 (RFWS):
* Over-ride the astUnformat method.
* 8-JAN-2003 (DSB):
* Added protected astInitFrameSetVtab method.
*-
*/
/* Include files. */
/* ============== */
/* Interface definitions. */
/* ---------------------- */
#include "frame.h" /* Parent Frame class */
/* Note that the usual setting of the FRAMESET_INCLUDED flag, which
prevents this file being included more than once, must be deferred
until after including the "frame.h" file. This is because "frame.h"
needs to include the present interface definition (as a form of
"forward reference") in order to have access to FrameSets
itself. */
#if !defined( FRAMESET_INCLUDED )
#define FRAMESET_INCLUDED
/* Macros. */
/* ======= */
/* Define a dummy __attribute__ macro for use on non-GNU compilers. */
#ifndef __GNUC__
# define __attribute__(x) /*NOTHING*/
#endif
#if defined(astCLASS) || defined(astFORTRAN77)
#define STATUS_PTR status
#else
#define STATUS_PTR astGetStatusPtr
#endif
#define AST__BASE (0) /* Identify base Frame */
#define AST__CURRENT (-1) /* Identify current Frame */
#define AST__NOFRAME (-99) /* An invalid Frame index */
#define AST__ALLFRAMES (-199) /* A value representing all Frames */
#define AST__FRAMESET_GETALLVARIANTS_BUFF_LEN 200 /* Length for AllVariants buffer */
#define AST__FRAMESET_GETATTRIB_BUFF_LEN 200 /* Length for GetAtribb buffer */
/* Type Definitions. */
/* ================= */
/* FrameSet structure. */
/* ------------------- */
/* This structure contains all information that is unique to each object in
the class (e.g. its instance variables). */
typedef struct AstFrameSet {
/* Attributes inherited from the parent class. */
AstFrame parent; /* Parent class structure */
/* Attributes specific to objects in this class. */
AstFrame **frame; /* Array of Frame pointers */
AstMapping **map; /* Array of Mapping pointers */
int *varfrm; /* Array of variants Frames indicies */
int *invert; /* Array of Mapping Invert values */
int *link; /* Parent node index for each node */
int *node; /* Index of node associated with Frame */
int base; /* Index of base Frame */
int current; /* Index of current Frame */
int nframe; /* Number of Frames */
int nnode; /* Number of nodes */
} AstFrameSet;
/* Virtual function table. */
/* ----------------------- */
/* This table contains all information that is the same for all objects in the
class (e.g. pointers to its virtual functions). */
#if defined(astCLASS) /* Protected */
typedef struct AstFrameSetVtab {
/* Properties (e.g. methods) inherited from the parent class. */
AstFrameVtab frame_vtab; /* Parent class virtual function table */
/* A Unique identifier to determine class membership. */
AstClassIdentifier id;
/* Properties (e.g. methods) specific to this class. */
AstFrame *(* GetFrame)( AstFrameSet *, int, int * );
AstMapping *(* GetMapping)( AstFrameSet *, int, int, int * );
int (* GetBase)( AstFrameSet *, int * );
int (* GetCurrent)( AstFrameSet *, int * );
int (* GetNframe)( AstFrameSet *, int * );
int (* TestBase)( AstFrameSet *, int * );
int (* TestCurrent)( AstFrameSet *, int * );
int (* ValidateFrameIndex)( AstFrameSet *, int, const char *, int * );
void (* AddFrame)( AstFrameSet *, int, AstMapping *, AstFrame *, int * );
void (* AddVariant)( AstFrameSet *, AstMapping *, const char *, int * );
void (* MirrorVariants)( AstFrameSet *, int, int * );
void (* ClearBase)( AstFrameSet *, int * );
void (* ClearCurrent)( AstFrameSet *, int * );
void (* RemapFrame)( AstFrameSet *, int, AstMapping *, int * );
void (* RemoveFrame)( AstFrameSet *, int, int * );
void (* SetBase)( AstFrameSet *, int, int * );
void (* SetCurrent)( AstFrameSet *, int, int * );
void (* ClearVariant)( AstFrameSet *, int * );
const char *(* GetVariant)( AstFrameSet *, int * );
void (* SetVariant)( AstFrameSet *, const char *, int * );
int (* TestVariant)( AstFrameSet *, int * );
const char *(* GetAllVariants)( AstFrameSet *, int * );
} AstFrameSetVtab;
#if defined(THREAD_SAFE)
/* Define a structure holding all data items that are global within this
class. */
typedef struct AstFrameSetGlobals {
AstFrameSetVtab Class_Vtab;
int Class_Init;
char GetAttrib_Buff[ AST__FRAMESET_GETATTRIB_BUFF_LEN + 1 ];
char GetAllVariants_Buff[ AST__FRAMESET_GETALLVARIANTS_BUFF_LEN + 1 ];
AstFrame *Integrity_Frame;
const char *Integrity_Method;
int Integrity_Lost;
} AstFrameSetGlobals;
#endif
#endif
/* Function prototypes. */
/* ==================== */
/* Prototypes for standard class functions. */
/* ---------------------------------------- */
astPROTO_CHECK(FrameSet) /* Check class membership */
astPROTO_ISA(FrameSet) /* Test class membership */
/* Constructor. */
#if defined(astCLASS) /* Protected */
AstFrameSet *astFrameSet_( void *, const char *, int *, ...);
#else
AstFrameSet *astFrameSetId_( void *, const char *, ... )__attribute__((format(printf,2,3)));
#endif
#if defined(astCLASS) /* Protected */
/* Initialiser. */
AstFrameSet *astInitFrameSet_( void *, size_t, int, AstFrameSetVtab *,
const char *, AstFrame *, int * );
/* Vtab initialiser. */
void astInitFrameSetVtab_( AstFrameSetVtab *, const char *, int * );
/* Loader. */
AstFrameSet *astLoadFrameSet_( void *, size_t, AstFrameSetVtab *,
const char *, AstChannel *, int * );
/* Thread-safe initialiser for all global data used by this module. */
#if defined(THREAD_SAFE)
void astInitFrameSetGlobals_( AstFrameSetGlobals * );
#endif
#endif
/* Prototypes for member functions. */
/* -------------------------------- */
AstFrame *astGetFrame_( AstFrameSet *, int, int * );
AstMapping *astGetMapping_( AstFrameSet *, int, int, int * );
void astAddFrame_( AstFrameSet *, int , AstMapping *, AstFrame *, int * );
void astAddVariant_( AstFrameSet *, AstMapping *, const char *, int * );
void astMirrorVariants_( AstFrameSet *, int, int * );
void astRemapFrame_( AstFrameSet *, int, AstMapping *, int * );
void astRemoveFrame_( AstFrameSet *, int, int * );
#if defined(astCLASS) /* Protected */
const char *astGetAllVariants_( AstFrameSet *, int * );
int astGetBase_( AstFrameSet *, int * );
int astGetCurrent_( AstFrameSet *, int * );
int astGetNframe_( AstFrameSet *, int * );
int astTestBase_( AstFrameSet *, int * );
int astTestCurrent_( AstFrameSet *, int * );
int astValidateFrameIndex_( AstFrameSet *, int, const char *, int * );
void astClearBase_( AstFrameSet *, int * );
void astClearCurrent_( AstFrameSet *, int * );
void astSetBase_( AstFrameSet *, int, int * );
void astSetCurrent_( AstFrameSet *, int, int * );
void astClearVariant_( AstFrameSet *, int * );
const char *astGetVariant_( AstFrameSet *, int * );
void astSetVariant_( AstFrameSet *, const char *, int * );
int astTestVariant_( AstFrameSet *, int * );
#endif
/* Function interfaces. */
/* ==================== */
/* These macros are wrap-ups for the functions defined by this class to make
them easier to invoke (e.g. to avoid type mis-matches when passing pointers
to objects from derived classes). */
/* Interfaces to standard class functions. */
/* --------------------------------------- */
/* Some of these functions provide validation, so we cannot use them to
validate their own arguments. We must use a cast when passing object
pointers (so that they can accept objects from derived classes). */
/* Check class membership. */
#define astCheckFrameSet(this) astINVOKE_CHECK(FrameSet,this,0)
#define astVerifyFrameSet(this) astINVOKE_CHECK(FrameSet,this,1)
/* Test class membership. */
#define astIsAFrameSet(this) astINVOKE_ISA(FrameSet,this)
/* Constructor. */
#if defined(astCLASS) /* Protected */
#define astFrameSet astINVOKE(F,astFrameSet_)
#else
#define astFrameSet astINVOKE(F,astFrameSetId_)
#endif
#if defined(astCLASS) /* Protected */
/* Initialiser. */
#define astInitFrameSet(mem,size,init,vtab,name,frame) \
astINVOKE(O,astInitFrameSet_(mem,size,init,vtab,name,astCheckFrame(frame),STATUS_PTR))
/* Vtab Initialiser. */
#define astInitFrameSetVtab(vtab,name) astINVOKE(V,astInitFrameSetVtab_(vtab,name,STATUS_PTR))
/* Loader. */
#define astLoadFrameSet(mem,size,vtab,name,channel) \
astINVOKE(O,astLoadFrameSet_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR))
#endif
/* Interfaces to public member functions. */
/* -------------------------------------- */
/* Here we make use of astCheckFrameSet to validate FrameSet pointers before
use. This provides a contextual error report if a pointer to the wrong sort
of object is supplied. */
#define astAddFrame(this,iframe,map,frame) \
astINVOKE(V,astAddFrame_(astCheckFrameSet(this),iframe,(((iframe)!=AST__ALLFRAMES)?astCheckMapping(map):NULL),astCheckFrame(frame),STATUS_PTR))
#define astAddVariant(this,map,name) \
astINVOKE(V,astAddVariant_(astCheckFrameSet(this),map?astCheckMapping(map):NULL,name,STATUS_PTR))
#define astMirrorVariants(this,iframe) \
astINVOKE(V,astMirrorVariants_(astCheckFrameSet(this),iframe,STATUS_PTR))
#define astGetFrame(this,iframe) \
astINVOKE(O,astGetFrame_(astCheckFrameSet(this),iframe,STATUS_PTR))
#define astGetMapping(this,iframe1,iframe2) \
astINVOKE(O,astGetMapping_(astCheckFrameSet(this),iframe1,iframe2,STATUS_PTR))
#define astRemapFrame(this,iframe,map) \
astINVOKE(V,astRemapFrame_(astCheckFrameSet(this),iframe,astCheckMapping(map),STATUS_PTR))
#define astRemoveFrame(this,iframe) \
astINVOKE(V,astRemoveFrame_(astCheckFrameSet(this),iframe,STATUS_PTR))
/* Interfaces to protected member functions. */
/* ----------------------------------------- */
#if defined(astCLASS) /* Protected */
#define astClearBase(this) \
astINVOKE(V,astClearBase_(astCheckFrameSet(this),STATUS_PTR))
#define astClearCurrent(this) \
astINVOKE(V,astClearCurrent_(astCheckFrameSet(this),STATUS_PTR))
#define astGetBase(this) \
astINVOKE(V,astGetBase_(astCheckFrameSet(this),STATUS_PTR))
#define astGetCurrent(this) \
astINVOKE(V,astGetCurrent_(astCheckFrameSet(this),STATUS_PTR))
#define astGetNframe(this) \
astINVOKE(V,astGetNframe_(astCheckFrameSet(this),STATUS_PTR))
#define astSetBase(this,ibase) \
astINVOKE(V,astSetBase_(astCheckFrameSet(this),ibase,STATUS_PTR))
#define astSetCurrent(this,icurrent) \
astINVOKE(V,astSetCurrent_(astCheckFrameSet(this),icurrent,STATUS_PTR))
#define astTestBase(this) \
astINVOKE(V,astTestBase_(astCheckFrameSet(this),STATUS_PTR))
#define astTestCurrent(this) \
astINVOKE(V,astTestCurrent_(astCheckFrameSet(this),STATUS_PTR))
#define astValidateFrameIndex(this,iframe,method) \
astINVOKE(V,astValidateFrameIndex_(astCheckFrameSet(this),iframe,method,STATUS_PTR))
#define astClearVariant(this) \
astINVOKE(V,astClearVariant_(astCheckFrameSet(this),STATUS_PTR))
#define astGetVariant(this) \
astINVOKE(V,astGetVariant_(astCheckFrameSet(this),STATUS_PTR))
#define astSetVariant(this,variant) \
astINVOKE(V,astSetVariant_(astCheckFrameSet(this),variant,STATUS_PTR))
#define astTestVariant(this) \
astINVOKE(V,astTestVariant_(astCheckFrameSet(this),STATUS_PTR))
#define astGetAllVariants(this) \
astINVOKE(V,astGetAllVariants_(astCheckFrameSet(this),STATUS_PTR))
#endif
#endif
ast-8.0.7/cmpmap.c 0000664 0001750 0001750 00000532075 12610415011 010664 0000000 0000000 /*
*class++
* Name:
* CmpMap
* Purpose:
* Compound Mapping.
* Constructor Function:
c astCmpMap
f AST_CMPMAP
* Description:
* A CmpMap is a compound Mapping which allows two component
* Mappings (of any class) to be connected together to form a more
* complex Mapping. This connection may either be "in series"
* (where the first Mapping is used to transform the coordinates of
* each point and the second mapping is then applied to the
* result), or "in parallel" (where one Mapping transforms the
* earlier coordinates for each point and the second Mapping
* simultaneously transforms the later coordinates).
*
* Since a CmpMap is itself a Mapping, it can be used as a
* component in forming further CmpMaps. Mappings of arbitrary
* complexity may be built from simple individual Mappings in this
* way.
* Inheritance:
* The CmpMap class inherits from the Mapping class.
* Attributes:
* The CmpMap class does not define any new attributes beyond those
* which are applicable to all Mappings.
* Functions:
c The CmpMap class does not define any new functions beyond those
f The CmpMap 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
* 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
* .
* Authors:
* RFWS: R.F. Warren-Smith (Starlink)
* History:
* 1-FEB-1996 (RFWS):
* Original version.
* 25-SEP-1996 (RFWS):
* Implemented external interface and I/O facilities.
* 12-DEC-1996 (RFWS):
* Over-ride the astMapList method.
* 13-DEC-1996 (RFWS):
* Over-ride the astSimplify method.
* 4-JUN-1997 (RFWS):
* Eliminate any simplification when MapList is used. Instead,
* over-ride the MapMerge method and implement all
* simplification in this.
* 24-MAR-1998 (RFWS):
* Fixed bug in testing of simplified invert flag in Simplify.
* 15-APR-1998 (RFWS):
* Improved the MapMerge method to allow parallel combinations
* of series CmpMaps to be replaced by series combinations of
* parallel CmpMaps, and vice versa.
* 26-SEP-2001 (DSB):
* Over-ride the astDecompose method.
* 8-JAN-2003 (DSB):
* - Changed private InitVtab method to protected astInitCmpMapVtab
* method.
* 8-JAN-2003 (DSB):
* - Modified MapMerge so that a parallel CmpMap can swap with a
* suitable PermMap lower neighbour.
* 23-APR-2004 (DSB):
* - Modified Simplify to avoid infinite loops.
* 27-APR-2004 (DSB):
* - Correction to MapMerge to prevent segvio if CmpMap and PermMap
* cannot be swapped.
* 4-OCT-2004 (DSB):
* Modify astMapList to return flag indicating presence of inverted
* CmpMaps in supplied Mapping.
* 20-APR-2005 (DSB):
* Modify MapMerge so that it will attempt to merge the first
* and second CmpMaps in a list of series CmpMaps.
* 8-FEB-2006 (DSB):
* Corrected logic within MapMerge for cases where a PermMap is
* followed by a parallel CmpMap.
* 14-FEB-2006 (DSB):
* Override astGetObjSize.
* 14-MAR-2006 (DSB):
* - When checking for patterns in the simplification process,
* require at least 30 samples in the waveform for evidence of a
* pattern.
* - Override astEqual.
* - The constructor no longer reports an error if the resulting
* CmpMap cannot transform points in either direction. This is
* because it may be possible to simplify such a CmpMap and the
* simplified Mapping may have defined transformations. E.g. if a
* Mapping which has only a forward transformation is combined in
* series with its own inverse, the combination will simplify to a
* UnitMap (usually).
* 9-MAY-2006 (DSB):
* - In Simplify, remove checks for patterns in the number of atomic
* mappings when calling astSimplify recursively.
* 23-AUG-2006 (DSB):
* - In Simplify, add checks for re-appearance of a Mapping that is
* already being simplified at a higher levelin the call stack.
* 18-APR-2007 (DSB):
* In Simplify: if the returned Mapping is not a CmpMap, always copy
* the returned component Mapping (rather than cloning it) so that
* the returned Mapping is not affected if user code subsequently
* inverts the component Mapping via some other pointer.
* 12-MAR-2008 (DSB):
* Modify MapSplit so that attempts to split the inverse
* transformation if it cannot split the forward transformation.
* 30-JUL-2009 (DSB):
* Ensure the PermMap has equal number of inputs and outputs when
* swapping a PermMap and a CmpMap in astMapMerge.
* 3-JAN-2011 (DSB):
* In MapSplit, certain classes of Mapping (e.g. PermMaps) can
* produce a returned Mapping with zero outputs. Consider such
* Mappings to be unsplitable.
* 11-JAN-2011 (DSB):
* Improve simplification of serial combinations of parellel CmpMaps.
* 25-JAN-2011 (DSB):
* Big improvement to the efficiency of the astMapSplit method.
* 24-JAN-2012 (DSB):
* If efficient MapSplit fails to split (e.g. due to the presence
* of PermMaps), then revert to the older slower method.
* 5-FEB-2013 (DSB):
* Take account of Invert flags when combining parallel CmpMaps in
* series.
* 29-APR-2013 (DSB):
* In MapList, use the astDoNotSimplify method to check that it is
* OK to expand the CmpMap.
* 23-APR-2015 (DSB):
* In Simplify, prevent mappings that are known to cause infinite
* loops from being nominated for simplification.
*class--
*/
/* Module Macros. */
/* ============== */
/* Set the name of the class we are implementing. This indicates to
the header files that define class interfaces that they should make
"protected" symbols available. */
#define astCLASS CmpMap
/* Include files. */
/* ============== */
/* Interface definitions. */
/* ---------------------- */
#include "error.h" /* Error reporting facilities */
#include "memory.h" /* Memory allocation facilities */
#include "object.h" /* Base Object class */
#include "pointset.h" /* Sets of points/coordinates */
#include "mapping.h" /* Coordinate Mappings (parent class) */
#include "channel.h" /* I/O channels */
#include "permmap.h" /* Coordinate permutation Mappings */
#include "unitmap.h" /* Unit transformations */
#include "cmpmap.h" /* Interface definition for this class */
#include "frameset.h" /* Interface definition for FrameSets */
#include "globals.h" /* Thread-safe global data access */
/* Error code definitions. */
/* ----------------------- */
#include "ast_err.h" /* AST error codes */
/* C header files. */
/* --------------- */
#include
#include
#include
#include
/* 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 int (* parent_getobjsize)( AstObject *, int * );
static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
static int (* parent_maplist)( AstMapping *, int, int, int *, AstMapping ***, int **, int * );
static int *(* parent_mapsplit)( AstMapping *, int, const int *, AstMapping **, int * );
#if defined(THREAD_SAFE)
static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * );
#endif
/* 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->Simplify_Depth = 0; \
globals->Simplify_Stackmaps = NULL;
/* Create the function that initialises global data for this module. */
astMAKE_INITGLOBALS(CmpMap)
#define class_init astGLOBAL(CmpMap,Class_Init)
#define class_vtab astGLOBAL(CmpMap,Class_Vtab)
#define simplify_depth astGLOBAL(CmpMap,Simplify_Depth)
#define simplify_stackmaps astGLOBAL(CmpMap,Simplify_Stackmaps)
/* If thread safety is not needed, declare and initialise globals at static
variables. */
#else
static int simplify_depth = 0;
static AstMapping **simplify_stackmaps = NULL;
/* Define the class virtual function table and its initialisation flag
as static variables. */
static AstCmpMapVtab 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. */
AstCmpMap *astCmpMapId_( void *, void *, int, const char *, ... );
/* Prototypes for Private Member Functions. */
/* ======================================== */
static AstMapping *CombineMaps( AstMapping *, int, AstMapping *, int, int, int * );
static AstMapping *RemoveRegions( AstMapping *, int * );
static AstMapping *Simplify( AstMapping *, int * );
static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
static double Rate( AstMapping *, double *, int, int, int * );
static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * );
static int *MapSplit0( AstMapping *, int, const int *, AstMapping **, int, int * );
static int *MapSplit1( AstMapping *, int, const int *, AstMapping **, int * );
static int *MapSplit2( AstMapping *, int, const int *, AstMapping **, int * );
static int Equal( AstObject *, AstObject *, int * );
static int GetIsLinear( AstMapping *, int * );
static int MapList( AstMapping *, int, int, int *, AstMapping ***, int **, int * );
static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * );
static int PatternCheck( int, int, int **, int *, int * );
static void Copy( const AstObject *, AstObject *, int * );
static void Decompose( AstMapping *, AstMapping **, AstMapping **, int *, int *, int *, int * );
static void Delete( AstObject *, int * );
static void Dump( AstObject *, AstChannel *, int * );
static int GetObjSize( AstObject *, int * );
#if defined(THREAD_SAFE)
static int ManageLock( AstObject *, int, int, AstObject **, int * );
#endif
/* Member functions. */
/* ================= */
static int Equal( AstObject *this_object, AstObject *that_object, int *status ) {
/*
* Name:
* Equal
* Purpose:
* Test if two CmpMaps are equivalent.
* Type:
* Private function.
* Synopsis:
* #include "cmpmap.h"
* int Equal( AstObject *this, AstObject *that, int *status )
* Class Membership:
* CmpMap member function (over-rides the astEqual protected
* method inherited from the astMapping class).
* Description:
* This function returns a boolean result (0 or 1) to indicate whether
* two CmpMaps are equivalent.
* Parameters:
* this
* Pointer to the first Object (a CmpMap).
* that
* Pointer to the second Object.
* status
* Pointer to the inherited status variable.
* Returned Value:
* One if the CmpMaps are equivalent, zero otherwise.
* 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: */
AstCmpMap *that;
AstCmpMap *this;
AstMapping **that_map_list;
AstMapping **this_map_list;
int *that_invert_list;
int *this_invert_list;
int i;
int result;
int that_inv;
int that_nmap;
int this_inv;
int this_nmap;
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain pointers to the two CmpMap structures. */
this = (AstCmpMap *) this_object;
that = (AstCmpMap *) that_object;
/* Check the second object is a CmpMap. We know the first is a
CmpMap since we have arrived at this implementation of the virtual
function. */
if( astIsACmpMap( that ) ) {
/* Check they are both either parallel or series. */
if( that->series == that->series ) {
/* Decompose the first CmpMap into a sequence of Mappings to be applied in
series or parallel, as appropriate, and an associated list of
Invert flags. */
this_nmap = 0;
this_map_list = NULL;
this_invert_list = NULL;
astMapList( (AstMapping *) this, this->series, astGetInvert( this ),
&this_nmap, &this_map_list, &this_invert_list );
/* Similarly decompose the second CmpMap. */
that_nmap = 0;
that_map_list = NULL;
that_invert_list = NULL;
astMapList( (AstMapping *) that, that->series, astGetInvert( that ),
&that_nmap, &that_map_list, &that_invert_list );
/* Check the decompositions yielded the same number of component
Mappings. */
if( that_nmap == this_nmap ) {
/* Check equality of every component. */
for( i = 0; i < this_nmap; i++ ) {
/* Temporarily set the Mapping Invert flags to the required values,
saving the original values so that they can be re-instated later.*/
this_inv = astGetInvert( this_map_list[ i ] );
astSetInvert( this_map_list[ i ], this_invert_list[ i ] );
that_inv = astGetInvert( that_map_list[ i ] );
astSetInvert( that_map_list[ i ], that_invert_list[ i ] );
/* Compare the two component Mappings for equality. */
result = astEqual( this_map_list[ i ], that_map_list[ i ] );
/* Re-instate the original Invert flags. */
astSetInvert( this_map_list[ i ], this_inv );
astSetInvert( that_map_list[ i ], that_inv );
/* Leave the loop if the Mappings are not equal. */
if( !result ) break;
}
}
/* Free resources */
for( i = 0; i < this_nmap; i++ ) {
this_map_list[ i ] = astAnnul( this_map_list[ i ] );
}
this_map_list = astFree( this_map_list );
this_invert_list = astFree( this_invert_list );
for( i = 0; i < that_nmap; i++ ) {
that_map_list[ i ] = astAnnul( that_map_list[ i ] );
}
that_map_list = astFree( that_map_list );
that_invert_list = astFree( that_invert_list );
}
}
/* If an error occurred, clear the result value. */
if ( !astOK ) result = 0;
/* Return the result, */
return result;
}
static int GetIsLinear( AstMapping *this_mapping, int *status ){
/*
* Name:
* GetIsLinear
* Purpose:
* Return the value of the IsLinear attribute for a CmpMap.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* void GetIsLinear( AstMapping *this, int *status )
* Class Membership:
* CmpMap member function (over-rides the protected astGetIsLinear
* method inherited from the Mapping class).
* Description:
* This function returns the value of the IsLinear attribute for a
* Frame, which is one if both component Mappings have a value of 1
* for the IsLinear attribute.
* Parameters:
* this
* Pointer to the CmpqMap.
* status
* Pointer to the inherited status variable.
*/
AstCmpMap *this;
this = (AstCmpMap *) this_mapping;
return astGetIsLinear( this->map1 ) && astGetIsLinear( this->map2 );
}
static int GetObjSize( AstObject *this_object, int *status ) {
/*
* Name:
* GetObjSize
* Purpose:
* Return the in-memory size of an Object.
* Type:
* Private function.
* Synopsis:
* #include "cmpmap.h"
* int GetObjSize( AstObject *this, int *status )
* Class Membership:
* CmpMap member function (over-rides the astGetObjSize protected
* method inherited from the parent class).
* Description:
* This function returns the in-memory size of the supplied CmpMap,
* in bytes.
* Parameters:
* this
* Pointer to the CmpMap.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The Object size, in bytes.
* 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: */
AstCmpMap *this; /* Pointer to CmpMap structure */
int result; /* Result value to return */
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain a pointers to the CmpMap structure. */
this = (AstCmpMap *) this_object;
/* Invoke the GetObjSize method inherited from the parent class, and then
add on any components of the class structure defined by thsi class
which are stored in dynamically allocated memory. */
result = (*parent_getobjsize)( this_object, status );
result += astGetObjSize( this->map1 );
result += astGetObjSize( this->map2 );
/* If an error occurred, clear the result value. */
if ( !astOK ) result = 0;
/* Return the result, */
return result;
}
static AstMapping *CombineMaps( AstMapping *mapping1, int invert1,
AstMapping *mapping2, int invert2,
int series, int *status ) {
/*
* Name:
* CombineMaps
* Purpose:
* Combine two Mappings with specified Invert flags into a CmpMap.
* Type:
* Private function.
* Synopsis:
* #include "cmpmap.h"
* AstMapping *CombineMaps( AstMapping *mapping1, int invert1,
* AstMapping *mapping2, int invert2,
* int series, int *status )
* Class Membership:
* CmpMap member function.
* Description:
* This function combines two Mappings into a CmpMap (compound
* Mapping) as if their Invert flags were set to specified values
* when the CmpMap is created. However, the individual Mappings are
* returned with their Invert flag values unchanged from their
* original state.
* Parameters:
* mapping1
* Pointer to the first Mapping.
* invert1
* The (boolean) Invert flag value required for the first Mapping.
* mapping2
* Pointer to the second Mapping.
* invert2
* The (boolean) Invert flag value required for the second Mapping.
* series
* Whether the Mappings are to be combined in series (as opposed to
* in parallel).
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the resulting compound Mapping (a CmpMap).
* Notes:
* - This function is a wrap-up for the astCmpMap constructor and
* temporarily assigns the required Invert flag values while
* creating the required CmpMap. However, it also takes account of
* the possibility that the two Mapping pointers supplied may point
* at the same Mapping.
* - 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.
*/
/* Local Variables: */
AstMapping *map1; /* First temporary Mapping pointer */
AstMapping *map2; /* Second temporary Mapping pointer */
AstMapping *result; /* Pointer to result Mapping */
int copy; /* Copy needed? */
int inv1; /* First original Invert flag value */
int inv2; /* Second original Invert flag value */
int set1; /* First Invert flag originally set? */
int set2; /* Second Invert flag originally set? */
/* Initialise */
result = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Limit incoming values to 0 or 1. */
invert1 = ( invert1 != 0 );
invert2 = ( invert2 != 0 );
/* Obtain the Invert flag values for each Mapping. */
inv1 = astGetInvert( mapping1 );
inv2 = astGetInvert( mapping2 );
/* Also determine if these values are explicitly set. */
set1 = astTestInvert( mapping1 );
set2 = astTestInvert( mapping2 );
/* If both Mappings are actually the same but we need different Invert
flag values to be set, then this can only be achieved by making a
copy. Note if this is necessary. */
copy = ( ( mapping1 == mapping2 ) && ( invert1 != invert2 ) );
/* Clone the first Mapping pointer. Do likewise for the second but
make a copy instead if necessary. */
map1 = astClone( mapping1 );
map2 = copy ? astCopy( mapping2 ) : astClone( mapping2 );
/* If the Invert value for the first Mapping needs changing, make the
change. */
if ( invert1 != inv1 ) {
if ( invert1 ) {
astSetInvert( map1, 1 );
} else {
astClearInvert( map1 );
}
}
/* Similarly, change the Invert flag for the second Mapping if
necessary. */
if ( invert2 != inv2 ) {
if ( invert2 ) {
astSetInvert( map2, 1 );
} else {
astClearInvert( map2 );
}
}
/* Combine the two Mappings into a CmpMap. */
result = (AstMapping *) astCmpMap( map1, map2, series, "", status );
/* If the first Mapping's Invert value was changed, restore it to its
original state. */
if ( invert1 != inv1 ) {
if ( set1 ) {
astSetInvert( map1, inv1 );
} else {
astClearInvert( map1 );
}
}
/* Similarly, restore the second Mapping's Invert value if
necessary. This step is not needed, however, if a copy was made. */
if ( ( invert2 != inv2 ) && !copy ) {
if ( set2 ) {
astSetInvert( map2, inv2 );
} else {
astClearInvert( map2 );
}
}
/* Annul the temporary Mapping pointers. */
map1 = astAnnul( map1 );
map2 = astAnnul( map2 );
/* If an error occurred, then annul the result pointer. */
if ( !astOK ) result = astAnnul( result );
/* Return the result. */
return result;
}
static void Decompose( AstMapping *this_mapping, AstMapping **map1,
AstMapping **map2, int *series, int *invert1,
int *invert2, int *status ) {
/*
*
* Name:
* Decompose
* Purpose:
* Decompose a Mapping into two component Mappings.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* void Decompose( AstMapping *this, AstMapping **map1,
* AstMapping **map2, int *series,
* int *invert1, int *invert2, int *status )
* Class Membership:
* CmpMap member function (over-rides the protected astDecompose
* method inherited from the Mapping class).
* Description:
* This function returns pointers to two Mappings which, when applied
* either in series or parallel, are equivalent to the supplied Mapping.
*
* Since the Frame class inherits from the Mapping class, Frames can
* be considered as special types of Mappings and so this method can
* be used to decompose either CmpMaps or CmpFrames.
* Parameters:
* this
* Pointer to the Mapping.
* map1
* Address of a location to receive a pointer to first component
* Mapping.
* map2
* Address of a location to receive a pointer to second component
* Mapping.
* series
* Address of a location to receive a value indicating if the
* component Mappings are applied in series or parallel. A non-zero
* value means that the supplied Mapping is equivalent to applying map1
* followed by map2 in series. A zero value means that the supplied
* Mapping is equivalent to applying map1 to the lower numbered axes
* and map2 to the higher numbered axes, in parallel.
* invert1
* The value of the Invert attribute to be used with map1.
* invert2
* The value of the Invert attribute to be used with map2.
* status
* Pointer to the inherited status variable.
* Notes:
* - Any changes made to the component Mappings using the returned
* pointers will be reflected in the supplied Mapping.
*-
*/
/* Local Variables: */
AstCmpMap *this; /* Pointer to CmpMap structure */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the CmpMap structure. */
this = (AstCmpMap *) this_mapping;
/* First deal with series mappings. */
if( this->series ) {
if( series ) *series = 1;
/* If the CmpMap has been inverted, return the Mappings in reverse
order with inverted Invert falgs. */
if( astGetInvert( this ) ) {
if( map1 ) *map1 = astClone( this->map2 );
if( map2 ) *map2 = astClone( this->map1 );
if( invert1 ) *invert1 = this->invert2 ? 0 : 1;
if( invert2 ) *invert2 = this->invert1 ? 0 : 1;
/* If the CmpMap has not been inverted, return the Mappings in their
original order with their original Invert flags. */
} else {
if( map1 ) *map1 = astClone( this->map1 );
if( map2 ) *map2 = astClone( this->map2 );
if( invert1 ) *invert1 = this->invert1;
if( invert2 ) *invert2 = this->invert2;
}
/* Now deal with parallel mappings. */
} else {
if( series ) *series = 0;
/* The mappings are returned in their original order whether or not the
CmpMap has been inverted. */
if( map1 ) *map1 = astClone( this->map1 );
if( map2 ) *map2 = astClone( this->map2 );
/* If the CmpMap has been inverted, return inverted Invert flags. */
if( astGetInvert( this ) ) {
if( invert1 ) *invert1 = this->invert1 ? 0 : 1;
if( invert2 ) *invert2 = this->invert2 ? 0 : 1;
/* If the CmpMap has not been inverted, return the original Invert flags. */
} else {
if( invert1 ) *invert1 = this->invert1;
if( invert2 ) *invert2 = this->invert2;
}
}
}
void astInitCmpMapVtab_( AstCmpMapVtab *vtab, const char *name, int *status ) {
/*
*+
* Name:
* astInitCmpMapVtab
* Purpose:
* Initialise a virtual function table for a CmpMap.
* Type:
* Protected function.
* Synopsis:
* #include "cmpmap.h"
* void astInitCmpMapVtab( AstCmpMapVtab *vtab, const char *name )
* Class Membership:
* CmpMap vtab initialiser.
* Description:
* This function initialises the component of a virtual function
* table which is used by the CmpMap 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 */
AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */
AstObjectVtab *object; /* Pointer to Object 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. */
astInitMappingVtab( (AstMappingVtab *) vtab, name );
/* Store a unique "magic" value in the virtual function table. This
will be used (by astIsACmpMap) 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 = &(((AstMappingVtab *) vtab)->id);
/* Initialise member function pointers. */
/* ------------------------------------ */
/* Store pointers to the member functions (implemented here) that
provide virtual methods for this class. */
/* None. */
/* Save the inherited pointers to methods that will be extended, and
replace them with pointers to the new member functions. */
object = (AstObjectVtab *) vtab;
mapping = (AstMappingVtab *) vtab;
parent_getobjsize = object->GetObjSize;
object->GetObjSize = GetObjSize;
#if defined(THREAD_SAFE)
parent_managelock = object->ManageLock;
object->ManageLock = ManageLock;
#endif
parent_maplist = mapping->MapList;
mapping->MapList = MapList;
parent_transform = mapping->Transform;
mapping->Transform = Transform;
parent_mapsplit = mapping->MapSplit;
mapping->MapSplit = MapSplit;
/* Store replacement pointers for methods which will be over-ridden by
new member functions implemented here. */
object->Equal = Equal;
mapping->Decompose = Decompose;
mapping->MapMerge = MapMerge;
mapping->Simplify = Simplify;
mapping->RemoveRegions = RemoveRegions;
mapping->GetIsLinear = GetIsLinear;
/* For some reason the CmpMap implementation of astRate can be immensely
slow for complex Mapping, so it's currently disable until such time as
I have time to sort it out.
mapping->Rate = Rate;
*/
/* Declare the copy constructor, destructor and class dump function. */
astSetCopy( vtab, Copy );
astSetDelete( vtab, Delete );
astSetDump( vtab, Dump, "CmpMap", "Compound Mapping" );
/* 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) );
}
}
#if defined(THREAD_SAFE)
static int ManageLock( AstObject *this_object, int mode, int extra,
AstObject **fail, int *status ) {
/*
* Name:
* ManageLock
* Purpose:
* Manage the thread lock on an Object.
* Type:
* Private function.
* Synopsis:
* #include "object.h"
* AstObject *ManageLock( AstObject *this, int mode, int extra,
* AstObject **fail, int *status )
* Class Membership:
* CmpMap member function (over-rides the astManageLock protected
* method inherited from the parent class).
* Description:
* This function manages the thread lock on the supplied Object. The
* lock can be locked, unlocked or checked by this function as
* deteremined by parameter "mode". See astLock for details of the way
* these locks are used.
* Parameters:
* this
* Pointer to the Object.
* mode
* An integer flag indicating what the function should do:
*
* AST__LOCK: Lock the Object for exclusive use by the calling
* thread. The "extra" value indicates what should be done if the
* Object is already locked (wait or report an error - see astLock).
*
* AST__UNLOCK: Unlock the Object for use by other threads.
*
* AST__CHECKLOCK: Check that the object is locked for use by the
* calling thread (report an error if not).
* extra
* Extra mode-specific information.
* fail
* If a non-zero function value is returned, a pointer to the
* Object that caused the failure is returned at "*fail". This may
* be "this" or it may be an Object contained within "this". Note,
* the Object's reference count is not incremented, and so the
* returned pointer should not be annulled. A NULL pointer is
* returned if this function returns a value of zero.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A local status value:
* 0 - Success
* 1 - Could not lock or unlock the object because it was already
* locked by another thread.
* 2 - Failed to lock a POSIX mutex
* 3 - Failed to unlock a POSIX mutex
* 4 - Bad "mode" value supplied.
* Notes:
* - This function attempts to execute even if an error has already
* occurred.
*/
/* Local Variables: */
AstCmpMap *this; /* Pointer to CmpMap structure */
int result; /* Returned status value */
/* Initialise */
result = 0;
/* Check the supplied pointer is not NULL. */
if( !this_object ) return result;
/* Obtain a pointers to the CmpMap structure. */
this = (AstCmpMap *) this_object;
/* Invoke the ManageLock method inherited from the parent class. */
if( !result ) result = (*parent_managelock)( this_object, mode, extra,
fail, status );
/* Invoke the astManageLock method on any Objects contained within
the supplied Object. */
if( !result ) result = astManageLock( this->map1, mode, extra, fail );
if( !result ) result = astManageLock( this->map2, mode, extra, fail );
return result;
}
#endif
static int MapList( AstMapping *this_mapping, int series, int invert,
int *nmap, AstMapping ***map_list, int **invert_list, int *status ) {
/*
* Name:
* MapList
* Purpose:
* Decompose a CmpMap into a sequence of simpler Mappings.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* int MapList( AstMapping *this, int series, int invert, int *nmap,
* AstMapping ***map_list, int **invert_list )
* Class Membership:
* CmpMap member function (over-rides the protected astMapList
* method inherited from the Maping class).
* Description:
* This function decomposes a CmpMap into a sequence of simpler
* Mappings which may be applied in sequence to achieve the same
* effect. The CmpMap is decomposed as far as possible, but it is
* not guaranteed that this will necessarily yield any more than
* one Mapping, which may actually be the original CmpMap supplied.
*
* This function is provided to support both the simplification of
* CmpMaps, and the analysis of CmpMap structure so that particular
* forms can be recognised.
* Parameters:
* this
* Pointer to the CmpMap to be decomposed (the CmpMap is not
* actually modified by this function).
* series
* If this value is non-zero, an attempt will be made to
* decompose the CmpMap into a sequence of equivalent Mappings
* which can be applied in series (i.e. one after the other). If
* it is zero, the decomposition will instead yield Mappings
* which can be applied in parallel (i.e. on successive sub-sets
* of the input/output coordinates).
* invert
* The value to which the CmpMap's Invert attribute is to be
* (notionally) set before performing the
* decomposition. Normally, the value supplied here will be the
* actual Invert value obtained from the CmpMap (e.g. using
* astGetInvert). Sometimes, however, when a CmpMap is
* encapsulated within another structure, that structure may
* retain an Invert value (in order to prevent external
* interference) which should be used instead.
*
* Note that the actual Invert value of the CmpMap supplied is
* not used (or modified) by this function.
* nmap
* The address of an int which holds a count of the number of
* individual Mappings in the decomposition. On entry, this
* should count the number of Mappings already in the
* "*map_list" array (below). On exit, it is updated to include
* any new Mappings appended by this function.
* map_list
* Address of a pointer to an array of Mapping pointers. On
* entry, this array pointer should either be NULL (if no
* Mappings have yet been obtained) or should point at a
* dynamically allocated array containing Mapping pointers
* ("*nmap" in number) which have been obtained from a previous
* invocation of this function.
*
* On exit, the dynamic array will be enlarged to contain any
* new Mapping pointers that result from the decomposition
* requested. These pointers will be appended to any previously
* present, and the array pointer will be updated as necessary
* to refer to the enlarged array (any space released by the
* original array will be freed automatically).
*
* The new Mapping pointers returned will identify a sequence of
* Mappings which, when applied in order, will perform a forward
* transformation equivalent to that of the original CmpMap
* (after its Invert flag has first been set to the value
* requested above). The Mappings should be applied in series or
* in parallel according to the type of decomposition requested.
*
* All the Mapping pointers returned by this function should be
* annulled by the caller, using astAnnul, when no longer
* required. The dynamic array holding these pointers should
* also be freed, using astFree.
* invert_list
* Address of a pointer to an array of int. On entry, this array
* pointer should either be NULL (if no Mappings have yet been
* obtained) or should point at a dynamically allocated array
* containing Invert attribute values ("*nmap" in number) which
* have been obtained from a previous invocation of this
* function.
*
* On exit, the dynamic array will be enlarged to contain any
* new Invert attribute values that result from the
* decomposition requested. These values will be appended to any
* previously present, and the array pointer will be updated as
* necessary to refer to the enlarged array (any space released
* by the original array will be freed automatically).
*
* The new Invert values returned identify the values which must
* be assigned to the Invert attributes of the corresponding
* Mappings (whose pointers are in the "*map_list" array) before
* they are applied. Note that these values may differ from the
* actual Invert attribute values of these Mappings, which are
* not relevant.
*
* The dynamic array holding these values should be freed by the
* caller, using astFree, when no longer required.
* Returned Value:
* A non-zero value is returned if the supplied Mapping contained any
* inverted CmpMaps.
* Notes:
* - It is unspecified to what extent the original CmpMap and the
* individual (decomposed) Mappings are
* inter-dependent. Consequently, the individual Mappings cannot be
* modified without risking modification of the original CmpMap.
* - If this function is invoked with the global error status set,
* or if it should fail for any reason, then the *nmap value, the
* list of Mapping pointers and the list of Invert values will all
* be returned unchanged.
*/
/* Local Variables: */
AstCmpMap *this; /* Pointer to CmpMap structure */
int invert1; /* Invert flag for first component Mapping */
int invert2; /* Invert flag for second component Mapping */
int r1; /* Value returned from first map list */
int r2; /* Value returned from second map list */
int result; /* Returned value */
/* Check the global error status. */
if ( !astOK ) return 0;
/* Obtain a pointer to the CmpMap structure. */
this = (AstCmpMap *) this_mapping;
/* Check if the CmpMap combines its component Mappings in the same way
(series or parallel) as the decomposition requires. Also, do not
expand CmpMaps that are not appropriate for simplification. */
if ( this->series == series && !astDoNotSimplify( this ) ) {
/* If so, obtain the Invert attribute values to be applied to each
component Mapping. */
invert1 = this->invert1;
invert2 = this->invert2;
/* If the CmpMap itself is inverted, also invert the Invert values to be
applied to its components. */
if ( invert ) {
invert1 = !invert1;
invert2 = !invert2;
}
/* If the component Mappings are applied in series, then concatenate
the Mapping lists obtained from each of them. Do this in reverse
order if the CmpMap is inverted, since the second Mapping would be
applied first in this case. */
if ( series ) {
if ( !invert ) {
r1 = astMapList( this->map1, series, invert1,
nmap, map_list, invert_list );
r2 = astMapList( this->map2, series, invert2,
nmap, map_list, invert_list );
} else {
r1 = astMapList( this->map2, series, invert2,
nmap, map_list, invert_list );
r2 = astMapList( this->map1, series, invert1,
nmap, map_list, invert_list );
}
/* If the component Mappings are applied in parallel, then concatenate
the Mapping lists obtained from each of them. In this case,
inverting the CmpMap has no effect on the order in which they are
applied. */
} else {
r1 = astMapList( this->map1, series, invert1,
nmap, map_list, invert_list );
r2 = astMapList( this->map2, series, invert2,
nmap, map_list, invert_list );
}
/* Did we find any inverted CmpMaps? */
result = invert || r1 || r2;
/* If the CmpMap does not combine its components in the way required
by the decomposition (series or parallel), then we cannot decompose
it. In this case it must be appended to the Mapping list as a
single entity. We can use the parent class method to do this. */
} else {
result = ( *parent_maplist )( this_mapping, series, invert, nmap,
map_list, invert_list, status );
}
return result;
}
static int MapMerge( AstMapping *this, int where, int series, int *nmap,
AstMapping ***map_list, int **invert_list, int *status ) {
/*
* Name:
* MapMerge
* Purpose:
* Simplify a sequence of Mappings containing a CmpMap.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* int MapMerge( AstMapping *this, int where, int series, int *nmap,
* AstMapping ***map_list, int **invert_list )
* Class Membership:
* CmpMap method (over-rides the protected astMapMerge method
* inherited from the Mapping class).
* Description:
* This function attempts to simplify a sequence of Mappings by
* merging a nominated CmpMap in the sequence with its neighbours,
* so as to shorten the sequence if possible.
*
* In many cases, simplification will not be possible and the
* function will return -1 to indicate this, without further
* action.
*
* In most cases of interest, however, this function will either
* attempt to replace the nominated CmpMap with one which it
* considers simpler, or to merge it with the Mappings which
* immediately precede it or follow it in the sequence (both will
* normally be considered). This is sufficient to ensure the
* eventual simplification of most Mapping sequences by repeated
* application of this function.
*
* In some cases, the function may attempt more elaborate
* simplification, involving any number of other Mappings in the
* sequence. It is not restricted in the type or scope of
* simplification it may perform, but will normally only attempt
* elaborate simplification in cases where a more straightforward
* approach is not adequate.
* Parameters:
* this
* Pointer to the nominated CmpMap which is to be merged with
* its neighbours. This should be a cloned copy of the CmpMap
* pointer contained in the array element "(*map_list)[where]"
* (see below). This pointer will not be annulled, and the
* CmpMap it identifies will not be modified by this function.
* where
* Index in the "*map_list" array (below) at which the pointer
* to the nominated CmpMap resides.
* series
* A non-zero value indicates that the sequence of Mappings to
* be simplified will be applied in series (i.e. one after the
* other), whereas a zero value indicates that they will be
* applied in parallel (i.e. on successive sub-sets of the
* input/output coordinates).
* nmap
* Address of an int which counts the number of Mappings in the
* sequence. On entry this should be set to the initial number
* of Mappings. On exit it will be updated to record the number
* of Mappings remaining after simplification.
* map_list
* Address of a pointer to a dynamically allocated array of
* Mapping pointers (produced, for example, by the astMapList
* method) which identifies the sequence of Mappings. On entry,
* the initial sequence of Mappings to be simplified should be
* supplied.
*
* On exit, the contents of this array will be modified to
* reflect any simplification carried out. Any form of
* simplification may be performed. This may involve any of: (a)
* removing Mappings by annulling any of the pointers supplied,
* (b) replacing them with pointers to new Mappings, (c)
* inserting additional Mappings and (d) changing their order.
*
* The intention is to reduce the number of Mappings in the
* sequence, if possible, and any reduction will be reflected in
* the value of "*nmap" returned. However, simplifications which
* do not reduce the length of the sequence (but improve its
* execution time, for example) may also be performed, and the
* sequence might conceivably increase in length (but normally
* only in order to split up a Mapping into pieces that can be
* more easily merged with their neighbours on subsequent
* invocations of this function).
*
* If Mappings are removed from the sequence, any gaps that
* remain will be closed up, by moving subsequent Mapping
* pointers along in the array, so that vacated elements occur
* at the end. If the sequence increases in length, the array
* will be extended (and its pointer updated) if necessary to
* accommodate any new elements.
*
* Note that any (or all) of the Mapping pointers supplied in
* this array may be annulled by this function, but the Mappings
* to which they refer are not modified in any way (although
* they may, of course, be deleted if the annulled pointer is
* the final one).
* invert_list
* Address of a pointer to a dynamically allocated array which,
* on entry, should contain values to be assigned to the Invert
* attributes of the Mappings identified in the "*map_list"
* array before they are applied (this array might have been
* produced, for example, by the astMapList method). These
* values will be used by this function instead of the actual
* Invert attributes of the Mappings supplied, which are
* ignored.
*
* On exit, the contents of this array will be updated to
* correspond with the possibly modified contents of the
* "*map_list" array. If the Mapping sequence increases in
* length, the "*invert_list" array will be extended (and its
* pointer updated) if necessary to accommodate any new
* elements.
* Returned Value:
* If simplification was possible, the function returns the index
* in the "map_list" array of the first element which was
* modified. Otherwise, it returns -1 (and makes no changes to the
* arrays supplied).
* Notes:
* - A value of -1 will be returned if this function is invoked
* with the global error status set, or if it should fail for any
* reason.
*/
/* Local Variables: */
AstCmpMap *cmpmap1; /* Pointer to first CmpMap */
AstCmpMap *cmpmap2; /* Pointer to second CmpMap */
AstCmpMap *cmpmap; /* Pointer to nominated CmpMap */
AstCmpMap *new_cm; /* Pointer to new CmpMap */
AstMapping **map_list1; /* Pointer to list of cmpmap1 component Mappings */
AstMapping **map_list2; /* Pointer to list of cmpmap2 component Mappings */
AstMapping **new_map_list; /* Extended Mapping list */
AstMapping *map; /* Pointer to nominated CmpMap */
AstMapping *new1; /* Pointer to new CmpMap */
AstMapping *new2; /* Pointer to new CmpMap */
AstMapping *new; /* Pointer to replacement Mapping */
AstMapping *simp1; /* Pointer to simplified Mapping */
AstMapping *simp2; /* Pointer to simplified Mapping */
AstMapping *submap1; /* A subset of mappings from cmpmap1 */
AstMapping *submap2; /* A subset of mappings from cmpmap2 */
AstMapping *tmap2; /* Temporary Mapping */
AstMapping *tmap; /* Temporary Mapping */
AstPermMap *new_pm; /* Pointer to new PermMap */
AstPermMap *permmap1; /* Pointer to first PermMap */
AstUnitMap *unit; /* UnitMap that feeds const PermMap i/p's */
const char *class; /* Pointer to Mapping class string */
double *conperm; /* Pointer to PermMap constants array */
double *const_new; /* Pointer to new PermMap constants array */
double *p; /* Pointer to PermMap input position */
double *q; /* Pointer to PermMap output position */
double *qa; /* Pointer to 1st component output position */
double *qb; /* Pointer to 2nd component output position */
int *inperm; /* Pointer to copy of PermMap inperm array */
int *inperm_new; /* Pointer to new PermMap inperm array */
int *invert_list1; /* Pointer to list of cmpmap1 invert values */
int *invert_list2; /* Pointer to list of cmpmap2 invert values */
int *new_invert_list; /* Extended Invert flag list */
int *outperm; /* Pointer to copy of PermMap outperm array */
int *outperm_new; /* Pointer to new PermMap outperm array */
int aconstants; /* Are all 1st component outputs constant? */
int bconstants; /* Are all 2nd component outputs constant? */
int canswap; /* Can nominated Mapping swap with lower neighbour? */
int i; /* Coordinate index */
int iconid; /* Constant identifier in supplied PermMap */
int imap1; /* Index of first Mapping */
int imap2; /* Index of second Mapping */
int imap; /* Loop counter for Mappings */
int invert1; /* Invert flag for first CmpMap */
int invert1a; /* Invert flag for sub-Mapping */
int invert1b; /* Invert flag for sub-Mapping */
int invert2; /* Invert flag for second CmpMap */
int invert2a; /* Invert flag for sub-Mapping */
int invert2b; /* Invert flag for sub-Mapping */
int invert; /* Invert attribute value */
int j; /* Coordinate index */
int jmap1; /* Index of next component Mapping in cmpmap1 */
int jmap2; /* Index of next component Mapping in cmpmap2 */
int new_invert; /* New Invert attribute value */
int nin2a; /* No. input coordinates for sub-Mapping */
int nin2b; /* No. input coordinates for sub-Mapping */
int nmap1; /* Number of Mappings in cmpmap1 */
int nmap2; /* Number of Mappings in cmpmap2 */
int nout2a; /* No. of outputs for 1st component Mapping */
int nout2b; /* No. of outputs for 2nd component Mapping */
int npin; /* No. of inputs for original PermMap */
int npin_new; /* No. of inputs for new PermMap */
int npout; /* No. of outputs for original PermMap */
int npout_new; /* No. of outputs for new PermMap */
int nunit; /* No. of PermMap i/p's fed by UnitMap */
int oconid; /* Constant identifier in returned PermMap */
int result; /* Result value to return */
int set; /* Invert attribute set? */
int simpler; /* Simplification possible? */
int subin2; /* Number of inputs of submap2 */
int subinv1; /* Invert attribute to use with submap1 */
int subinv2; /* Invert attribute to use with submap2 */
int subout1; /* Number of outputs of submap1 */
/* Initialise.*/
result = -1;
/* Check the inherited status. */
if ( !astOK ) return result;
/* Simplify the CmpMap on its own. */
/* =============================== */
/* Obtain a pointer to the nominated Mapping (which is a CmpMap). */
map = ( *map_list )[ where ];
cmpmap = (AstCmpMap *) map;
/* Determine if the Mapping's Invert attribute is set and obtain its
value. */
set = astTestInvert( map );
invert = astGetInvert( map );
/* If necessary, change the Invert attribute to the value we want. We
do this so that simplification (below) has a chance to absorb a
non-zero Invert value into the implementation of the simplified
Mapping (the preference being to have an Invert value of zero after
simplification, if possible). */
if ( invert != ( *invert_list )[ where ] ) {
astSetInvert( map, ( *invert_list )[ where ] );
}
/* Simplify the Mapping and obtain the new Invert value. */
new = astSimplify( map );
new_invert = astGetInvert( new );
/* If necessary, restore the original Mapping's Invert attribute to
its initial state. */
if ( invert != ( *invert_list )[ where ] ) {
if ( set ) {
astSetInvert( map, invert );
} else {
astClearInvert( map );
}
}
/* We must now determine if simplification has occurred. Since this is
internal code, we can compare the two Mapping pointers directly to
see whether "astSimplify" just cloned the pointer we gave it. If it
did, then simplification was probably not possible, but check to
see if the Invert attribute has changed to be sure. */
if ( astOK ) {
simpler = ( new != map ) || ( new_invert != ( *invert_list )[ where ] );
/* If simplification was successful, annul the original pointer in the
Mapping list and replace it with the new one, together with its
invert flag. */
if ( simpler ) {
(void) astAnnul( ( *map_list )[ where ] );
( *map_list )[ where ] = new;
( *invert_list )[ where ] = new_invert;
/* Return the result. */
result = where;
/* Otherwise, annul the new Mapping pointer. */
} else {
new = astAnnul( new );
/* If the nominated CmpMap is a series CmpMap and the sequence of
Mappings are being combined in series, or if the nominated CmpMap is
a parallel CmpMap and the sequence of Mappings are being combined in
parallel, replace the single CmpMap with the two component Mappings. */
if( ( series && cmpmap->series ) ||
( !series && !cmpmap->series ) ) {
/* We are increasing the number of Mappings in the list, so we need to create
new, larger, arrays to hold the list of Mapping pointers and invert flags. */
new_map_list = astMalloc( ( *nmap + 1 )*sizeof( AstMapping * ) );
new_invert_list = astMalloc( ( *nmap + 1 )*sizeof( int ) );
if( astOK ) {
/* Copy the values prior to the nominated CmpMap. */
for( i = 0; i < where; i++ ) {
new_map_list[ i ] = astClone( ( *map_list )[ i ] );
new_invert_list[ i ] = ( *invert_list )[ i ];
}
/* Next insert the two components of the nominated CmpMap */
new_map_list[ where ] = astClone( cmpmap->map1 );
new_invert_list[ where ] = cmpmap->invert1;
new_map_list[ where + 1 ] = astClone( cmpmap->map2 );
new_invert_list[ where + 1 ] = cmpmap->invert2;
/* Now copy any values after the nominated CmpMap. */
for( i = where + 1; i < *nmap; i++ ) {
new_map_list[ i + 1 ] = astClone( ( *map_list )[ i ] );
new_invert_list[ i + 1 ] = ( *invert_list )[ i ];
}
/* Now annul the Object pointers in the supplied map list. */
for( i = 0; i < *nmap; i++ ) {
(* map_list )[ i ] = astAnnul( ( *map_list )[ i ] );
}
/* Free the memory holding the supplied Mapping and invert flag lists. */
astFree( *map_list );
astFree( *invert_list );
/* Return pointers to the new extended lists. */
*map_list = new_map_list;
*invert_list = new_invert_list;
/* Increase the number of Mappings in the list, and the index of
the first modified Mapping. */
(*nmap)++;
result = where;
/* Indicate some simplification has taken place */
simpler = 1;
}
}
}
/* If no simplification has been done, merge adjacent CmpMaps. */
/* ========================================================== */
/* If the CmpMap would not simplify on its own, we now look for a
neighbouring CmpMap with which it might merge. We use the previous
Mapping, if suitable, since this will normally also have been fully
simplified on its own. Check if a previous Mapping exists. */
if( !simpler ) {
if ( astOK && *nmap > 1 ) {
/* Obtain the indices of the two potential Mappings to be merged. imap1
is the first Mapping, imap2 is the second. imapc is the CmpMap, imapn is
the neighbouring Mapping. */
if( where == 0 ) {
imap1 = 0;
imap2 = 1;
} else {
imap1 = where - 1;
imap2 = where;
}
/* Obtain the Class string of the neighbouring Mapping and determine if it
is a CmpMap. */
class = astGetClass( ( *map_list )[ (where>0)?where-1:1 ] );
if ( astOK && !strcmp( class, "CmpMap" ) ) {
/* If suitable, obtain pointers to the two CmpMaps. */
cmpmap1 = (AstCmpMap *) ( *map_list )[ imap1 ];
cmpmap2 = (AstCmpMap *) ( *map_list )[ imap2 ];
/* Obtain the associated invert flag values. */
invert1 = ( *invert_list )[ imap1 ];
invert2 = ( *invert_list )[ imap2 ];
/* Extract the invert flags associated with each CmpMap sub-Mapping
and combine these with the flag values obtained above so as to give
the invert flag to be used with each individual sub-Mapping. */
invert1a = cmpmap1->invert1;
invert1b = cmpmap1->invert2;
if ( invert1 ) {
invert1a = !invert1a;
invert1b = !invert1b;
}
invert2a = cmpmap2->invert1;
invert2b = cmpmap2->invert2;
if ( invert2 ) {
invert2a = !invert2a;
invert2b = !invert2b;
}
/* Series CmpMaps in parallel. */
/* =========================== */
/* Now check if the CmpMaps can be merged. This may be possible if we
are examining a list of Mappings combined in parallel and the two
adjacent CmpMaps both combine their sub-Mappings in series. */
if ( !series && cmpmap1->series && cmpmap2->series ) {
/* Form two new parallel CmpMaps with the sub-Mappings re-arranged so
that when combined in series these new CmpMaps are equivalent to
the original ones. In doing this, we must take account of the
invert flags which apply to each sub-Mapping and also of the fact
that the order in which the sub-Mappings are applied depends on the
invert flags of the original CmpMaps. */
new1 = CombineMaps( invert1 ? cmpmap1->map2 : cmpmap1->map1,
invert1 ? invert1b : invert1a,
invert2 ? cmpmap2->map2 : cmpmap2->map1,
invert2 ? invert2b : invert2a, 0, status );
new2 = CombineMaps( invert1 ? cmpmap1->map1 : cmpmap1->map2,
invert1 ? invert1a : invert1b,
invert2 ? cmpmap2->map1 : cmpmap2->map2,
invert2 ? invert2a : invert2b, 0, status );
/* Having converted the parallel combination of series CmpMaps into a
pair of equivalent parallel CmpMaps that can be combined in series,
try and simplify each of these new CmpMaps. */
simp1 = astSimplify( new1 );
simp2 = astSimplify( new2 );
/* Test if either could be simplified by checking if its pointer value
has changed. Also check if the Invert attribute has changed (not
strictly necessary, but a useful safety feature in case of any
rogue code which just changes this attribute instead of issuing a
new pointer). */
simpler = ( simp1 != new1 ) || ( simp2 != new2 ) ||
astGetInvert( simp1 ) || astGetInvert( simp2 );
/* If either CmpMap was simplified, then combine the resulting
Mappings in series to give the replacement CmpMap. */
if ( simpler ) new =
(AstMapping *) astCmpMap( simp1, simp2, 1, "", status );
/* Annul the temporary Mapping pointers. */
new1 = astAnnul( new1 );
new2 = astAnnul( new2 );
simp1 = astAnnul( simp1 );
simp2 = astAnnul( simp2 );
/* Parallel CmpMaps in series. */
/* =========================== */
/* A pair of adjacent CmpMaps can also potentially be merged if we are
examining a list of Mappings combined in series and the two
adjacent CmpMaps both combine their sub-Mappings in parallel. */
} else if ( series && !cmpmap1->series && !cmpmap2->series ) {
/* Expand each of the two adjacent CmpMaps into a list of Mappings to be
combined in parallel. */
map_list1 = map_list2 = NULL;
invert_list1 = invert_list2 = NULL;
nmap1 = nmap2 = 0;
(void) astMapList( (AstMapping *) cmpmap1, 0, invert1,
&nmap1, &map_list1, &invert_list1 );
(void) astMapList( (AstMapping *) cmpmap2, 0, invert2,
&nmap2, &map_list2, &invert_list2 );
/* We want to divide each of these lists into N sub-lists so that the
outputs of the Mappings in the i'th sub-list from cmpmap1 can feed
(i.e. equal in number) the inputs of the Mappings in the i'th sub-list
from cmpmap2. If such a sub-list contains more than one Mapping we
combine them together into a parallel CmpMap. Initialise a flag to
indicate that we have not yet found any genuine simplification. */
simpler = 0;
/* Initialise the index of the next Mapping to be added into each
sublist. */
jmap1 = jmap2 = 0;
/* Indicate both sublists are currently empty. */
subout1 = subin2 = 0;
new = submap1 = submap2 = NULL;
subinv1 = subinv2 = 0;
/* Loop round untill all Mappings have been used. */
while( jmap1 <= nmap1 && jmap2 <= nmap2 && astOK ) {
/* Note the number of outputs from submap1 and the number of inputs to
submap2. If the Invert flag is not set to the required value for
either Mapping, then inputs become outputs and vice-versa, so swap Nin
and Nout. */
if( !submap1 ) {
subout1 = 0;
} else if( subinv1 == astGetInvert( submap1 ) ) {
subout1 = astGetNout( submap1 );
} else {
subout1 = astGetNin( submap1 );
}
if( !submap2 ) {
subin2 = 0;
} else if( subinv2 == astGetInvert( submap2 ) ) {
subin2 = astGetNin( submap2 );
} else {
subin2 = astGetNout( submap2 );
}
/* If sublist for cmpmap1 has too few outputs, add the next Mapping from
the cmpmap1 list into the submap1 sublist. */
if( subout1 < subin2 ) {
tmap = CombineMaps( submap1, subinv1,
map_list1[ jmap1 ],
invert_list1[ jmap1 ], 0, status );
(void) astAnnul( submap1 );
submap1 = tmap;
subinv1 = 0;
jmap1++;
/* If sublist for cmpmap2 has too few inputs, add the next Mapping from
the cmpmap2 list into the submap2 sublist. */
} else if( subin2 < subout1 ) {
tmap = CombineMaps( submap2, subinv2,
map_list2[ jmap2 ],
invert_list2[ jmap2 ], 0, status );
(void) astAnnul( submap2 );
submap2 = tmap;
subinv2 = 0;
jmap2++;
/* If submap1 can now feed submap2, combine them in series, and attempt to
simplify it. */
} else {
/* Check this is not the first pass (when we do not have a submap1 or
submap2). */
if( submap1 && submap2 ) {
/* Combine the Mappings in series and simplify. */
tmap = CombineMaps( submap1, subinv1, submap2,
subinv2, 1, status );
submap1 = astAnnul( submap1 );
submap2 = astAnnul( submap2 );
tmap2 = astSimplify( tmap );
tmap = astAnnul( tmap );
/* Note if any simplification took place. */
if( tmap != tmap2 ||
astGetInvert( tmap ) != astGetInvert( tmap2 ) )
simpler = 1;
/* Add the simplifed Mapping into the total merged Mapping (a parallel
CmpMap). */
if( !new ) {
new = tmap2;
} else {
tmap = (AstMapping *) astCmpMap( new, tmap2, 0,
" ", status );
tmap2 = astAnnul( tmap2 );
(void) astAnnul( new );
new = tmap;
}
}
/* Reset submap1 to be the next Mapping from the cmpmap1 map list. First,
save its old Invert flag and set it to the required value. */
if( jmap1 < nmap1 ) {
submap1 = astClone( map_list1[ jmap1 ] );
subinv1 = invert_list1[ jmap1 ];
jmap1++;
} else {
break;
}
/* Do the same for the second list. */
if( jmap2 < nmap2 ) {
submap2 = astClone( map_list2[ jmap2 ] );
subinv2 = invert_list2[ jmap2 ];
jmap2++;
} else {
break;
}
}
}
/* Free the lists of Mapping pointers and invert flags. */
if( map_list1 ) {
for( jmap1 = 0; jmap1 < nmap1; jmap1++ ) {
map_list1[ jmap1 ] = astAnnul( map_list1[ jmap1 ] );
}
map_list1 = astFree( map_list1 );
}
invert_list1 = astFree( invert_list1 );
if( map_list2 ) {
for( jmap2 = 0; jmap2 < nmap2; jmap2++ ) {
map_list2[ jmap2 ] = astAnnul( map_list2[ jmap2 ] );
}
map_list2 = astFree( map_list2 );
}
invert_list2 = astFree( invert_list2 );
}
}
/* Update Mapping list. */
/* ==================== */
/* If adjacent CmpMaps can be combined, then annul the original pointers. */
if ( astOK && simpler ) {
( *map_list )[ imap1 ] = astAnnul( ( *map_list )[ imap1 ] );
( *map_list )[ imap2 ] = astAnnul( ( *map_list )[ imap2 ] );
/* Insert the pointer to the replacement CmpMap and initialise its
invert flag. */
( *map_list )[ imap1 ] = new;
( *invert_list )[ imap1 ] = 0;
/* Loop to close the resulting gap by moving subsequent elements down
in the arrays. */
for ( imap = imap2 + 1; imap < *nmap; imap++ ) {
( *map_list )[ imap - 1 ] = ( *map_list )[ imap ];
( *invert_list )[ imap - 1 ] = ( *invert_list )[ imap ];
}
/* Clear the vacated elements at the end. */
( *map_list )[ *nmap - 1 ] = NULL;
( *invert_list )[ *nmap - 1 ] = 0;
/* Decrement the Mapping count and return the index of the first
modified element. */
( *nmap )--;
result = imap1;
}
}
}
}
/* If we are merging the Mappings in series, and if the nominated CmpMap
is a parallel CmpMap, and if the lower neighbour is a PermMap, it may
be possible to swap the PermMap and the CmpMap. This may allow one of
the two swapped Mappings to merge with its new neighbour.
==================================================================== */
/* Only do this if no simplification occurred above, and if the Mappings
are being merged in series, and if the nominated Mapping is not the
first in the list. */
if( result == -1 && where > 0 ){
/* Obtain the indices of the two potential Mappings to be swapped. */
imap1 = where - 1;
imap2 = where;
/* Obtain a pointer to the CmpMap. */
cmpmap2 = (AstCmpMap *) ( *map_list )[ imap2 ];
/* Obtain the Class string of the first (previous) Mapping and
determine if it is a PermMap. Also check that the nominated Mapping is
a parallel CmpMap. */
class = astGetClass( ( *map_list )[ imap1 ] );
if ( astOK && !strcmp( class, "PermMap" ) && !cmpmap2->series) {
/* Indicate we have no new Mapping to store. */
new = NULL;
/* If suitable, obtain a pointer to the PermMap. */
permmap1 = (AstPermMap *) ( *map_list )[ imap1 ];
/* Obtain the current values of the Invert attribute in the Mappings. */
invert1 = astGetInvert( permmap1 );
invert2 = astGetInvert( cmpmap2 );
/* Temporarily set the Invert attributes of both Mappings to the values
supplied in the "invert_list" parameter. */
astSetInvert( permmap1, ( *invert_list )[ imap1 ] );
astSetInvert( cmpmap2, ( *invert_list )[ imap2 ] );
/* Get the number of inputs and outputs for the PermMap.*/
npout = astGetNout( permmap1 );
npin = astGetNin( permmap1 );
/* Get the number of inputs and outputs for the two components of the
nominated parallel CmpMap. */
nin2a = astGetNin( cmpmap2->map1 );
nin2b = astGetNin( cmpmap2->map2 );
nout2a = astGetNout( cmpmap2->map1 );
nout2b = astGetNout( cmpmap2->map2 );
/* Get the input and output axis permutation arrays and the constants
array from the PermMap */
inperm =astGetInPerm( permmap1 );
outperm =astGetOutPerm( permmap1 );
conperm = astGetConstants( permmap1 );
/* In order to swap the Mappings, the PermMap outputs which feed the
inputs of the first component of the parallel CmpMap must be copied
from a contiguous block at the end of the list of PermMap inputs, or
must all be assigned constant values. Likewise, the PermMap outputs which
feed the inputs of the second component of the parallel CmpMap must be
copied from a contiguous block at the beggining of the list of PermMap
inputs or must be assigned constant values. Also, there must be a
one-to-one correspondance between inputs and outputs in the PermMap.
Check that the first block of nin2a PermMap outputs are copied from
the last block of nin2a PermMap inputs (and vica-versa) or are constant. */
canswap = ( npin == npout );
aconstants = ( outperm[ 0 ] < 0 );
for( i = 0, j = npin - nin2a; i < nin2a; i++, j++ ) {
if( aconstants ) {
if( outperm[ i ] >= 0 ) {
canswap = 0;
break;
}
} else if( outperm[ i ] != j || inperm[ j ] != i ) {
canswap = 0;
break;
}
}
/* Check that the first block of nin2b PermMap inputs are copied from
the last block of nin2b PermMap outputs, and vica-versa. */
bconstants = ( outperm[ nin2a ] < 0 );
for( i = 0, j = npout - nin2b; i < nin2b; i++, j++ ) {
if( bconstants ) {
if( outperm[ j ] >= 0 ) {
canswap = 0;
break;
}
} else if( inperm[ i ] != j || outperm[ j ] != i ) {
canswap = 0;
break;
}
}
/* If the Mappings can be swapped.. */
new_pm = NULL;
new_cm = NULL;
qa = NULL;
qb = NULL;
if( canswap ) {
/* Temporarily set the Invert attributes of the component Mappings to the
values they had when the CmpMap was created. */
invert2a = astGetInvert( cmpmap2->map1 );
invert2b = astGetInvert( cmpmap2->map2 );
astSetInvert( cmpmap2->map1, cmpmap2->invert1 );
astSetInvert( cmpmap2->map2, cmpmap2->invert2 );
/* If any PermMap outputs are constant, we will need the results of
transforming these constants using the CmpMap which follows. */
if( aconstants || bconstants ) {
/* Transform a set of bad inputs using the PermMap. This will assign the
PermMap constant to any fixed outputs. */
p = astMalloc( sizeof( double )*(size_t) npin );
q = astMalloc( sizeof( double )*(size_t) npout );
qa = astMalloc( sizeof( double )*(size_t) nout2a );
qb = astMalloc( sizeof( double )*(size_t) nout2b );
if( astOK ) {
for( i = 0; i < npin; i++ ) p[ i ] = AST__BAD;
astTranN( permmap1, 1, npin, 1, p, 1, npout, 1, q );
/* Transform the PermMap outputs using the two component Mappings in the
CmpMap. */
astTranN( cmpmap2->map1, 1, nin2a, 1, q, 1, nout2a, 1, qa );
astTranN( cmpmap2->map2, 1, nin2b, 1, q + nin2a, 1, nout2b, 1, qb );
}
p = astFree( p );
q = astFree( q );
}
/* If necessary, create a UnitMap to replace a Mapping which has constant
outputs. The number of axes for the UnitMap is chosen to give the
correct total number of inputs for the final parallel CmpMap. At the
same time determine the number of inputs needed by the final PermMap. */
if( aconstants ) {
nunit = npin - nin2b;
npin_new = nout2b + nunit;
} else if( bconstants ) {
nunit = npin - nin2a;
npin_new = nout2a + nunit;
} else {
nunit = 0;
npin_new = nout2a + nout2b;
}
unit = nunit ? astUnitMap( nunit, "", status ) : NULL;
/* Determine the number of outputs for the final PermMap and allocate memory
for its permutation arrays. */
npout_new = nout2a + nout2b;
outperm_new = astMalloc( sizeof( int )*(size_t) npout_new );
inperm_new = astMalloc( sizeof( int )*(size_t) npin_new );
const_new = astMalloc( sizeof( double )*(size_t) ( npout_new + npin_new ) );
if( astOK ) {
oconid = 0;
/* First assign permutations for the second component Mapping, if used. */
if( !bconstants ) {
for( i = 0, j = npout_new - nout2b; i < nout2b; i++,j++ ) {
inperm_new[ i ] = j;
outperm_new[ j ] = i;
}
/* Otherwise, store constants */
} else {
for( i = 0; i < nunit; i++ ){
iconid = inperm[ i ];
if( iconid >= npout ) {
inperm_new[ i ] = npout_new;
} else if( iconid >= 0 ) {
astError( AST__INTER, "astMapMerge(CmpMap): Swapped PermMap "
"input is not constant (internal AST programming "
"error)." , status);
break;
} else {
inperm_new[ i ] = --oconid;
const_new[ -( oconid + 1 ) ] = conperm[ -( iconid + 1 ) ];
}
}
for( i = 0, j = npout_new - nout2b; i < nout2b; i++,j++ ) {
outperm_new[ j ] = --oconid;
const_new[ -( oconid + 1 ) ] = qb[ i ];
}
}
/* Now assign permutations for the first component Mapping, if used. */
if( !aconstants ) {
for( i = 0, j = npin_new - nout2a; i < nout2a; i++,j++ ) {
inperm_new[ j ] = i;
outperm_new[ i ] = j;
}
/* Otherwise, store constants */
} else {
for( i = nout2b; i < npin_new; i++ ){
iconid = inperm[ i - nout2b + nin2b ];
if( iconid >= npout ) {
inperm_new[ i ] = npout_new;
} else if( iconid >= 0 ) {
astError( AST__INTER, "astMapMerge(CmpMap): Swapped PermMap "
"input is not constant (internal AST programming "
"error)." , status);
break;
} else {
inperm_new[ i ] = --oconid;
const_new[ -( oconid + 1 ) ] = conperm[ -( iconid + 1 ) ];
}
}
for( i = 0; i < nout2a; i++ ) {
outperm_new[ i ] = --oconid;
const_new[ -( oconid + 1 ) ] = qa[ i ];
}
}
/* Create the new PermMap */
new_pm = astPermMap( npin_new, inperm_new, npout_new,
outperm_new, const_new, "", status );
/* Create the new CmpMap.*/
if( aconstants ) {
if( unit ) {
new_cm = astCmpMap( cmpmap2->map2, unit, 0, "", status );
} else {
new_cm = astCopy( cmpmap2->map2 );
}
} else if( bconstants ) {
if( unit ) {
new_cm = astCmpMap( unit, cmpmap2->map1, 0, "", status );
} else {
new_cm = astCopy( cmpmap2->map1 );
}
} else{
new_cm = astCmpMap( cmpmap2->map2, cmpmap2->map1, 0, "", status );
}
}
/* Free Memory. */
if( unit ) unit = astAnnul( unit );
outperm_new = astFree( outperm_new );
inperm_new = astFree( inperm_new );
const_new = astFree( const_new );
if( aconstants || bconstants ) {
qa = astFree( qa );
qb = astFree( qb );
}
/* Re-instate the original Invert attributes in the component Mappings. */
astSetInvert( cmpmap2->map1, invert2a );
astSetInvert( cmpmap2->map2, invert2b );
}
/* Release the arrays holding the input and output permutation arrays
and constants copied from the PermMap. */
inperm = astFree( inperm );
outperm = astFree( outperm );
conperm = astFree( conperm );
/* Re-instate the original values of the Invert attributes of both
Mappings. */
astSetInvert( permmap1, invert1 );
astSetInvert( cmpmap2, invert2 );
/* If the Mappings can be swapped... */
if( astOK && canswap ) {
/* Annul the supplied pointer to the two Mappings. */
( *map_list )[ imap1 ] = astAnnul( ( *map_list )[ imap1 ] );
( *map_list )[ imap2 ] = astAnnul( ( *map_list )[ imap2 ] );
/* Store the new PermMap pointer in the slot previously occupied by the
nominated CmpMap pointer. Likewise, store the invert flag. */
( *map_list )[ imap2 ] = (AstMapping *) new_pm;
( *invert_list )[ imap2 ] = astGetInvert( new_pm );
/* Store the new PermMap pointer in the slot previously occupied by the
nominated CmpMap pointer. Likewise, store the invert flag. */
( *map_list )[ imap1 ] = (AstMapping *) new_cm;
( *invert_list )[ imap1 ] = astGetInvert( new_cm );
/* Return the index of the first modified element. */
result = imap1;
}
}
}
/* If an error occurred, clear the result value. */
if ( !astOK ) result = -1;
/* Return the result. */
return result;
}
static int *MapSplit1( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ){
/*
* Name:
* MapSplit1
* Purpose:
* Create a Mapping representing a subset of the inputs of an existing
* Mapping.
* Type:
* Private function.
* Synopsis:
* #include "cmpmap.h"
* int *MapSplit1( AstMapping *this, int nin, const int *in, AstMapping **map )
* Class Membership:
* CmpMap method
* Description:
* This function performs the work for the astMapSplit method. It
* first invokes the astMapSplit method to see if the forward
* transformation of the supplied Mapping (not necessarily a CmpMap)
* can be split as requested. If this is not possible it invokes MapSplit2
* which attempts an inverse approach to the problem. For each possible
* sub-sets of the Mapping outputs it call astMapSplit to see if the
* sub-set of outputs are generated from the selected inputs.
* Parameters:
* this
* Pointer to the Mapping to be split. It is not assumed to be a CmpMap.
* nin
* The number of inputs to pick from "this".
* in
* Pointer to an array of indices (zero based) for the inputs which
* are to be picked. This array should have "nin" elements. If "Nin"
* is the number of inputs of the supplied Mapping, then each element
* should have a value in the range zero to Nin-1.
* map
* Address of a location at which to return a pointer to the new
* Mapping. This Mapping will have "nin" inputs (the number of
* outputs may be different to "nin"). A NULL pointer will be
* returned if the supplied Mapping has no subset of outputs which
* depend only on the selected inputs.
* Returned Value:
* A pointer to a dynamically allocated array of ints. The number of
* elements in this array will equal the number of outputs for the
* returned Mapping. Each element will hold the index of the
* corresponding output in the supplied Mapping. The array should be
* freed using astFree when no longer needed. A NULL pointer will
* be returned if no output Mapping can be created.
* Notes:
* - If this function is invoked with the global error status set,
* or if it should fail for any reason, then NULL values will be
* returned as the function value and for the "map" pointer.
*/
/* Local Variables: */
int *result; /* Axis order to return */
/* Initialise */
result = NULL;
*map = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* First see if the forward transformation can be split as requested. */
result = astMapSplit( this, nin, in, map );
/* If forward transformation could not be split, we attempt to split the
inverse transformation by selecting every possible sub-set of Mapping
outputs until one is found which is fed by the requested mapping inputs. */
if( !result ) result = MapSplit2( this, nin, in, map, status );
/* Free returned resources if an error has occurred. */
if( !astOK ) {
result = astFree( result );
*map = astAnnul( *map );
}
/* Return the list of output indices. */
return result;
}
static int *MapSplit2( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ){
/*
* Name:
* MapSplit2
* Purpose:
* Create a Mapping representing a subset of the inputs of an existing
* Mapping.
* Type:
* Private function.
* Synopsis:
* #include "cmpmap.h"
* int *MapSplit2( AstMapping *this, int nin, const int *in, AstMapping **map )
* Class Membership:
* CmpMap method
* Description:
* This function attempts to split the supplied Mapping using an
* inverse approach to the problem. For each possible sub-sets of the
* Mapping outputs it call astMapSplit to see if the sub-set of outputs
* are generated from the selected inputs.
* Parameters:
* this
* Pointer to the Mapping to be split. It is not assumed to be a CmpMap.
* nin
* The number of inputs to pick from "this".
* in
* Pointer to an array of indices (zero based) for the inputs which
* are to be picked. This array should have "nin" elements. If "Nin"
* is the number of inputs of the supplied Mapping, then each element
* should have a value in the range zero to Nin-1.
* map
* Address of a location at which to return a pointer to the new
* Mapping. This Mapping will have "nin" inputs (the number of
* outputs may be different to "nin"). A NULL pointer will be
* returned if the supplied Mapping has no subset of outputs which
* depend only on the selected inputs.
* Returned Value:
* A pointer to a dynamically allocated array of ints. The number of
* elements in this array will equal the number of outputs for the
* returned Mapping. Each element will hold the index of the
* corresponding output in the supplied Mapping. The array should be
* freed using astFree when no longer needed. A NULL pointer will
* be returned if no output Mapping can be created.
* Notes:
* - If this function is invoked with the global error status set,
* or if it should fail for any reason, then NULL values will be
* returned as the function value and for the "map" pointer.
*/
/* Local Variables: */
AstMapping *map2; /* Subset Mapping */
AstMapping *this2; /* Inverted copy of the supplied Mapping */
int *out; /* Selected output indices */
int *result; /* Axis order to return */
int *result2; /* Axis order for current output subset */
int i; /* Loop count */
int iscmp; /* Is "this" a CmpMap? */
int j; /* Loop count */
int mout; /* Number of selected outputs */
int nin2; /* Number of inputs fed by current outputs */
int nout; /* The number of outputs from the supplied Mapping */
int ok; /* Are all required inputs fed by current outputs? */
/* Initialise */
result = NULL;
*map = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Get the number of Mapping outputs. */
nout = astGetNout( this );
/* Get an inverted copy of the Mapping. We do this rather than inverting
the supplied Maping in case an error occurs which may leave the
supplied Mapping inverted. */
this2 = astCopy( this );
astInvert( this2 );
/* Note if the Mapping is a CmpMap. */
iscmp = astIsACmpMap( this );
/* Allocate memory to hold the selected output indices. */
out = astMalloc( nout*sizeof( int ) );
/* Loop round all useful subset sizes. */
if( out ) {
for( mout = 1; mout < nout && !result; mout++ ) {
/* Initialise the first subset of outputs to check at the current subset
size. */
for( i = 0; i < mout; i++ ) out[ i ] = 0;
/* Loop round all ways of picking a subset of "mout" outputs from the total
available "nout" outputs. */
while( ! result ) {
/* Skip this subset if it refers to any axis index more than once. */
ok = 1;
for( i = 1; i < mout && ok; i++ ) {
for( j = 0; j < i; j++ ) {
if( out[ i ] == out[ j ] ) {
ok = 0;
break;
}
}
}
if( ok ) {
/* Attempt to split the inverted Mapping using the current subset of
outputs. Take care to avoid an infinite loop if "this" is a CmpMap. */
if( iscmp ) {
result2 = MapSplit0( this2, mout, out, &map2, 1, status );
} else {
result2 = astMapSplit( this2, mout, out, &map2 );
}
/* If succesful... */
if( result2 ) {
/* See if the inputs that feed the current subset of outputs are the same
as the inputs specified by the caller (and in the same order). */
nin2 = astGetNout( map2 );
ok = ( nin2 == nin );
if( ok ) {
for( i = 0; i < nin; i++ ) {
if( in[ i ] != result2[ i ] ) {
ok = 0;
break;
}
}
}
/* If so, set up the values returned to the caller. */
if( ok ) {
result = astStore( result, out, mout*sizeof(int) );
astInvert( map2 );
*map = astClone( map2 );
}
/* Free resources. */
result2 = astFree( result2 );
map2 = astAnnul( map2 );
}
}
/* Increment the first axis index. */
i = 0;
out[ i ]++;
/* If the incremented axis index is now too high, reset it to zero and
increment the next higher axis index. Do this until an incremented axis
index is not too high. */
while( out[ i ] == nout ) {
out[ i++ ] = 0;
if( i < mout ) {
out[ i ]++;
} else {
break;
}
}
/* If all subsets have been checked break out of the loop. */
if( i == mout ) break;
}
}
}
/* Free resources. */
out = astFree( out );
this2 = astAnnul( this2 );
/* Free returned resources if an error has occurred. */
if( !astOK ) {
result = astFree( result );
*map = astAnnul( *map );
}
/* Return the list of output indices. */
return result;
}
static int *MapSplit0( AstMapping *this_mapping, int nin, const int *in,
AstMapping **map, int reentry, int *status ){
/*
* Name:
* MapSplit0
* Purpose:
* Create a Mapping representing a subset of the inputs of an existing
* CmpMap.
* Type:
* Private function.
* Synopsis:
* #include "cmpmap.h"
* int *MapSplit0( AstMapping *this, int nin, const int *in,
* AstMapping **map, int reentry, int *status )
* Class Membership:
* CmpMap method
* Description:
* This function creates a new Mapping by picking specified inputs from
* an existing CmpMap. This is only possible if the specified inputs
* correspond to some subset of the CmpMap outputs. That is, there
* must exist a subset of the CmpMap outputs for which each output
* depends only on the selected CmpMap inputs, and not on any of the
* inputs which have not been selected. If this condition is not met
* by the supplied CmpMap, then a NULL Mapping is returned.
* Parameters:
* this
* Pointer to the CmpMap to be split (the CmpMap is not actually
* modified by this function).
* nin
* The number of inputs to pick from "this".
* in
* Pointer to an array of indices (zero based) for the inputs which
* are to be picked. This array should have "nin" elements. If "Nin"
* is the number of inputs of the supplied CmpMap, then each element
* should have a value in the range zero to Nin-1.
* map
* Address of a location at which to return a pointer to the new
* Mapping. This Mapping will have "nin" inputs (the number of
* outputs may be different to "nin"). A NULL pointer will be
* returned if the supplied CmpMap has no subset of outputs which
* depend only on the selected inputs.
* reentry
* Set to zero if this is a top level entry, and non-zero if it is
* a recursive entry.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to a dynamically allocated array of ints. The number of
* elements in this array will equal the number of outputs for the
* returned Mapping. Each element will hold the index of the
* corresponding output in the supplied CmpMap. The array should be
* freed using astFree when no longer needed. A NULL pointer will
* be returned if no output Mapping can be created.
* Notes:
* - If this function is invoked with the global error status set,
* or if it should fail for any reason, then NULL values will be
* returned as the function value and for the "map" pointer.
*/
/* Local Variables: */
AstCmpMap *this;
AstMapping **map_list;
AstMapping *amap;
AstMapping *bmap;
AstPermMap *pmap;
int *aout;
int *cin;
int *cout;
int *inp;
int *invert_list;
int *outp;
int *p;
int *result;
int doperm;
int i;
int ibot;
int ibotout;
int iin;
int imap;
int iout;
int itop;
int j;
int naout;
int ncin;
int ncout;
int nmap;
int npin;
int npout;
int ok;
int old_inv;
int t;
/* Initialise */
result = NULL;
*map = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Get a pointer to the CmpMap structure. */
this = (AstCmpMap *) this_mapping;
/* Get the number of inputs and outputs in the supplied CmpMap. */
npin = astGetNin( this );
npout = astGetNout( this );
/* Check all input axis indices are valid. */
ok = 1;
for( i = 0; i < nin; i++ ) {
if( in[ i ] < 0 || in[ i ] >= npin ) {
ok = 0;
break;
}
}
/* If OK, proceed. */
if( ok ) {
/* Initialise dynamic arrays of Mapping pointers and associated Invert
flags. */
nmap = 0;
map_list = NULL;
invert_list = NULL;
/* Decompose the CmpMap into a sequence of Mappings to be applied in
series or parallel, as appropriate, and an associated list of
Invert flags. */
(void) astMapList( this_mapping, this->series, astGetInvert( this ),
&nmap, &map_list, &invert_list );
/* First handle lists of Mapping in series. */
if( this->series ) {
/* Initialise the array of inputs to be split from the next component
Mapping. */
ncin = nin;
cin = astStore( NULL, in, sizeof( int )*nin );
/* Loop round all the component Mappings that are combined in series to form
the supplied CmpMap. */
for( imap = 0; imap < nmap && cin; imap++ ) {
/* Temporarily reset the Invert attribute within the commponent Mapping back
to the value it had when the CmpMap was created. */
old_inv = astGetInvert( map_list[ imap ] );
astSetInvert( map_list[ imap ], invert_list[ imap ] );
/* Attempt to split the component Mapping using the current list of
inputs. */
cout = MapSplit1( map_list[ imap ], ncin, cin, &amap, status );
/* If the split could be done... */
if( amap ) {
/* The outputs that correspond to the picked inputs become the inputs to
be picked from the next component Mapping. */
(void) astFree( cin );
cin = cout;
ncin = astGetNout( amap );
/* Combine the split Mapping in series with the earlier split Mappings. */
if( *map ) {
bmap = (AstMapping *) astCmpMap( *map, amap, 1, " ", status );
amap = astAnnul( amap );
(void) astAnnul( *map );
*map = bmap;
} else {
*map = amap;
}
/* If the split could not be done, free the array of Mapping inputs to
indicate that no more component Mappings need be checked. */
} else {
cin = astFree( cin );
cout = astFree( cout );
}
/* Re-instate the original value of the Invert attribute within the
commponent Mapping. */
astSetInvert( map_list[ imap ], old_inv );
}
/* Return the final array of output indices. */
result = cin;
/* Now handle lists of Mapping in parallel. */
} else {
/* Allocate work space. */
outp = astMalloc( sizeof(int)*(size_t)nin );
inp = astMalloc( sizeof(int)*(size_t)nin );
cin = astMalloc( sizeof(int)*(size_t)npin );
cout = astMalloc( sizeof(int)*(size_t)npout );
if( astOK ) {
/* The caller may have selected the Mapping inputs in any order, so we
need to create a PermMap which will permute the inputs from the
requested order to the order used by the CmpMap. First fill the outperm
work array with its own indices. */
for( i = 0; i < nin; i++ ) outp[ i ] = i;
/* Sort the outperm work array so that it accesses the array of input indices
in ascending order */
for( j = nin - 1; j > 0; j-- ) {
p = outp;
for( i = 0; i < j; i++,p++ ) {
if( in[ p[0] ] > in[ p[1] ] ) {
t = p[0];
p[0] = p[1];
p[1] = t;
}
}
}
/* Create the inperm array which is the inverse of the above outperm
array. Note if the permutation is necessary. */
doperm = 0;
for( i = 0; i < nin; i++ ) {
if( outp[ i ] != i ) doperm = 1;
inp[ outp[ i ] ] = i;
}
/* Create a PermMap which reorders the inputs into ascending order. */
pmap = doperm ? astPermMap( nin, inp, nin, outp, NULL, "", status ) : NULL;
/* Store the sorted input indices in the inp work array. */
for( i = 0; i < nin; i++ ) {
inp[ i ] = in[ outp[ i ] ];
}
/* Initialise the index within the supplied CmpMap of the last (highest)
input in the current component Mapping. */
itop = -1;
/* Initialise the index within the supplied CmpMap of the first (lowest)
output for the current component Mapping. */
ibotout = 0;
/* Initialise the index within the supplied CmpMap of the current picked input. */
iin = 0;
/* Initialise the index of the next returned output index. */
ncout = 0;
/* Loop round all the component Mappings that are combined in series to form
the supplied CmpMap. */
for( imap = 0; imap < nmap && cout; imap++ ) {
/* Temporarily reset the Invert attribute within the component Mapping back
to the value it had when the CmpMap was created. */
old_inv = astGetInvert( map_list[ imap ] );
astSetInvert( map_list[ imap ], invert_list[ imap ] );
/* Get the index within the supplied CmpMap of the first (lowest) input in
the current component Mapping. */
ibot = itop + 1;
/* Get the index within the supplied CmpMap of the last (highest) input in
the current component Mapping. */
itop += astGetNin( map_list[ imap ] );
/* Get the zero-based indicies of the required inputs that feed the current
component Mapping. */
ncin = 0;
while( iin < nin && inp[ iin ] <= itop ) {
cin[ ncin++ ] = inp[ iin++ ] - ibot;
}
/* Skip components from which no inputs are being picked. */
if( ncin > 0 ) {
/* Attempt to split the component Mapping using the current list of inputs. */
aout = MapSplit1( map_list[ imap ], ncin, cin, &amap,
status );
/* If successful... */
if( amap ) {
/* Correct the output indices so that they refer to the numbering scheme
of the total CmpMap, and append to the total list of output indices. */
naout = astGetNout( amap );
for( iout = 0; iout < naout; iout++ ) {
cout[ ncout++ ] = aout[ iout ] + ibotout;
}
/* Combine the split Mapping in parallel with the earlier split Mappings. */
if( *map ) {
bmap = (AstMapping *) astCmpMap( *map, amap, 0, " ",
status );
amap = astAnnul( amap );
(void) astAnnul( *map );
*map = bmap;
} else {
*map = amap;
}
/* If the component Mapping could not be split, free the cout array to
indicate that no more component Mappings need be considered. */
} else {
cout = astFree( cout );
}
/* Free remaining resources. */
aout = astFree( aout );
}
/* Update the index within the supplied CmpMap of the first (lowest) output in
the next component Mapping. */
ibotout += astGetNout( map_list[ imap ] );
/* Re-instate the original value of the Invert attribute within the
commponent Mapping. */
astSetInvert( map_list[ imap ], old_inv );
}
/* If the requested inputs could be split from the total CmpMap, add in any
PermMap needed to re-order the inputs. */
if( cout && ncout ){
if( doperm ) {
bmap = (AstMapping *) astCmpMap( pmap, *map, 1, "", status );
(void) astAnnul( *map );
*map = bmap;
}
/* Also return the list of output indices. */
result = cout;
cout = NULL;
}
/* Free remaining resources. */
if( pmap ) pmap = astAnnul( pmap );
}
outp = astFree( outp );
inp = astFree( inp );
cin = astFree( cin );
cout = astFree( cout );
}
/* Loop to annul all the Mapping pointers in the list. */
for ( i = 0; i < nmap; i++ ) map_list[ i ] = astAnnul( map_list[ i ] );
/* Free the dynamic arrays. */
map_list = astFree( map_list );
invert_list = astFree( invert_list );
}
/* Mappings that have no outputs cannot be used. */
if( !result && *map ) *map = astAnnul( *map );
/* If the above method failed to split the CmpMap, we attempt to split the
inverse transformation by selecting every possible sub-set of Mapping
outputs until one is found which is fed by the requested mapping inputs. */
if( !result && !reentry ) result = MapSplit2( this_mapping, nin, in, map,
status );
/* Free returned resources if an error has occurred. */
if( !astOK ) {
result = astFree( result );
*map = astAnnul( *map );
}
/* Return the list of output indices. */
return result;
}
static int *MapSplit( AstMapping *this, int nin, const int *in,
AstMapping **map, int *status ){
/*
* Name:
* MapSplit
* Purpose:
* Create a Mapping representing a subset of the inputs of an existing
* CmpMap.
* Type:
* Private function.
* Synopsis:
* #include "cmpmap.h"
* int *MapSplit( AstMapping *this, int nin, const int *in,
* AstMapping **map, int *status )
* Class Membership:
* CmpMap method (over-rides the protected astMapSplit method
* inherited from the Mapping class).
* Description:
* This function is the main entry point for the astMapSplit method.
* It is a simple wrapper for MapSplit0 which calls MapSplit0
* indicating that this is a top-level entry.
* Parameters:
* this
* Pointer to the CmpMap to be split (the CmpMap is not actually
* modified by this function).
* nin
* The number of inputs to pick from "this".
* in
* Pointer to an array of indices (zero based) for the inputs which
* are to be picked. This array should have "nin" elements. If "Nin"
* is the number of inputs of the supplied CmpMap, then each element
* should have a value in the range zero to Nin-1.
* map
* Address of a location at which to return a pointer to the new
* Mapping. This Mapping will have "nin" inputs (the number of
* outputs may be different to "nin"). A NULL pointer will be
* returned if the supplied CmpMap has no subset of outputs which
* depend only on the selected inputs.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to a dynamically allocated array of ints. The number of
* elements in this array will equal the number of outputs for the
* returned Mapping. Each element will hold the index of the
* corresponding output in the supplied CmpMap. The array should be
* freed using astFree when no longer needed. A NULL pointer will
* be returned if no output Mapping can be created.
* Notes:
* - If this function is invoked with the global error status set,
* or if it should fail for any reason, then NULL values will be
* returned as the function value and for the "map" pointer.
*/
return MapSplit0( this, nin, in, map, 0, status );
}
static int PatternCheck( int val, int check, int **list, int *list_len, int *status ){
/*
* Name:
* Looping
* Purpose:
* Check for repeating patterns in a set of integer values.
* Type:
* Private function.
* Synopsis:
* #include "cmpmap.h"
* int PatternCheck( int val, int nmap, int **mlist, int **nlist, int *list_len )
* Class Membership:
* CmpMap member function.
* Description:
* This function appends a supplied integer to a dynamic list, creating
* or expanding the list if necessary.It then optionally, check the
* list for evidence of repeating patterns. If such a pattern is
* found, its wavelength is returned.
* Parameters:
* val
* The integer value to add to the list.
* check
* Should a check for reating patterns be performed?
* list
* Address of a location at which is stored a pointer to an array
* holding the values supplied on previous invocations of this
* function. If a NULL pointer is supplied a new array is allocated.
* On exit, the supplied value is appended to the end of the array. The
* array is extended as necessary. The returned pointer should be
* freed using astFree when no longer needed.
* list_len
* Address of a location at which is stored the number of elements
* in the "list" array.
* Returned Value:
* A non-zero "wavelength" value is returned if there is a repeating
* pattern is found in the "list" array. Otherwise, zero is returned.
* The "wavelength" is the number of integer values which constitute a
* single instance of the pattern.
* Notes:
* - A value of 1 is returned if this function is invoked with the AST
* error status set, or if it should fail for any reason.
*/
/* Local Variables: */
int *wave[ 30 ]; /* Pointers to start of waves */
int iat; /* Index of elements added by this invocation */
int jat; /* Index of element condiered next */
int jlo; /* Earliest "mlist" entry to consider */
int k; /* Index of element within pattern */
int mxwave; /* Max pattern length to consider */
int iwave; /* Index of current wave */
int nwave; /* Number of waves required to mark a pattern */
int result; /* Returned flag */
int wavelen; /* Current pattern length */
/* Check the global status. */
if ( !astOK ) return 1;
/* Initialise */
result = 0;
/* If no array has been supplied, create a new array. */
if( !(*list) ) {
*list = astMalloc( 100*sizeof( int ) );
*list_len = 0;
}
/* Store the new value in the array, extending it if necessary. */
iat = (*list_len)++;
*list = astGrow( *list, *list_len, sizeof( int ) );
if( astOK ) {
(*list)[ iat ] = val;
/* If required, determine the maximum "wavelength" for looping patterns to be
checked, and store the earliest list entry to consider. We take 3 complete
patterns as evidence of looping, but we only do the check when the
list length is at least 30. */
if( check && *list_len > 29 ){
mxwave = iat/3;
if( mxwave > 50 ) mxwave = 50;
jlo = iat - 3*mxwave;
/* Search backwards from the end of "list" looking for the most recent
occurence of the supplied "val" value. Limit the search to
wavelengths of no more than the above limit. */
jat = iat - 1;
while( jat >= jlo ) {
if( (*list)[ jat ] == val ) {
/* When an earlier occurrence of "val" is found, see if the values
which precede it are the same as the values which precede the new
element if "list" added by this invocation. We use 3 complete
patterns as evidence of looping, unless the wavelength is 1 in which
case we use 30 patterns (this is because wavelengths of 1 can occur
in short sequences legitamately). */
wavelen = iat - jat;
if( wavelen == 1 ) {
nwave = 30;
if( nwave > iat ) nwave = iat;
} else {
nwave = 3;
}
if( nwave*wavelen <= *list_len ) {
result = wavelen;
wave[ 0 ] = *list + *list_len - wavelen;
for( iwave = 1; iwave < nwave; iwave++ ) {
wave[ iwave ] = wave[ iwave - 1 ] - wavelen;
}
for( k = 0; k < wavelen; k++ ) {
for( iwave = 1; iwave < nwave; iwave++ ) {
if( *wave[ iwave ] != *wave[ 0 ] ) {
result = 0;
break;
}
wave[ iwave ]++;
}
wave[ 0 ]++;
}
}
/* Break if we have found a repeating pattern. */
if( result ) break;
}
jat--;
}
}
}
if( !astOK ) result= 1;
/* Return the result.*/
return result;
}
static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){
/*
* Name:
* Rate
* Purpose:
* Calculate the rate of change of a Mapping output.
* Type:
* Private function.
* Synopsis:
* #include "cmpmap.h"
* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status )
* Class Membership:
* CmpMap member function (overrides the astRate method inherited
* from the Mapping class ).
* Description:
* This function returns the rate of change of a specified output of
* the supplied Mapping with respect to a specified input, at a
* specified input position.
* Parameters:
* this
* Pointer to the Mapping to be applied.
* at
* The address of an array holding the axis values at the position
* at which the rate of change is to be evaluated. The number of
* elements in this array should equal the number of inputs to the
* Mapping.
* ax1
* The index of the Mapping output for which the rate of change is to
* be found (output numbering starts at 0 for the first output).
* ax2
* The index of the Mapping input which is to be varied in order to
* find the rate of change (input numbering starts at 0 for the first
* input).
* status
* Pointer to the inherited status variable.
* Returned Value:
* The rate of change of Mapping output "ax1" with respect to input
* "ax2", evaluated at "at", or AST__BAD if the value cannot be
* calculated.
*/
/* Local Variables: */
AstMapping *c1;
AstMapping *c2;
AstCmpMap *map;
double result;
int old_inv1;
int old_inv2;
int nin1;
int nin2;
double *at2;
double r1;
double r2;
int nout1;
int i;
/* Check inherited status */
if( !astOK ) return AST__BAD;
/* Get a pointer to the CmpMap structure. */
map = (AstCmpMap *) this;
/* Note the current Invert flags of the two component Mappings. */
old_inv1 = astGetInvert( map->map1 );
old_inv2 = astGetInvert( map->map2 );
/* Temporarily reset them to the values they had when the CmpMap was
created. */
astSetInvert( map->map1, map->invert1 );
astSetInvert( map->map2, map->invert2 );
/* If the CmpMap itself has been inverted, invert the component Mappings.
Also note the order in which the Mappings should be applied if in series. */
if( !astGetInvert( this ) ) {
c1 = map->map1;
c2 = map->map2;
} else {
c1 = map->map2;
c2 = map->map1;
astInvert( c1 );
astInvert( c2 );
}
/* First deal with Mappings in series. */
if( map->series ) {
/* Get the number of inputs to the two component Mappings. */
nin1 = astGetNin( c1 );
nin2 = astGetNin( c2 );
/* Allocate workspace to hold the result of transforming the supplied "at"
position using the first component. */
at2 = astMalloc( sizeof( double )*(size_t) nin2 );
/* Transform the supplied "at" position using the first component. */
astTranN( c1, 1, nin1, 1, at, 1, nin2, 1, at2 );
/* The required rate of change is the sum of the products of the rate of
changes of the two component mappings, summed over all the output axes
of the first componment. */
result = 0.0;
for( i = 0; i < nin2; i++ ) {
/* Find the rate of change of output "i" of the first component with
respect to input "ax2" at the supplied "at" position. */
r1 = astRate( c1, at, i, ax2 );
/* Find the rate of change of output "ax1" of the second component with
respect to input "i" at the transformed "at2" position. */
r2 = astRate( c2, at2, ax1, i );
/* If both are good, increment the ryunning total by the product of the
two rates. Otherwise, break. */
if( r1 != AST__BAD && r2 != AST__BAD ) {
result += r1*r2;
} else {
result = AST__BAD;
break;
}
}
/* Free the workspace. */
at2 = astFree( at2 );
/* Now deal with Mappings in parallel. */
} else {
/* Get the number of inputs and outputs for the lower component Mappings. */
nin1 = astGetNin( map->map1 );
nout1 = astGetNout( map->map1 );
/* If both input and output relate to the lower component Mappings, use its
astRate method. */
if( ax1 < nout1 && ax2 < nin1 ) {
result = astRate( map->map1, at, ax1, ax2 );
/* If both input and output relate to the upper component Mappings, use its
astRate method. */
} else if( ax1 >= nout1 && ax2 >= nin1 ) {
result = astRate( map->map2, at + nin1, ax1 - nout1, ax2 - nin1 );
/* If input and output relate to different component Mappings, return
zero. */
} else {
result = 0.0;
}
}
/* Reinstate the original Invert flags of the component Mappings .*/
astSetInvert( map->map1, old_inv1 );
astSetInvert( map->map2, old_inv2 );
/* Return the result. */
return result;
}
static AstMapping *RemoveRegions( AstMapping *this_mapping, int *status ) {
/*
* Name:
* RemoveRegions
* Purpose:
* Remove any Regions from a Mapping.
* Type:
* Private function.
* Synopsis:
* #include "cmpmap.h"
* AstMapping *RemoveRegions( AstMapping *this, int *status )
* Class Membership:
* CmpMap method (over-rides the astRemoveRegions method inherited
* from the Mapping class).
* Description:
* This function searches the supplied Mapping (which may be a
* compound Mapping such as a CmpMap) for any component Mappings
* that are instances of the AST Region class. It then creates a new
* Mapping from which all Regions have been removed. If a Region
* cannot simply be removed (for instance, if it is a component of a
* parallel CmpMap), then it is replaced with an equivalent UnitMap
* in the returned Mapping.
*
* The implementation provided by the CmpMap class invokes the
* astRemoveRegions method on the two component Mappings, and joins
* the results together into a new CmpMap.
* Parameters:
* this
* Pointer to the original Region.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the modified mapping.
* Notes:
* - A NULL pointer value will be returned if this function is
* invoked with the AST error status set, or if it should fail for
* any reason.
*/
/* Local Variables: */
AstCmpMap *new; /* Pointer to new CmpMap */
AstCmpMap *this; /* Pointer to CmpMap structure */
AstMapping *newmap1; /* New first component Mapping */
AstMapping *newmap2; /* New second component Mapping */
AstMapping *result; /* Result pointer to return */
int nax; /* Number of Frame axes */
int unit1; /* Is new first Mapping a UnitMap? */
int unit2; /* Is new second Mapping a UnitMap? */
/* Initialise. */
result = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Get a pointer to the CmpMap. */
this = (AstCmpMap *) this_mapping;
/* Invoke the astRemoveRegions method on the two component Mappings. */
newmap1 = astRemoveRegions( this->map1 );
newmap2 = astRemoveRegions( this->map2 );
/* If neither component was modified, just return a clone of the supplied
pointer. */
if( this->map1 == newmap1 && this->map2 == newmap2 ) {
result = astClone( this );
/* Otherwise, we need to create a new Mapping to return. */
} else {
/* The implementation of the astRemoveRegions method provided by the
Region class returns a Frame rather than a UnitMap. But we need
Mappings here, not Frames. So if either of these new Mappings is
a Frame, replace it with an equivalent UnitMap. Also, get flags
indicating if either Mapping is a UnitMap.*/
if( astIsAFrame( newmap1 ) ) {
nax = astGetNin( newmap1 );
(void) astAnnul( newmap1 );
newmap1 = (AstMapping *) astUnitMap( nax, " ", status );
unit1 = 1;
} else {
unit1 = astIsAUnitMap( newmap1 );
}
if( astIsAFrame( newmap2 ) ) {
nax = astGetNin( newmap2 );
(void) astAnnul( newmap2 );
newmap2 = (AstMapping *) astUnitMap( nax, " ", status );
unit2 = 1;
} else {
unit2 = astIsAUnitMap( newmap2 );
}
/* First handle series CmpMaps. */
if( this->series ) {
/* Otherwise, if the second new Mapping is a UnitMap, return a copy of the
first new Mapping (with the original Invert attribute) since the second
one will have no effect. */
if( unit1 ) {
result = astCopy( newmap2 );
astSetInvert( result, this->invert2 );
if( astGetInvert( this ) ) astInvert( result );
/* Otherwise, if the second new Mapping is a UnitMap, return a copy of the
first new Mapping (with the original Invert attribute) since the second
one will have no effect. */
} else if( unit2 ) {
result = astCopy( newmap1 );
astSetInvert( result, this->invert1 );
if( astGetInvert( this ) ) astInvert( result );
/* If neither of the new Mappings is a UnitMap, return a new CmpMap
containing the two new Mappings. We take a deep copy of the supplied
CmpMap and then modify the Mappings os that we retain any extra
information (such as invert flags) in the supplied CmpMap. */
} else {
new = astCopy( this );
(void) astAnnul( new->map1 );
(void) astAnnul( new->map2 );
new->map1 = astClone( newmap1 );
new->map2 = astClone( newmap2 );
result = (AstMapping *) new;
}
/* Now handle parallel CmpMaps. */
} else {
/* If both new Mappings are UnitMaps, return an equivalent UnitMap. */
if( unit1 && unit2 ) {
result = (AstMapping *) astUnitMap( astGetNin( newmap1 ) +
astGetNin( newmap2 ), " ",
status );
/* Otherwise, return a new CmpMap containing the two new Mappings. */
} else {
new = astCopy( this );
(void) astAnnul( new->map1 );
(void) astAnnul( new->map2 );
new->map1 = astClone( newmap1 );
new->map2 = astClone( newmap2 );
result = (AstMapping *) new;
}
}
}
/* Free resources. */
newmap1 = astAnnul( newmap1 );
newmap2 = astAnnul( newmap2 );
/* Annul the returned Mapping if an error has occurred. */
if( !astOK ) result = astAnnul( result );
/* Return the result. */
return result;
}
static AstMapping *Simplify( AstMapping *this_mapping, int *status ) {
/*
* Name:
* Simplify
* Purpose:
* Simplify a Mapping.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* AstMapping *Simplify( AstMapping *this, int *status )
* Class Membership:
* CmpMap method (over-rides the astSimplify method inherited from
* the Mapping class).
* Description:
* This function simplifies a CmpMap to eliminate redundant
* computational steps, or to merge separate steps which can be
* performed more efficiently in a single operation.
* Parameters:
* this
* Pointer to the original Mapping.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A new pointer to the (possibly simplified) Mapping.
* Notes:
* - A NULL pointer value will be returned if this function is
* invoked with the AST error status set, or if it should fail for
* any reason.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstCmpMap *this; /* Pointer to CmpMap structure */
AstMapping **map_list; /* Mapping array pointer */
AstMapping *map; /* Pointer to cloned Mapping pointer */
AstMapping *result; /* Result pointer to return */
AstMapping *tmp; /* Temporary Mapping pointer */
int *invert_list; /* Invert array pointer */
int *mlist; /* Point to list of modified Mapping indices */
int *nlist; /* Point to list of Mapping counts */
int i; /* Loop counter for Mappings */
int improved; /* Simplification achieved? */
int invert; /* Invert attribute value */
int invert_n; /* Invert value for final Mapping */
int mlist_len; /* No. of entries in mlist */
int nlist_len; /* No. of entries in nlist */
int modified; /* Index of first modified Mapping */
int nmap; /* Mapping count */
int nominated; /* Index of nominated Mapping */
int set; /* Invert attribute set? */
int set_n; /* Invert set for final Mapping? */
int simpler; /* Simplification possible? */
int t; /* Temporary storage */
int wlen1; /* Pattern wavelength for "modified" values */
int wlen2; /* Pattern wavelength for "nmap" values */
/* Initialise. */
result = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(this_mapping);
/* It is possible for the astSimplify method to be called recursively from
within astSimplify. It is also possible that the Mapping being
simplified by the current invocation is the same as the Mapping being
simplified by some recursive invocation higher up the call stack. If
this happens we will get into an infinite loop, since we already know
that simplifying the supplied Mapping will involve (eventually) a
recursive call to astSimplify with the same Mapping. To avoid this
looping, we note the Mappings supplied at each depth and first compare
the supplied Mapping with the Mappings which are currently being
simplified higher up the call stack. If the supplied Mapping is
already being simplified at a higher level, then we return immediately
without doing any simplification. Otherwise, we record the supplied
Mapping pointer in a static list so that it is available to subsequent
recursive invocations of this function. First compare the supplied
Mapping with the Mappingsbeing simpliied higher up. Return without
action if a match is found. */
for( i = 0; i < simplify_depth; i++ ) {
if( astEqual( this_mapping, simplify_stackmaps[ i ] ) ) {
return astClone( this_mapping );
}
}
/* We have further work to do, so increment the recursion depth, extend
the simplify_stackmaps array, and store the new Mapping in it for future use. */
simplify_depth++;
simplify_stackmaps = astGrow( simplify_stackmaps, simplify_depth, sizeof( AstMapping * ) );
if( astOK ) {
simplify_stackmaps[ simplify_depth - 1 ] = astClone( this_mapping );
}
/* Obtain a pointer to the CmpMap structure. */
this = (AstCmpMap *) this_mapping;
/* Initialise dynamic arrays of Mapping pointers and associated Invert
flags. */
nmap = 0;
map_list = NULL;
invert_list = NULL;
/* Decompose the CmpMap into a sequence of Mappings to be applied in
series or parallel, as appropriate, and an associated list of
Invert flags. If any inverted CmpMaps are found in the Mapping, then
we can at least simplify the returned Mapping by swapping and
inverting the components. Set "simpler" to indicate this. */
simpler = astMapList( this_mapping, this->series, astGetInvert( this ), &nmap,
&map_list, &invert_list );
/* Each Mapping has a flag that indicates if the mapping is frozen (i.e. cannot
be nominated for simplification). Mappings become frozen if nominating them
would create an infinite loop in which neighbouring mappings argue as to
their form. Freezing a mapping prevents the frozen mapping contributing any
further to the argument, so the other Mapping "wins" the argument.
Ensure no Mappings are frozen to begin with. */
for( i = 0; i < nmap; i++ ) {
map_list[ i ]->flags &= ~AST__FROZEN_FLAG;
}
/* Initialise pointers to memory used to hold lists of the modified
Mapping index and the number of mappings after each call of
astMapMerge. */
mlist = NULL;
nlist = NULL;
/* Loop to simplify the sequence until a complete pass through it has
been made without producing any improvement. */
improved = 1;
while ( astOK && improved ) {
improved = 0;
/* Loop to nominate each Mapping in the sequence in turn. */
nominated = 0;
while ( astOK && ( nominated < nmap ) ) {
/* If the current nominated mapping has been frozen, then we do not allow
it to suggest changes to the mapping sequence. Instead, just increment
the index of the next mapping to be checked and continue on to the next
pass round the while loop. */
if( map_list[ nominated ]->flags & AST__FROZEN_FLAG ) {
nominated++;
continue;
}
/* Clone a pointer to the nominated Mapping and attempt to merge it
with its neighbours. Annul the cloned pointer afterwards. */
map = astClone( map_list[ nominated ] );
modified = astMapMerge( map, nominated, this->series,
&nmap, &map_list, &invert_list );
map = astAnnul( map );
/* Move on to nominate the next Mapping in the sequence. */
nominated++;
/* Note if any simplification occurred above. */
if( modified >= 0 ) {
/* Append the index of the first modified Mapping in the list and and check
that there is no repreating pattern in the list. If there is, we are
probably in a loop where one mapping class is making a change, and another
is undoing the change. The Looping function returns the "wavelength"
of any pattern found. If a pattern was discovered, we ignore it unless
there is also a pattern in the "nmap" values - the wavelengths of the
two patterns must be related by a integer factor. */
wlen1 = PatternCheck( modified, 1, &mlist, &mlist_len, status );
wlen2 = PatternCheck( nmap, wlen1, &nlist, &nlist_len, status );
if( wlen1 && wlen2 ) {
/* Ensure wlen2 is larger than or equal to wlen1. */
if( wlen1 > wlen2 ) {
t = wlen1;
wlen1 = wlen2;
wlen2 = t;
}
/* See if wlen2 is an integer multiple of wlen1. If not, ignore the
patterns. */
if( ( wlen2 % wlen1 ) != 0 ) wlen1 = 0;
}
/* If a repeating pattern is occurring, set the frozen flag in order to
prevent the modified mapping from being modified any more. */
if( wlen1 > 0 ) {
map_list[ modified ]->flags |= AST__FROZEN_FLAG;
/* Otherwise, indicate we have improved the mapping and go round to test
the next nominated mapping. */
} else {
improved = 1;
simpler = 1;
/* If the simplification resulted in modification of an earlier
Mapping than would normally be considered next, then go back to
consider the modified one first. */
if ( modified < nominated ) nominated = modified;
}
}
}
}
/* Free resources */
mlist = astFree( mlist );
nlist = astFree( nlist );
/* Construct the output Mapping. */
/* ============================= */
/* If no simplification occurred above, then simply clone a pointer to
the original Mapping. */
if ( astOK ) {
if ( !simpler ) {
result = astClone( this );
/* Otherwise, we must construct the result from the contents of the
Mapping list. */
} else {
/* If the simplified Mapping list has only a single element, then the
output Mapping will not be a CmpMap. In this case, we cannot
necessarily set the Invert flag of the Mapping to the value we want
(because we must not modify the Mapping itself. */
if ( nmap == 1 ) {
/* We must make a copy. Cloning is no good (even if the Mapping already
has the Invert attribute value we want), since we want the returned
Mapping to be independent of the original component Mappings, so that
if user code inverts a component Mapping (via some other pre-existing
pointer), the returned simplified Mapping is not affected. */
result = astCopy( map_list[ 0 ] );
/* Either clear the copy's Invert attribute, or set it to 1, as
required. */
if ( invert_list[ 0 ] ) {
astSetInvert( result, 1 );
} else {
astClearInvert( result );
}
/* If the simplified Mapping sequence has more than one element, the
output Mapping will be a CmpMap. In this case, we can set each
individual Mapping element to have the Invert attribute value we
want, so long as we return these attribute values to their original
state again afterwards (once a Mapping is encapsulated inside a
CmpMap, further external changes to its Invert attribute do not
affect the behaviour of the CmpMap). */
} else {
/* Determine if the Invert attribute for the last Mapping is set, and
obtain its value. */
set_n = astTestInvert( map_list[ nmap - 1 ] );
invert_n = astGetInvert( map_list[ nmap - 1 ] );
/* Set this attribute to the value we want. */
astSetInvert( map_list[ nmap - 1 ], invert_list[ nmap - 1 ] );
/* Loop through the Mapping sequence in reverse to merge it into an
equivalent CmpMap. */
for ( i = nmap - 1; i >= 0; i-- ) {
/* Simply clone the pointer to the last Mapping in the sequence (which
will be encountered first). */
if ( !result ) {
result = astClone( map_list[ i ] );
/* For subsequent Mappings, test if the Invert attribute is set and
save its value. */
} else {
set = astTestInvert( map_list[ i ] );
invert = astGetInvert( map_list[ i ] );
/* Set this attribute to the value required. */
astSetInvert( map_list[ i ], invert_list[ i ] );
/* Combine the Mapping with the CmpMap formed so far and replace the
result pointer with the new pointer this produces, annulling the
previous pointer. */
tmp = (AstMapping *) astCmpMap( map_list[ i ], result,
this->series, "", status );
(void) astAnnul( result );
result = tmp;
/* Restore the Invert attribute of the Mapping to its original
state. */
if ( !set ) {
astClearInvert( map_list[ i ] );
} else {
astSetInvert( map_list[ i ], invert );
}
}
}
/* When all the Mappings have been merged into the CmpMap, restore the
state of the Invert attribute for the final Mapping in the
sequence. */
if ( !set_n ) {
astClearInvert( map_list[ nmap - 1 ] );
} else {
astSetInvert( map_list[ nmap - 1 ], invert_n );
}
}
}
}
/* Clean up. */
/* ========= */
/* Loop to annul all the Mapping pointers in the simplified list. */
for ( i = 0; i < nmap; i++ ) map_list[ i ] = astAnnul( map_list[ i ] );
/* Free the dynamic arrays. */
map_list = astFree( map_list );
invert_list = astFree( invert_list );
/* Decrement the recursion depth and free the pointer to the supplied
Mapping currently stored at the end of the simplify_stackmaps array. */
simplify_depth--;
if( astOK ) {
simplify_stackmaps[ simplify_depth ] = astAnnul( simplify_stackmaps[ simplify_depth ] );
}
/* If we are now at depth zero, free the simplify_stackmaps array. */
if( simplify_depth == 0 ) simplify_stackmaps = astFree( simplify_stackmaps );
/* If an error occurred, annul the returned Mapping. */
if ( !astOK ) result = astAnnul( result );
/* Return the result. */
return result;
}
static AstPointSet *Transform( AstMapping *this, AstPointSet *in,
int forward, AstPointSet *out, int *status ) {
/*
* Name:
* Transform
* Purpose:
* Apply a CmpMap to transform a set of points.
* Type:
* Private function.
* Synopsis:
* #include "cmpmap.h"
* AstPointSet *Transform( AstMapping *this, AstPointSet *in,
* int forward, AstPointSet *out, int *status )
* Class Membership:
* CmpMap member function (over-rides the astTransform method inherited
* from the Mapping class).
* Description:
* This function takes a CmpMap and a set of points encapsulated in a
* PointSet and transforms the points so as to apply the required Mapping.
* This implies applying each of the CmpMap's component Mappings in turn,
* either in series or in parallel.
* Parameters:
* this
* Pointer to the CmpMap.
* in
* Pointer to the PointSet associated with the input coordinate values.
* forward
* A non-zero value indicates that the forward coordinate transformation
* should be applied, while a zero value requests the inverse
* transformation.
* out
* Pointer to a PointSet which will hold the transformed (output)
* coordinate values. A NULL value may also be given, in which case a
* new PointSet will be created by this function.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the output (possibly new) PointSet.
* 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.
* - The number of coordinate values per point in the input PointSet must
* match the number of coordinates for the CmpMap being applied.
* - If an output PointSet is supplied, it must have space for sufficient
* number of points and coordinate values per point to accommodate the
* result. Any excess space will be ignored.
*/
/* Local Variables: */
AstCmpMap *map; /* Pointer to CmpMap to be applied */
AstPointSet *result; /* Pointer to output PointSet */
AstPointSet *temp1; /* Pointer to temporary PointSet */
AstPointSet *temp2; /* Pointer to temporary PointSet */
AstPointSet *temp; /* Pointer to temporary PointSet */
int forward1; /* Use forward direction for Mapping 1? */
int forward2; /* Use forward direction for Mapping 2? */
int ipoint1; /* Index of first point in batch */
int ipoint2; /* Index of last point in batch */
int nin1; /* No. input coordinates for Mapping 1 */
int nin2; /* No. input coordinates for Mapping 2 */
int nin; /* No. input coordinates supplied */
int nout1; /* No. output coordinates for Mapping 1 */
int nout2; /* No. output coordinates for Mapping 2 */
int nout; /* No. output coordinates supplied */
int np; /* Number of points in batch */
int npoint; /* Number of points to be transformed */
/* Local Constants: */
const int nbatch = 2048; /* Maximum points in a batch */
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Obtain a pointer to the CmpMap. */
map = (AstCmpMap *) this;
/* Apply the parent Mapping using the stored pointer to the Transform member
function inherited from the parent Mapping class. This function validates
all arguments and generates an output PointSet if necessary, but does not
actually transform any coordinate values. */
result = (*parent_transform)( this, in, forward, out, status );
/* We now extend the parent astTransform method by applying the component
Mappings of the CmpMap to generate the output coordinate values. */
/* Determine whether to apply the forward or inverse Mapping, according to the
direction specified and whether the Mapping has been inverted. */
if ( astGetInvert( map ) ) forward = !forward;
/* Check if either component Mapping's inversion flag has changed since it was
used to construct the CmpMap. Set a "forward" flag for each Mapping to
change the direction we will use, to compensate if necessary. (Such changes
may have occurred if other pointers to the component Mappings are in
circulation). */
forward1 = forward;
forward2 = forward;
if ( map->invert1 != astGetInvert( map->map1 ) ) forward1 = !forward1;
if ( map->invert2 != astGetInvert( map->map2 ) ) forward2 = !forward2;
/* Determine the number of points being transformed. */
npoint = astGetNpoint( in );
/* Mappings in series. */
/* ------------------- */
/* If required, use the two component Mappings in series. To do this, we must
apply one Mapping followed by the other, which means storing an intermediate
result. Since this function may be invoked recursively and have to store an
intermediate result on each occasion, the memory required may become
excessive when transforming large numbers of points. To overcome this, we
split the points up into smaller batches. */
if ( astOK ) {
if ( map->series ) {
/* Obtain the numbers of input and output coordinates. */
nin = astGetNcoord( in );
nout = astGetNcoord( result );
/* Loop to process all the points in batches, of maximum size nbatch points. */
for ( ipoint1 = 0; ipoint1 < npoint; ipoint1 += nbatch ) {
/* Calculate the index of the final point in the batch and deduce the number of
points (np) to be processed in this batch. */
ipoint2 = ipoint1 + nbatch - 1;
if ( ipoint2 > npoint - 1 ) ipoint2 = npoint - 1;
np = ipoint2 - ipoint1 + 1;
/* Create temporary PointSets to describe the input and output points for this
batch. */
temp1 = astPointSet( np, nin, "", status );
temp2 = astPointSet( np, nout, "", status );
/* Associate the required subsets of the input and output coordinates with the
two PointSets. */
astSetSubPoints( in, ipoint1, 0, temp1 );
astSetSubPoints( result, ipoint1, 0, temp2 );
/* Apply the two Mappings in sequence and in the required order and direction.
Store the intermediate result in a temporary PointSet (temp) which is
created by the first Mapping applied. */
if ( forward ) {
temp = astTransform( map->map1, temp1, forward1, NULL );
(void) astTransform( map->map2, temp, forward2, temp2 );
} else {
temp = astTransform( map->map2, temp1, forward2, NULL );
(void) astTransform( map->map1, temp, forward1, temp2 );
}
/* Delete the temporary PointSets after processing each batch of points. */
temp = astDelete( temp );
temp1 = astDelete( temp1 );
temp2 = astDelete( temp2 );
/* Quit processing batches if an error occurs. */
if ( !astOK ) break;
}
/* Mappings in parallel. */
/* --------------------- */
/* If required, use the two component Mappings in parallel. Since we do not
need to allocate any memory to hold intermediate coordinate values here,
there is no need to process the points in batches. */
} else {
/* Get the effective number of input and output coordinates per point for each
Mapping (taking account of the direction in which each will be used to
transform points). */
nin1 = forward1 ? astGetNin( map->map1 ) : astGetNout( map->map1 );
nout1 = forward1 ? astGetNout( map->map1 ) : astGetNin( map->map1 );
nin2 = forward2 ? astGetNin( map->map2 ) : astGetNout( map->map2 );
nout2 = forward2 ? astGetNout( map->map2 ) : astGetNin( map->map2 );
/* Create temporary PointSets to describe the input and output coordinates for
the first Mapping. */
temp1 = astPointSet( npoint, nin1, "", status );
temp2 = astPointSet( npoint, nout1, "", status );
/* Associate the required subsets of the input and output coordinates with
these PointSets. */
astSetSubPoints( in, 0, 0, temp1 );
astSetSubPoints( result, 0, 0, temp2 );
/* Use the astTransform method to apply the coordinate transformation described
by the first Mapping. */
(void) astTransform( map->map1, temp1, forward1, temp2 );
/* Delete the temporary PointSets. */
temp1 = astDelete( temp1 );
temp2 = astDelete( temp2 );
/* Create a new pair of temporary PointSets to describe the input and output
coordinates for the second Mapping, and associate the required subsets of
the input and output coordinates with these PointSets. */
temp1 = astPointSet( npoint, nin2, "", status );
temp2 = astPointSet( npoint, nout2, "", status );
astSetSubPoints( in, 0, nin1, temp1 );
astSetSubPoints( result, 0, nout1, temp2 );
/* Apply the coordinate transformation described by the second Mapping. */
(void) astTransform( map->map2, temp1, forward2, temp2 );
/* Delete the two temporary PointSets. */
temp1 = astDelete( temp1 );
temp2 = astDelete( temp2 );
}
}
/* If an error occurred, clean up by deleting the output PointSet (if
allocated by this function) and setting a NULL result pointer. */
if ( !astOK ) {
if ( !out ) result = astDelete( result );
result = NULL;
}
/* Return a pointer to the output PointSet. */
return result;
}
/* Copy constructor. */
/* ----------------- */
static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
/*
* Name:
* Copy
* Purpose:
* Copy constructor for CmpMap objects.
* Type:
* Private function.
* Synopsis:
* void Copy( const AstObject *objin, AstObject *objout, int *status )
* Description:
* This function implements the copy constructor for CmpMap objects.
* Parameters:
* objin
* Pointer to the object to be copied.
* objout
* Pointer to the object being constructed.
* status
* Pointer to the inherited status variable.
* Returned Value:
* void
* Notes:
* - This constructor makes a deep copy, including a copy of the component
* Mappings within the CmpMap.
*/
/* Local Variables: */
AstCmpMap *in; /* Pointer to input CmpMap */
AstCmpMap *out; /* Pointer to output CmpMap */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain pointers to the input and output CmpMaps. */
in = (AstCmpMap *) objin;
out = (AstCmpMap *) objout;
/* For safety, start by clearing any references to the input component
Mappings from the output CmpMap. */
out->map1 = NULL;
out->map2 = NULL;
/* Make copies of these Mappings and store pointers to them in the output
CmpMap structure. */
out->map1 = astCopy( in->map1 );
out->map2 = astCopy( in->map2 );
}
/* Destructor. */
/* ----------- */
static void Delete( AstObject *obj, int *status ) {
/*
* Name:
* Delete
* Purpose:
* Destructor for CmpMap objects.
* Type:
* Private function.
* Synopsis:
* void Delete( AstObject *obj, int *status )
* Description:
* This function implements the destructor for CmpMap objects.
* Parameters:
* obj
* Pointer to the object to be deleted.
* status
* Pointer to the inherited status variable.
* Returned Value:
* void
* Notes:
* This function attempts to execute even if the global error status is
* set.
*/
/* Local Variables: */
AstCmpMap *this; /* Pointer to CmpMap */
/* Obtain a pointer to the CmpMap structure. */
this = (AstCmpMap *) obj;
/* Annul the pointers to the component Mappings. */
this->map1 = astAnnul( this->map1 );
this->map2 = astAnnul( this->map2 );
/* Clear the remaining CmpMap variables. */
this->invert1 = 0;
this->invert2 = 0;
this->series = 0;
}
/* Dump function. */
/* -------------- */
static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
/*
* Name:
* Dump
* Purpose:
* Dump function for CmpMap 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 CmpMap class to an output Channel.
* Parameters:
* this
* Pointer to the CmpMap 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: */
AstCmpMap *this; /* Pointer to the CmpMap structure */
int ival; /* Integer value */
int set; /* Attribute value set? */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the CmpMap structure. */
this = (AstCmpMap *) this_object;
/* Write out values representing the instance variables for the CmpMap
class. Accompany these with appropriate comment strings, possibly
depending on the values being written.*/
/* In the case of attributes, we first use the appropriate (private)
Test... member function to see if they are set. If so, we then use
the (private) Get... function to obtain the value to be written
out.
For attributes which are not set, we use the astGet... method to
obtain the value instead. This will supply a default value
(possibly provided by a derived class which over-rides this method)
which is more useful to a human reader as it corresponds to the
actual default attribute value. Since "set" will be zero, these
values are for information only and will not be read back. */
/* Series. */
/* ------- */
ival = this->series;
set = ( ival == 0 );
astWriteInt( channel, "Series", set, 0, ival,
ival ? "Component Mappings applied in series" :
"Component Mappings applied in parallel" );
/* First Invert flag. */
/* ------------------ */
ival = this->invert1;
set = ( ival != 0 );
astWriteInt( channel, "InvA", set, 0, ival,
ival ? "First Mapping used in inverse direction" :
"First Mapping used in forward direction" );
/* Second Invert flag. */
/* ------------------- */
ival = this->invert2;
set = ( ival != 0 );
astWriteInt( channel, "InvB", set, 0, ival,
ival ? "Second Mapping used in inverse direction" :
"Second Mapping used in forward direction" );
/* First Mapping. */
/* -------------- */
astWriteObject( channel, "MapA", 1, 1, this->map1,
"First component Mapping" );
/* Second Mapping. */
/* --------------- */
astWriteObject( channel, "MapB", 1, 1, this->map2,
"Second component Mapping" );
}
/* Standard class functions. */
/* ========================= */
/* Implement the astIsACmpMap and astCheckCmpMap functions using the
macros defined for this purpose in the "object.h" header file. */
astMAKE_ISA(CmpMap,Mapping)
astMAKE_CHECK(CmpMap)
AstCmpMap *astCmpMap_( void *map1_void, void *map2_void, int series,
const char *options, int *status, ...) {
/*
*+
* Name:
* astCmpMap
* Purpose:
* Create a CmpMap.
* Type:
* Protected function.
* Synopsis:
* #include "cmpmap.h"
* AstCmpMap *astCmpMap( AstMapping *map1, AstMapping *map2, int series,
* const char *options, ... )
* Class Membership:
* CmpMap constructor.
* Description:
* This function creates a new CmpMap and optionally initialises its
* attributes.
* Parameters:
* map1
* Pointer to the first Mapping.
* map2
* Pointer to the second Mapping.
* series
* If a non-zero value is given, the two Mappings will be connected
* together in series. A zero value requests that they be connected in
* parallel.
* options
* Pointer to a null terminated string containing an optional
* comma-separated list of attribute assignments to be used for
* initialising the new CmpMap. The syntax used is the same as for the
* astSet method 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 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 method (and for the C "printf"
* function).
* Returned Value:
* A pointer to the new CmpMap.
* 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.
*-
* Implementation Notes:
* - This function implements the basic CmpMap constructor which is
* available via the protected interface to the CmpMap class. A
* public interface is provided by the astCmpMapId_ function.
* - Because this function has a variable argument list, it is
* invoked by a macro that evaluates to a function pointer (not a
* function invocation) and no checking or casting of arguments is
* performed before the function is invoked. Because of this, the
* "map1" and "map2" parameters are of type (void *) and are
* converted and validated within the function itself.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstCmpMap *new; /* Pointer to new CmpMap */
AstMapping *map1; /* Pointer to first Mapping structure */
AstMapping *map2; /* Pointer to second Mapping structure */
va_list args; /* Variable argument list */
/* Initialise. */
new = NULL;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(NULL);
/* Check the global status. */
if ( !astOK ) return new;
/* Obtain and validate pointers to the Mapping structures provided. */
map1 = astCheckMapping( map1_void );
map2 = astCheckMapping( map2_void );
if ( astOK ) {
/* Initialise the CmpMap, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitCmpMap( NULL, sizeof( AstCmpMap ), !class_init, &class_vtab,
"CmpMap", map1, map2, series );
/* 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 CmpMap'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 CmpMap. */
return new;
}
AstCmpMap *astCmpMapId_( void *map1_void, void *map2_void, int series,
const char *options, ... ) {
/*
*++
* Name:
c astCmpMap
f AST_CMPMAP
* Purpose:
* Create a CmpMap.
* Type:
* Public function.
* Synopsis:
c #include "cmpmap.h"
c AstCmpMap *astCmpMap( AstMapping *map1, AstMapping *map2, int series,
c const char *options, ... )
f RESULT = AST_CMPMAP( MAP1, MAP2, SERIES, OPTIONS, STATUS )
* Class Membership:
* CmpMap constructor.
* Description:
* This function creates a new CmpMap and optionally initialises
* its attributes.
*
* A CmpMap is a compound Mapping which allows two component
* Mappings (of any class) to be connected together to form a more
* complex Mapping. This connection may either be "in series"
* (where the first Mapping is used to transform the coordinates of
* each point and the second mapping is then applied to the
* result), or "in parallel" (where one Mapping transforms the
* earlier coordinates for each point and the second Mapping
* simultaneously transforms the later coordinates).
*
* Since a CmpMap is itself a Mapping, it can be used as a
* component in forming further CmpMaps. Mappings of arbitrary
* complexity may be built from simple individual Mappings in this
* way.
* Parameters:
c map1
f MAP1 = INTEGER (Given)
* Pointer to the first component Mapping.
c map2
f MAP2 = INTEGER (Given)
* Pointer to the second component Mapping.
c series
f SERIES = LOGICAL (Given)
c If a non-zero value is given for this parameter, the two
c component Mappings will be connected in series. A zero
c value requests that they are connected in parallel.
f If a .TRUE. value is given for this argument, the two
f component Mappings will be connected in series. A
f .FALSE. value requests that they are connected in parallel.
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 CmpMap. 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 CmpMap. 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 astCmpMap()
f AST_CMPMAP = INTEGER
* A pointer to the new CmpMap.
* Notes:
* - If the component Mappings are connected in series, then using
* the resulting CmpMap to transform coordinates will cause the
* first Mapping to be applied, followed by the second Mapping. If
* the inverse CmpMap transformation is requested, the two
* component Mappings will be applied in both the reverse order and
* the reverse direction.
* - When connecting two component Mappings in series, the number
* of output coordinates generated by the first Mapping (its Nout
* attribute) must equal the number of input coordinates accepted
* by the second Mapping (its Nin attribute).
* - If the component Mappings of a CmpMap are connected in
* parallel, then the first Mapping will be used to transform the
* earlier input coordinates for each point (and to produce the
* earlier output coordinates) and the second Mapping will be used
* simultaneously to transform the remaining input coordinates (to
* produce the remaining output coordinates for each point). If the
* inverse transformation is requested, each Mapping will still be
* applied to the same coordinates, but in the reverse direction.
* - When connecting two component Mappings in parallel, there is
* no restriction on the number of input and output coordinates for
* each Mapping.
c - Note that the component Mappings supplied are not copied by
c astCmpMap (the new CmpMap simply retains a reference to
c them). They may continue to be used for other purposes, but
c should not be deleted. If a CmpMap containing a copy of its
c component Mappings is required, then a copy of the CmpMap should
c be made using astCopy.
f - Note that the component Mappings supplied are not copied by
f AST_CMPMAP (the new CmpMap simply retains a reference to
f them). They may continue to be used for other purposes, but
f should not be deleted. If a CmpMap containing a copy of its
f component Mappings is required, then a copy of the CmpMap should
f be made using AST_COPY.
* - A null Object pointer (AST__NULL) will be returned if this
c function is invoked with the AST error status set, or if it
f function is invoked with STATUS set to an error value, or if it
* should fail for any reason.
*--
* Implementation Notes:
* - This function implements the external (public) interface to
* the astCmpMap constructor function. It returns an ID value
* (instead of a true C pointer) to external users, and must be
* provided because astCmpMap_ has a variable argument list which
* cannot be encapsulated in a macro (where this conversion would
* otherwise occur).
* - Because no checking or casting of arguments is performed
* before the function is invoked, the "map1" and "map2" parameters
* are of type (void *) and are converted from an ID value to a
* pointer and validated within the function itself.
* - The variable argument list also prevents this function from
* invoking astCmpMap_ directly, so it must be a re-implementation
* of it in all respects, except for the conversions between IDs
* and pointers on input/output of Objects.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstCmpMap *new; /* Pointer to new CmpMap */
AstMapping *map1; /* Pointer to first Mapping structure */
AstMapping *map2; /* Pointer to second Mapping structure */
va_list args; /* Variable argument list */
int *status; /* Pointer to inherited status value */
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(NULL);
/* Initialise. */
new = NULL;
/* Get a pointer to the inherited status value. */
status = astGetStatusPtr;
/* Check the global status. */
if ( !astOK ) return new;
/* Obtain the Mapping pointers from the ID's supplied and validate the
pointers to ensure they identify valid Mappings. */
map1 = astVerifyMapping( astMakePointer( map1_void ) );
map2 = astVerifyMapping( astMakePointer( map2_void ) );
if ( astOK ) {
/* Initialise the CmpMap, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitCmpMap( NULL, sizeof( AstCmpMap ), !class_init, &class_vtab,
"CmpMap", map1, map2, series );
/* 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 CmpMap'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 CmpMap. */
return astMakeId( new );
}
AstCmpMap *astInitCmpMap_( void *mem, size_t size, int init,
AstCmpMapVtab *vtab, const char *name,
AstMapping *map1, AstMapping *map2, int series, int *status ) {
/*
*+
* Name:
* astInitCmpMap
* Purpose:
* Initialise a CmpMap.
* Type:
* Protected function.
* Synopsis:
* #include "cmpmap.h"
* AstCmpMap *astInitCmpMap( void *mem, size_t size, int init,
* AstCmpMapVtab *vtab, const char *name,
* AstMapping *map1, AstMapping *map2,
* int series )
* Class Membership:
* CmpMap initialiser.
* Description:
* This function is provided for use by class implementations to initialise
* a new CmpMap object. It allocates memory (if necessary) to
* accommodate the CmpMap plus any additional data associated with the
* derived class. It then initialises a CmpMap structure at the start
* of this memory. If the "init" flag is set, it also initialises the
* contents of a virtual function table for a CmpMap at the start of
* the memory passed via the "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory in which the CmpMap is to be initialised.
* This must be of sufficient size to accommodate the CmpMap data
* (sizeof(CmpMap)) 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 CmpMap (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
* CmpMap structure, so a valid value must be supplied even if not
* required for allocating memory.
* init
* A logical flag indicating if the CmpMap'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 CmpMap.
* 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 Object
* astClass function).
* map1
* Pointer to the first Mapping.
* map2
* Pointer to the second Mapping.
* series
* If a non-zero value is given, the two Mappings will be connected
* together in series. A zero value requests that they be connected in
* parallel.
* Returned Value:
* A pointer to the new CmpMap.
* 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: */
AstCmpMap *new; /* Pointer to new CmpMap */
int map_f; /* Forward transformation defined? */
int map_i; /* Inverse transformation defined? */
int nin2; /* No. input coordinates for Mapping 2 */
int nin; /* No. input coordinates for CmpMap */
int nout1; /* No. output coordinates for Mapping 1 */
int nout; /* No. output coordinates for CmpMap */
/* Check the global status. */
if ( !astOK ) return NULL;
/* If necessary, initialise the virtual function table. */
if ( init ) astInitCmpMapVtab( vtab, name );
/* Initialise. */
new = NULL;
/* Determine in which directions each component Mapping is able to transform
coordinates. Combine these results to obtain a result for the overall
CmpMap. */
map_f = astGetTranForward( map1 ) && astGetTranForward( map2 );
map_i = astGetTranInverse( map1 ) && astGetTranInverse( map2 );
if ( astOK ) {
/* If connecting the Mappings in series, check that the number of coordinates
are compatible and report an error if they are not. */
if ( series ) {
nout1 = astGetNout( map1 );
nin2 = astGetNin( map2 );
if ( astOK && ( nout1 != nin2 ) ) {
astError( AST__INNCO, "astInitCmpMap(%s): The number of output "
"coordinates per point (%d) for the first Mapping "
"supplied does not match the number of input "
"coordinates (%d) for the second Mapping.", status, name, nout1,
nin2 );
}
}
}
/* If OK, determine the total number of input and output coordinates per point
for the CmpMap. */
if ( astOK ) {
if ( series ) {
nin = astGetNin( map1 );
nout = astGetNout( map2 );
} else {
nin = astGetNin( map1 ) + astGetNin( map2 );
nout = astGetNout( map1 ) + astGetNout( map2 );
}
} else {
nin = 0;
nout = 0;
}
/* Initialise a Mapping structure (the parent class) as the first component
within the CmpMap structure, allocating memory if necessary. Specify
the number of input and output coordinates and in which directions the
Mapping should be defined. */
if ( astOK ) {
new = (AstCmpMap *) astInitMapping( mem, size, 0,
(AstMappingVtab *) vtab, name,
nin, nout, map_f, map_i );
if ( astOK ) {
/* Initialise the CmpMap data. */
/* --------------------------- */
/* Store pointers to the component Mappings. Extract Mappings if
FrameSets are provided. */
if( astIsAFrameSet( map1 ) ) {
new->map1 = astGetMapping( (AstFrameSet *) map1, AST__BASE,
AST__CURRENT );
} else {
new->map1 = astClone( map1 );
}
if( astIsAFrameSet( map2 ) ) {
new->map2 = astGetMapping( (AstFrameSet *) map2, AST__BASE,
AST__CURRENT );
} else {
new->map2 = astClone( map2 );
}
/* Save the initial values of the inversion flags for these Mappings. */
new->invert1 = astGetInvert( new->map1 );
new->invert2 = astGetInvert( new->map2 );
/* Note whether the Mappings are joined in series (instead of in parallel),
constraining this flag to be 0 or 1. */
new->series = ( series != 0 );
/* If an error occurred, clean up by annulling the Mapping pointers and
deleting the new object. */
if ( !astOK ) {
new->map1 = astAnnul( new->map1 );
new->map2 = astAnnul( new->map2 );
new = astDelete( new );
}
}
}
/* Return a pointer to the new object. */
return new;
}
AstCmpMap *astLoadCmpMap_( void *mem, size_t size,
AstCmpMapVtab *vtab, const char *name,
AstChannel *channel, int *status ) {
/*
*+
* Name:
* astLoadCmpMap
* Purpose:
* Load a CmpMap.
* Type:
* Protected function.
* Synopsis:
* #include "cmpmap.h"
* AstCmpMap *astLoadCmpMap( void *mem, size_t size,
* AstCmpMapVtab *vtab, const char *name,
* AstChannel *channel )
* Class Membership:
* CmpMap loader.
* Description:
* This function is provided to load a new CmpMap 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
* CmpMap 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 a CmpMap at the start of the memory
* passed via the "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory into which the CmpMap is to be
* loaded. This must be of sufficient size to accommodate the
* CmpMap data (sizeof(CmpMap)) 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 CmpMap (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 CmpMap 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(AstCmpMap) is used instead.
* vtab
* Pointer to the start of the virtual function table to be
* associated with the new CmpMap. If this is NULL, a pointer to
* the (static) virtual function table for the CmpMap 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 "CmpMap" is used instead.
* Returned Value:
* A pointer to the new CmpMap.
* 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 */
AstCmpMap *new; /* Pointer to the new CmpMap */
/* 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 CmpMap. In this case the
CmpMap belongs to this class, so supply appropriate values to be
passed to the parent class loader (and its parent, etc.). */
if ( !vtab ) {
size = sizeof( AstCmpMap );
vtab = &class_vtab;
name = "CmpMap";
/* If required, initialise the virtual function table for this class. */
if ( !class_init ) {
astInitCmpMapVtab( 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 CmpMap. */
new = astLoadMapping( mem, size, (AstMappingVtab *) 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, "CmpMap" );
/* Now read each individual data item from this list and use it to
initialise the appropriate instance variable(s) for this class. */
/* In the case of attributes, we first read the "raw" input value,
supplying the "unset" value as the default. If a "set" value is
obtained, we then use the appropriate (private) Set... member
function to validate and set the value properly. */
/* Series. */
/* ------- */
new->series = astReadInt( channel, "series", 1 );
new->series = ( new->series != 0 );
/* First Invert flag. */
/* ------------------ */
new->invert1 = astReadInt( channel, "inva", 0 );
new->invert1 = ( new->invert1 != 0 );
/* Second Invert flag. */
/* ------------------- */
new->invert2 = astReadInt( channel, "invb", 0 );
new->invert2 = ( new->invert2 != 0 );
/* First Mapping. */
/* -------------- */
new->map1 = astReadObject( channel, "mapa", NULL );
/* Second Mapping. */
/* --------------- */
new->map2 = astReadObject( channel, "mapb", NULL );
/* If an error occurred, clean up by deleting the new CmpMap. */
if ( !astOK ) new = astDelete( new );
}
/* Return the new CmpMap 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. */
/* None. */
ast-8.0.7/c2f77.h 0000664 0001750 0001750 00000011663 12610415011 010237 0000000 0000000 #if !defined( C2F77_INCLUDED ) /* Include this file only once */
#define C2F77_INCLUDED
/*
*+
* Name:
* c2f77.h
* Purpose:
* Define the interface to the c2f77 module.
* Description:
* This file defines language-specific functions which support the
* FORTRAN 77 interface to the AST library.
*
* Note that this module is not a class implementation, although it
* resembles one.
* Functions Defined:
* Public:
* None.
*
* Protected:
* astStringExport
* Export a C string to a FORTRAN string.
* Macros Defined:
* Public:
* None.
*
* Protected:
* astWatchSTATUS
* Execute C code while watching a FORTRAN STATUS variable.
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* 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
* .
* Authors:
* RFWS: R.F. Warren-Smith (Starlink)
* DSB: David S. Berry (Starlink)
* History:
* 15-NOV-1996 (RFWS):
* Original version.
* 16-JUL-1997 (RFWS):
* Added astWatchSTATUS.
* 13-JUN-2001 (DSB):
* Make astStringExport available to F77 interface modules as well
* as AST classes.
*-
*/
/* Macros. */
/* ======= */
/*
*+
* Name:
* astWatchSTATUS
* Type:
* Protected macro.
* Purpose:
* Execute C code while watching a FORTRAN STATUS variable.
* Synopsis:
* #include "c2f77.h"
* astWatchSTATUS(code)
* Description:
* This macro expands to code which executes the C code supplied
* via the "code" argument in a new C scope (delimited by
* {...}). The code supplied executes while the AST error status is
* equated to a variable called STATUS, which is an error status
* argument passed from a FORTRAN routine using the macros defined
* in the "f77.h" include file.
*
* The effect of this is roughly as if the astWatch function had
* been used to locally declare the FORTRAN STATUS argument as a
* new AST error status variable, except that this macro also works
* if STATUS is not an int.
* Parameters:
* code
* The C code to be executed.
* Examples:
* F77_SUBROUTINE(ast_doit)( INTEGER(STATUS) ) {
* astWatchSTATUS(
* astDoit();
* )
* }
* Causes the astDoit function to be invoked as if the AST error
* status were equated to the STATUS argument passed from
* FORTRAN. Typically, if STATUS is set to an error value,
* astDoit would detect this by means of the astOK macro and
* would not then execute. If an error occurs in astDoit,
* causing the AST error status to be set, then that value is
* transferred to STATUS after the C code has executed (i.e. at
* the end of the astWatchSTATUS macro).
* Notes:
* - The FORTRAN argument must be called STATUS and must appear in
* the C function's parameter list as an argument of the INTEGER()
* macro defined in the "f77.h" include file.
* - The C code supplied executes in a new scope, in which
* automatic variables may be declared. However, such variables
* will not exist after the macro's expansion has been executed.
* - The AST error status variable and its value remain unchanged
* after the expansion of this macro has executed.
*-
*/
/* Define the macro. */
#define astWatchSTATUS(code) \
\
/* Begin a new C scope. */ \
{ \
\
/* Ensure that a pointer to the STATUS argument exists. */ \
GENPTR_INTEGER(STATUS) \
\
/* Store the STATUS value in a local int. */ \
int ast_local_status = *STATUS; \
int *status = &ast_local_status; \
\
/* Make this int the AST error status variable, saving the address of \
the previous variable. */ \
int *ast_previous_status = astWatch( &ast_local_status ); \
\
/* Execute the code supplied using the new error status variable. */ \
code \
\
/* Restore the original error status variable. */ \
(void) astWatch( ast_previous_status ); \
\
/* Return the final error status to STATUS. */ \
*STATUS = ast_local_status; \
}
/* Function prototypes. */
/* ==================== */
void astStringExport_( const char *, char *, int );
/* Function interfaces. */
/* ==================== */
/* These wrap up the functions defined by this module to make them
easier to use. */
#define astStringExport astStringExport_
#endif
ast-8.0.7/fdssmap.c 0000664 0001750 0001750 00000004562 12610415012 011040 0000000 0000000 /*
*+
* Name:
* fdssmap.c
* Purpose:
* Define a FORTRAN 77 interface to the AST DssMap class.
* Type of Module:
* C source file.
* Description:
* This file defines FORTRAN 77-callable C functions which provide
* a public FORTRAN 77 interface to the DssMap class.
* Routines Defined:
* AST_ISADSSMAP
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* 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
* .
* Authors:
* DSB: D.S. Berry (Starlink)
* RFWS: R.F. Warren-Smith (Starlink)
* History:
* 19-FEB-1997 (DSB):
* Original version.
* 5-SEP-1997 (RFWS)
* Removed the AST_DSSMAP function (now protected, so not
* required in the Fortran interface).
*/
/* Define the astFORTRAN77 macro which prevents error messages from
AST C functions from reporting the file and line number where the
error occurred (since these would refer to this file, they would
not be useful). */
#define astFORTRAN77
/* Header files. */
/* ============= */
#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */
#include "c2f77.h" /* F77 <-> C support functions/macros */
#include "error.h" /* Error reporting facilities */
#include "memory.h" /* Memory handling facilities */
#include "dssmap.h" /* C interface to the DssMap class */
F77_LOGICAL_FUNCTION(ast_isadssmap)( INTEGER(THIS),
INTEGER(STATUS) ) {
GENPTR_INTEGER(THIS)
F77_LOGICAL_TYPE(RESULT);
astAt( "AST_ISADSSMAP", NULL, 0 );
astWatchSTATUS(
RESULT = astIsADssMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE;
)
return RESULT;
}
ast-8.0.7/matrixmap.c 0000664 0001750 0001750 00000571716 12610415012 011417 0000000 0000000 /*
*class++
* Name:
* MatrixMap
* Purpose:
* Map coordinates by multiplying by a matrix.
* Constructor Function:
c astMatrixMap
f AST_MATRIXMAP
* Description:
* A MatrixMap is form of Mapping which performs a general linear
* transformation. Each set of input coordinates, regarded as a
* column-vector, are pre-multiplied by a matrix (whose elements
* are specified when the MatrixMap is created) to give a new
* column-vector containing the output coordinates. If appropriate,
* the inverse transformation may also be performed.
* Inheritance:
* The MatrixMap class inherits from the Mapping class.
* Attributes:
* The MatrixMap class does not define any new attributes beyond
* those which are applicable to all Mappings.
* Functions:
c The MatrixMap class does not define any new functions beyond those
f The MatrixMap 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
* .
* Authors:
* DSB: D.S. Berry (Starlink)
* RFWS: R.F. Warren-Smith (Starlink)
* History:
* 9-FEB-1996 (DSB):
* Original version.
* 13-NOV-1996 (DSB):
* Updated to support attributes, I/O and an external interface.
* 3-JUN-1997 (DSB):
* astMtrMult and astMtrRot made protected instead of public.
* 16-JUN-1997 (RFWS):
* Tidied public prologues.
* 24-JUN-1997 (DSB):
* Zero returned for coordinates which are indeterminate as a
* result of using an inverted, non-square, diagonal matrix.
* 10-OCT-1997 (DSB):
* o The inverse matrix is no longer dumped by the Dump function.
* Instead, it is re-calculated by the Load function.
* o The description of argument "form" in astMatrixMap corrected
* to indicate that a value of 2 produces a unit matrix.
* o String values used to represent choices externally, instead
* of integers.
* 24-NOV-1997 (DSB):
* Use of error code AST__OPT replaced by AST__RDERR.
* 28-JAN-1998 (DSB):
* Bug fix in astMtrMult: the matrix (forward or inverse) used for
* the "a" MatrixMap was determined by the Invert flag of the other
* ("this") MatrixMap.
* 14-APR-1998 (DSB):
* Bug fix in Dump. Previously, matrix elements with value AST__BAD
* were explicitly written out. Now they are not written out, since
* AST__BAD can have different values on different machines. Missing
* elements default to AST__BAD when read back in using astLoadMatrixMap.
* 20-APR-1998 (DSB):
* Bug fix in astLoadMatrixMap: initialise the pointer to the inverse
* matrix array to NULL if no inverse matrix is needed.
* 25-AUG-1998 (DSB):
* - Transform changed so that bad input axis values are not
* propagated to output axes which are independant of the input axis.
* - CompressMatrix changed to allow a tolerance of DBL_EPSILON when
* determining if a matrix is a unit matrix, or a diagonal matrix.
* - MapMerge changed to allow MatrixMaps to swap with PermMaps
* in order to move the MatrixMap closer to a Mapping with which it
* could merge.
* 22-FEB-1999 (DSB):
* Changed logic of MapMerge to avoid infinite looping.
* 5-MAY-1999 (DSB):
* More corrections to MapMerge: Cleared up errors in the use of the
* supplied invert flags, and corrected logic for deciding which
* neighbouring Mapping to swap with.
* 16-JUL-1999 (DSB):
* Fixed memory leaks in MatWin and MapMerge.
* 8-JAN-2003 (DSB):
* Changed private InitVtab method to protected astInitatrixMapVtab
* method.
* 11-SEP-2003 (DSB):
* Increased tolerance on checks for unit matrices within
* CompressMatrix. Now uses sqrt(DBL_EPSILON)*diag (previously was
* DBL_EPSILON*DIAG ).
* 10-NOV-2003 (DSB):
* Modified functions which swap a MatrixMap with another Mapping
* (e.g. MatSwapPerm, etc), to simplify the returned Mappings.
* 13-JAN-2003 (DSB):
* Modified the tolerance used by CompressMatrix when checking for
* zero matrix elements. Old system compared each element to thre
* size of the diagonal, but different scalings on different axes could
* cause this to trat as zero values which should nto be treated as
* zero.
* 23-APR-2004 (DSB):
* Changes to simplification algorithm.
* 8-JUL-2004 (DSB):
* astMtrMult - Report an error if either MatrixMap does not have a
* defined forward transformation.
* 1-SEP-2004 (DSB):
* Ensure do1 and do2 are initialised before use in MapMerge.
* 7-SEP-2005 (DSB):
* Take account of the Invert flag when using the zoom factor from
* a ZoomMap.
* 14-FEB-2006 (DSB):
* Correct row/col confusion in CompressMatrix.
* 15-MAR-2006 (DSB):
* Override astEqual.
* 15-MAR-2009 (DSB):
* MapSplit: Only create the returned Mapping if it would have some
* outputs. Also, do not create the returned Mapping if any output
* depends on a mixture of selected and unselected inputs.
* 16-JUL-2009 (DSB):
* MatPerm: Fix memory leak (mm2 was not being annulled).
* 2-OCT-2012 (DSB):
* - Check for Infs as well as NaNs.
* - In MapSplit do not split the MatrixMap if the resulting
* matrix would contain only bad elements.
* - Report an error if an attempt is made to create a MatrixMap
* containing only bad elements.
* 4-NOV-2013 (DSB):
* Allow a full form MatrixMap to be simplified to a diagonal form
* MatrixMap if all the off-diagonal values are zero.
* 23-APR-2015 (DSB):
* Improve MapMerge. If a MatrixMap can merge with its next-but-one
* neighbour, then swap the MatrixMap with its neighbour, so that
* it is then next its next-but-one neighbour, and then merge the
* two Mappings into a single Mapping. Previously, only the swap
* was performed - not the merger. And the swap was only performed
* if the intervening neighbour could not itself merge. This could
* result in an infinite simplification loop, which was detected by
* CmpMap and and aborted, resulting in no useful simplification.
*class--
*/
/* Module Macros. */
/* ============== */
/* Set the name of the class we are implementing. This indicates to
the header files that define class interfaces that they should make
"protected" symbols available. */
#define astCLASS MatrixMap
/* Define identifiers for the different forms of matrix storage. */
#define FULL 0
#define DIAGONAL 1
#define UNIT 2
/* Macros which return the maximum and minimum of two values. */
#define MAX(aa,bb) ((aa)>(bb)?(aa):(bb))
#define MIN(aa,bb) ((aa)<(bb)?(aa):(bb))
/* Macro to check for equality of floating point values. We cannot
compare bad values directory because of the danger of floating point
exceptions, so bad values are dealt with explicitly. */
#define EQUAL(aa,bb) (((aa)==AST__BAD)?(((bb)==AST__BAD)?1:0):(((bb)==AST__BAD)?0:(fabs((aa)-(bb))<=1.0E5*MAX((fabs(aa)+fabs(bb))*DBL_EPSILON,DBL_MIN))))
/* 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 "pointset.h" /* Sets of points/coordinates */
#include "mapping.h" /* Coordinate mappings (parent class) */
#include "matrixmap.h" /* Interface definition for this class */
#include "pal.h" /* SLALIB function definitions */
#include "permmap.h"
#include "zoommap.h"
#include "unitmap.h"
#include "winmap.h"
/* Error code definitions. */
/* ----------------------- */
#include "ast_err.h" /* AST error codes */
/* C header files. */
/* --------------- */
#include
#include
#include
#include
#include
/* Module Variables. */
/* ================= */
/* Address of this static variable is used as a unique identifier for
member of this class. */
static int class_check;
static const char *Form[3] = { "Full", "Diagonal", "Unit" }; /* Text values
used to represent storage form externally */
/* Pointers to parent class methods which are extended by this class. */
static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
static int *(* parent_mapsplit)( AstMapping *, int, const int *, AstMapping **, int * );
#ifdef THREAD_SAFE
/* Define how to initialise thread-specific globals. */
#define GLOBAL_inits \
globals->Class_Init = 0;
/* Create the function that initialises global data for this module. */
astMAKE_INITGLOBALS(MatrixMap)
/* Define macros for accessing each item of thread specific global data. */
#define class_init astGLOBAL(MatrixMap,Class_Init)
#define class_vtab astGLOBAL(MatrixMap,Class_Vtab)
#include
#else
/* Define the class virtual function table and its initialisation flag
as static variables. */
static AstMatrixMapVtab 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. */
AstMatrixMap *astMatrixMapId_( int, int, int, const double [], const char *, ... );
/* Prototypes for Private Member Functions. */
/* ======================================== */
static AstMatrixMap *MatMat( AstMapping *, AstMapping *, int, int, int * );
static AstMatrixMap *MatPerm( AstMatrixMap *, AstPermMap *, int, int, int, int * );
static AstMatrixMap *MatZoom( AstMatrixMap *, AstZoomMap *, int, int, int * );
static AstMatrixMap *MtrMult( AstMatrixMap *, AstMatrixMap *, int * );
static AstMatrixMap *MtrRot( AstMatrixMap *, double, const double[], int * );
static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
static double *InvertMatrix( int, int, int, double *, int * );
static double Rate( AstMapping *, double *, int, int, int * );
static int Equal( AstObject *, AstObject *, int * );
static int FindString( int, const char *[], const char *, const char *, const char *, const char *, int * );
static int Ustrcmp( const char *, const char *, int * );
static int GetTranForward( AstMapping *, int * );
static int GetIsLinear( AstMapping *, int * );
static int GetTranInverse( AstMapping *, int * );
static int CanSwap( AstMapping *, AstMapping *, int, int, int *, int * );
static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * );
static int PermOK( AstMapping *, int * );
static int ScalingRowCol( AstMatrixMap *, int, int * );
static void CompressMatrix( AstMatrixMap *, int * );
static void Copy( const AstObject *, AstObject *, int * );
static void Delete( AstObject *obj, int * );
static void Dump( AstObject *, AstChannel *, int * );
static void ExpandMatrix( AstMatrixMap *, int * );
static void MatWin( AstMapping **, int *, int, int * );
static void MatPermSwap( AstMapping **, int *, int, int * );
static void PermGet( AstPermMap *, int **, int **, double **, int * );
static void SMtrMult( int, int, int, const double *, double *, double*, int * );
static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * );
/* Member functions. */
/* ================= */
static int CanSwap( AstMapping *map1, AstMapping *map2, int inv1, int inv2,
int *simpler, int *status ){
/*
* Name:
* CanSwap
* Purpose:
* Determine if two Mappings could be swapped.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* int CanSwap( AstMapping *map1, AstMapping *map2, int inv1, int inv2,
* int *simpler, int *status )
* Class Membership:
* MatrixMap member function
* Description:
* This function returns a flag indicating if the pair of supplied
* Mappings could be replaced by an equivalent pair of Mappings from the
* same classes as the supplied pair, but in reversed order. Each pair
* of Mappings is considered to be compunded in series. The supplied
* Mapings are not changed in any way.
* Parameters:
* map1
* The Mapping to be applied first.
* map2
* The Mapping to be applied second.
* inv1
* The invert flag to use with map1. A value of zero causes the forward
* mapping to be used, and a non-zero value causes the inverse
* mapping to be used.
* inv2
* The invert flag to use with map2.
* simpler
* Addresss of a location at which to return a flag indicating if
* the swapped Mappings would be intrinsically simpler than the
* original Mappings.
* status
* Pointer to the inherited status variable.
* Returned Value:
* 1 if the Mappings could be swapped, 0 otherwise.
* Notes:
* - One of the supplied pair of Mappings must be a MatrixMap.
* - A value of 0 is returned if an error has already occurred, or if
* this function should fail for any reason.
*/
/* Local Variables: */
AstMatrixMap *mat; /* Pointer to MatrixMap Mapping */
AstMapping *nomat; /* Pointer to non-MatrixMap Mapping */
const char *class1; /* Pointer to map1 class string */
const char *class2; /* Pointer to map2 class string */
const char *nomat_class; /* Pointer to non-MatrixMap class string */
double *consts; /* Pointer to constants array */
int *inperm; /* Pointer to input axis permutation array */
int *outperm; /* Pointer to output axis permutation array */
int i; /* Loop count */
int invert[ 2 ]; /* Original invert flags */
int nax; /* No. of in/out coordinates for the MatrixMap */
int nin; /* No. of input coordinates for the PermMap */
int nout; /* No. of output coordinates for the PermMap */
int ret; /* Returned flag */
/* Check the global error status. */
if ( !astOK ) return 0;
/* Initialise */
ret = 0;
*simpler = 0;
/* Temporarily set the Invert attributes of both Mappings to the supplied
values. */
invert[ 0 ] = astGetInvert( map1 );
astSetInvert( map1, inv1 );
invert[ 1 ] = astGetInvert( map2 );
astSetInvert( map2, inv2 );
/* Get the classes of the two mappings. */
class1 = astGetClass( map1 );
class2 = astGetClass( map2 );
if( astOK ){
/* Get a pointer to the MatrixMap and non-MatrixMap Mappings. */
if( !strcmp( class1, "MatrixMap" ) ){
mat = (AstMatrixMap *) map1;
nomat = map2;
nomat_class = class2;
} else {
nomat = map1;
mat = (AstMatrixMap *) map2;
nomat_class = class1;
}
/* Get the number of input axes for the MatrixMap. */
nax = astGetNin( mat );
/* If it is a WinMap, the Mappings can be swapped. */
if( !strcmp( nomat_class, "WinMap" ) ){
ret = 1;
/* If it is a PermMap, the Mappings can be swapped so long as:
1) all links between input and output axes in the PermMap are
bi-directional. This does not preclude the existence of unconnected
axes, which do not have links (bi-directional or otherwise).
2) The MatrixMap is square, and invertable.
3) If the permMap is applied first, then each output of the PermMap
which is assigned a constant value must correspond to a "scaling" row
and column in the MatrixMap. I.e. if PermMap output axis "i" is
assigned a constant value, then row i and column i of the following
MatrixMap must contain only zeros, EXCEPT for the diagonal term (row
i, column i) which must be non-zero. If the Mappings are in the other
order, then the same applies to PermMap input axes assigned a constant
value. */
/* Check the other Mapping is a PermMap, and that the MatrixMap is square
and has an inverse. */
} else if( !strcmp( nomat_class, "PermMap" ) &&
nax == astGetNout( mat ) && ( mat->form == UNIT ||
( mat->i_matrix != NULL &&
mat->f_matrix != NULL ) ) ) {
/* Get the number of input and output coordinates for the PermMap. */
nin = astGetNin( nomat );
nout = astGetNout( nomat );
/* We need to know the axis permutation arrays and constants array for
the PermMap. */
PermGet( (AstPermMap *) nomat, &outperm, &inperm, &consts, status );
if( astOK ) {
/* Indicate we can swap with the PermMap. */
ret = 1;
/* Check each output axis. If any links between axes are found which are
not bi-directional, indicate that we cannot swap with the PermMap. */
for( i = 0; i < nout; i++ ){
if( outperm[ i ] >= 0 && outperm[ i ] < nin ) {
if( inperm[ outperm[ i ] ] != i ) {
ret = 0;
break;
}
}
}
/* Check each input axis. If any links between axes are found which are
not bi-directional, indicate that we cannot swap with the PermMap. */
for( i = 0; i < nin; i++ ){
if( inperm[ i ] >= 0 && inperm[ i ] < nout ) {
if( outperm[ inperm[ i ] ] != i ) {
ret = 0;
break;
}
}
}
/* If the PermMap is suitable, check that any constant values fed from the
PermMap into the MatrixMap (in either forward or inverse direction)
are not changed by the MatrixMap. This requires the row and column for
each constant axis to be zeros, ecept for a value of 1.0 on the
diagonal. First deal with the cases where the PermMap is applied
first, so the outputs of the PermMap are fed into the MatrixMap in the
forward direction. */
if( ret && ( nomat == map1 ) ) {
if( nout != nax ){
astError( AST__RDERR, "PermMap produces %d outputs, but the following"
"MatrixMap has %d inputs\n", status, nout, nax );
ret = 0;
}
/* Consider each output axis of the PermMap. */
for( i = 0; i < nout && astOK ; i++ ) {
/* If this PermMap output is assigned a constant... */
if( outperm[ i ] < 0 || outperm[ i ] >= nin ) {
/* Check the i'th row of the MatrixMap is all zero except for the i'th
column which must be non-zero. If not indicate that the MatrixMap cannot
swap with the PermMap and leave the loop. */
if( !ScalingRowCol( mat, i, status ) ) {
ret = 0;
break;
}
}
}
}
/* Now deal with the cases where the PermMap is applied second, so the inputs
of the PermMap are fed into the MatrixMap in the inverse direction. */
if( ret && ( nomat == map2 ) ) {
if( nin != nax ){
astError( AST__RDERR, "Inverse PermMap produces %d inputs, but the "
"preceding MatrixMap has %d outputs\n", status, nin, nax );
ret = 0;
}
/* Consider each input axis of the PermMap. */
for( i = 0; i < nin && astOK; i++ ){
/* If this PermMap input is assigned a constant (by the inverse Mapping)... */
if( inperm[ i ] < 0 || inperm[ i ] >= nout ) {
/* Check the i'th row of the MatrixMap is all zero except for the i'th
column which must be non-zero. If not indicate that the MatrixMap cannot
swap with the PermMap and leave the loop. */
if( !ScalingRowCol( mat, i, status ) ) {
ret = 0;
break;
}
}
}
}
/* If we can swap with the PermMap, the swapped Mappings may be
intrinsically simpler than the original mappings. */
if( ret ) {
/* If the PermMap precedes the WinMap, this will be the case if the PermMap
has more outputs than inputs. If the WinMap precedes the PermMap, this
will be the case if the PermMap has more inputs than outputs. */
*simpler = ( nomat == map1 ) ? nout > nin : nin > nout;
}
/* Free the axis permutation and constants arrays. */
outperm = (int *) astFree( (void *) outperm );
inperm = (int *) astFree( (void *) inperm );
consts = (double *) astFree( (void *) consts );
}
}
}
/* Re-instate the original settings of the Invert attributes for the
supplied MatrixMaps. */
astSetInvert( map1, invert[ 0 ] );
astSetInvert( map2, invert[ 1 ] );
/* Return the answer. */
return astOK ? ret : 0;
}
static void CompressMatrix( AstMatrixMap *this, int *status ){
/*
* Name:
* CompressMatrix
* Purpose:
* If possible, reduce the amount of storage needed to store a MatrixMap.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* void CompressMatrix( AstMatrixMap *this, int *status )
* Class Membership:
* MatrixMap member function.
* Description:
* The supplid MatrixMap is converted to its most compressed form
* (i.e no element values if it is a unit matrix, diagonal elements only
* if it is a diagonal matrix, or all elements otherwise).
* Parameters:
* this
* A pointer to the MatrixMap to be compressed.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
double *a; /* Pointer to next element */
double *colmax; /* Pointer to array holding column max values */
double *fmat; /* Pointer to compressed forward matrix */
double *rowmax; /* Pointer to array holding row max values */
double mval; /* Matrix element value */
int i; /* Loop count */
int j; /* Loop count */
int k; /* Loop count */
int ncol; /* No. of columns in forward matrix */
int ndiag; /* No. of diagonal elements in matrix */
int new_form; /* Compressed storage form */
int new_inv; /* New inverse requied? */
int next_diag; /* Index of next diagonal element */
int nrow; /* No. of rows in forward matrix */
/* Check the global error status. */
if ( !astOK || !this ) return;
/* Initialise variables to avoid "used of uninitialised variable"
messages from dumb compilers. */
new_inv = 0;
/* Get the dimensions of the forward matrix. */
if( astGetInvert( this ) ){
nrow = astGetNin( this );
ncol = astGetNout( this );
} else {
ncol = astGetNin( this );
nrow = astGetNout( this );
}
/* Store the number of diagonal elements in the matrix. This is the
minimum of the number of rows and columns. */
if( ncol < nrow ){
ndiag = ncol;
} else {
ndiag = nrow;
}
/* If the MatrixMap is already stored in UNIT form, it cannot be compressed
any further. */
if( this->form == UNIT){
return;
/* Otherwise, if the MatrixMap is stored in DIAGONAL form, it could be
compressed into a UNIT MatrixMap if all the supplied element values are
one. */
} else if( this->form == DIAGONAL ){
new_form = UNIT;
for( i = 0; i < ndiag; i++ ){
if( !EQUAL( (this->f_matrix)[ i ], 1.0 ) ){
new_form = DIAGONAL;
break;
}
}
/* If it can be compressed, change the storage form and free the arrays
holding the diagonal element values. */
if( new_form == UNIT ) {
this->f_matrix = (double *) astFree( (void *)( this->f_matrix ) );
this->i_matrix = (double *) astFree( (void *)( this->i_matrix ) );
this->form = UNIT;
}
/* Otherwise, a full MatrixMap has been supplied, but this could be stored
in a unit or diagonal MatrixMap if the element values are appropriate. */
} else {
new_form = FULL;
/* Find the maximum absolute value in each column. Scale by
sqrt(DBL_EPSILON) to be come a lower limit for non-zero values. */
colmax = astMalloc( ncol*sizeof( double ) );
if( colmax ) {
for( j = 0; j < ncol; j++ ) {
colmax[ j ] = 0.0;
i = j;
for( k = 0; k < nrow; k++ ) {
mval = (this->f_matrix)[ i ];
if( mval != AST__BAD ) {
mval = fabs( mval );
if( mval > colmax[ j ] ) colmax[ j ] = mval;
}
i += ncol;
}
colmax[ j ] *= sqrt( DBL_EPSILON );
}
}
/* Find the maximum absolute value in each row. Scale by
sqrt(DBL_EPSILON) to be come a lower limit for non-zero values. */
rowmax = astMalloc( nrow*sizeof( double ) );
if( rowmax ) {
for( k = 0; k < nrow; k++ ) {
rowmax[ k ] = 0.0;
i = k*ncol;
for( j = 0; j < ncol; j++ ) {
mval = (this->f_matrix)[ i ];
if( mval != AST__BAD ) {
mval = fabs( mval );
if( mval > rowmax[ k ] ) rowmax[ k ] = mval;
}
i++;
}
rowmax[ k ] *= sqrt( DBL_EPSILON );
}
}
/* Check memory can be used */
if( astOK ) {
/* Initialise a flag indicating that the inverse matrix does not need to
be re-calculated. */
new_inv = 0;
/* Initially assume that the forward matrix is a unit matrix. */
new_form = UNIT;
/* Store a pointer to the next matrix element. */
a = this->f_matrix;
/* Loop through all the rows in the forward matrix array. */
for( k = 0; k < nrow; k++ ) {
/* Loop through all the elements in this column. */
for( j = 0; j < ncol; j++, a++ ) {
/* If this element is bad, use full form. */
if( *a == AST__BAD ) {
new_form = FULL;
/* Otherwise, if this is a diagonal term, check its value. If it is not one,
then the matrix cannot be a unit matrix, but it could still be a diagonal
matrix. */
} else {
if( j == k ) {
if( *a != 1.0 && new_form == UNIT ) new_form = DIAGONAL;
/* If this is not a diagonal element, and the element value is not zero,
then the matrix is not a diagonal matrix. Allow a tolerance of
SQRT(DBL_EPSILON) times the largest value in the same row or column as
the current matrix element. That is, an element must be insignificant
to both its row and its column to be considered as effectively zero.
Replace values less than this limit with zero. */
} else {
mval = fabs( *a );
if( mval <= rowmax[ k ] &&
mval <= colmax[ j ] ) {
/* If the element will change value, set a flag indicating that the inverse
matrix needs to be re-calculated. */
if( *a != 0.0 ) new_inv = 1;
/* Ensure this element value is zero. */
*a = 0.0;
} else {
new_form = FULL;
}
}
}
}
}
}
/* Free memory. */
colmax = astFree( colmax );
rowmax = astFree( rowmax );
/* If it can be compressed into a UNIT MatrixMap, change the storage form and
free the arrays holding the element values. */
if( new_form == UNIT ) {
this->f_matrix = (double *) astFree( (void *)( this->f_matrix ) );
this->i_matrix = (double *) astFree( (void *)( this->i_matrix ) );
this->form = UNIT;
/* Otherwise, if it can be compressed into a DIAGONAL MatrixMap, copy the
diagonal elements from the full forward matrix into a newly allocated
array, use this array to replace the forward matrix array in the MatrixMap,
create a new inverse matrix, and change the storage form. */
} else if( new_form == DIAGONAL ) {
fmat = astMalloc( sizeof(double)*(size_t)ndiag );
if( fmat ){
next_diag = 0;
for( i = 0; i < ndiag; i++ ){
fmat[ i ] = (this->f_matrix)[ next_diag ];
next_diag += ncol + 1;
}
(void) astFree( (void *) this->f_matrix );
(void) astFree( (void *) this->i_matrix );
this->f_matrix = fmat;
this->i_matrix = InvertMatrix( DIAGONAL, nrow, ncol, fmat, status );
this->form = DIAGONAL;
}
/* Calculate a new inverse matrix if necessary. */
} else if( new_inv ) {
(void) astFree( (void *) this->i_matrix );
this->i_matrix = InvertMatrix( FULL, nrow, ncol, this->f_matrix, status );
}
}
return;
}
static int Equal( AstObject *this_object, AstObject *that_object, int *status ) {
/*
* Name:
* Equal
* Purpose:
* Test if two MatrixMaps are equivalent.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* int Equal( AstObject *this, AstObject *that, int *status )
* Class Membership:
* MatrixMap member function (over-rides the astEqual protected
* method inherited from the astMapping class).
* Description:
* This function returns a boolean result (0 or 1) to indicate whether
* two MatrixMaps are equivalent.
* Parameters:
* this
* Pointer to the first Object (a MatrixMap).
* that
* Pointer to the second Object.
* status
* Pointer to the inherited status variable.
* Returned Value:
* One if the MatrixMaps are equivalent, zero otherwise.
* 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: */
AstMatrixMap *that;
AstMatrixMap *this;
double *that_matrix;
double *this_matrix;
int i;
int nin;
int nout;
int result;
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain pointers to the two MatrixMap structures. */
this = (AstMatrixMap *) this_object;
that = (AstMatrixMap *) that_object;
/* Check the second object is a MatrixMap. We know the first is a
MatrixMap since we have arrived at this implementation of the virtual
function. */
if( astIsAMatrixMap( that ) ) {
/* Get the number of inputs and outputs and check they are the same for both. */
nin = astGetNin( this );
nout = astGetNout( this );
if( astGetNout( that ) == nout && astGetNin( that ) == nin ) {
/* Assume the MatrixMaps are equivalent. */
result = 1;
/* Ensure both MatrixMaps are stored in full form. */
ExpandMatrix( this, status );
ExpandMatrix( that, status );
/* Get pointers to the arrays holding the elements of the forward matrix
for both MatrixMaps. */
if( astGetInvert( this ) ) {
this_matrix = this->i_matrix;
} else {
this_matrix = this->f_matrix;
}
if( astGetInvert( that ) ) {
that_matrix = that->i_matrix;
} else {
that_matrix = that->f_matrix;
}
/* If either of the above arrays is not available, try to get the inverse
matrix arrays. */
if( !this_matrix || !that_matrix ) {
if( astGetInvert( this ) ) {
this_matrix = this->f_matrix;
} else {
this_matrix = this->i_matrix;
}
if( astGetInvert( that ) ) {
that_matrix = that->f_matrix;
} else {
that_matrix = that->i_matrix;
}
}
/* If both arrays are now available compare their elements. */
if( this_matrix && that_matrix ) {
result = 1;
for( i = 0; i < nin*nout; i++ ) {
if( !EQUAL( this_matrix[ i ], that_matrix[ i ] ) ){
result = 0;
break;
}
}
}
/* Ensure the supplied MatrixMaps are stored back in compressed form. */
CompressMatrix( this, status );
CompressMatrix( that, status );
}
}
/* If an error occurred, clear the result value. */
if ( !astOK ) result = 0;
/* Return the result, */
return result;
}
static void ExpandMatrix( AstMatrixMap *this, int *status ){
/*
* Name:
* ExpandMatrix
* Purpose:
* Ensure the MatrixMap is stored in full (non-compressed) form.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* void ExpandMatrix( AstMatrixMap *this, int *status )
* Class Membership:
* MatrixMap member function.
* Description:
* If the supplid MatrixMap is stored in a compressed form (i.e no
* element values if it is a unit matrix, diagonal elements only
* if it is a diagonal matrix), it is expanded into a full MatrixMap
* in which all elements are stored.
* Parameters:
* this
* A pointer to the MatrixMap to be expanded.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
double *fmat; /* Pointer to full forward matrix */
double *imat; /* Pointer to full inverse matrix */
int i; /* Loop count */
int ncol; /* No. of columns in forward matrix */
int ndiag; /* No. of diagonal elements in matrix */
int nrow; /* No. of rows in forward matrix */
/* Check the global error status. Also return if the MatrixMap
pointer is null. */
if ( !astOK || !this ) return;
/* Return without action if the MatrixMap is already in full form. */
if( this->form == FULL ) return;
/* Get the dimensions of the forward matrix. */
if( astGetInvert( this ) ){
nrow = astGetNin( this );
ncol = astGetNout( this );
} else {
ncol = astGetNin( this );
nrow = astGetNout( this );
}
/* Store the number of diagonal elements. */
if( nrow > ncol ){
ndiag = ncol;
} else {
ndiag = nrow;
}
/* Allocate arrays to hold the full forward and inverse matrices. */
fmat = (double *) astMalloc( sizeof( double )*(size_t)( nrow*ncol ) );
imat = (double *) astMalloc( sizeof( double )*(size_t)( nrow*ncol ) );
if( imat && fmat ){
/* Fill them both with zeros. */
for( i = 0; i < nrow*ncol; i++ ) {
fmat[ i ] = 0.0;
imat[ i ] = 0.0;
}
/* If a unit MatrixMap was supplied, put ones on the diagonals. */
if( this->form == UNIT ){
for( i = 0; i < ndiag; i++ ) {
fmat[ i*( ncol + 1 ) ] = 1.0;
imat[ i*( nrow + 1 ) ] = 1.0;
}
/* If a diagonal MatrixMap was supplied, copy the diagonal terms from
the supplied MatrixMap. */
} else if( this->form == DIAGONAL ){
for( i = 0; i < ndiag; i++ ) {
fmat[ i*( ncol + 1 ) ] = (this->f_matrix)[ i ];
imat[ i*( nrow + 1 ) ] = (this->i_matrix)[ i ];
}
}
/* Free any existing arrays in the MatrixMap and store the new ones. */
(void) astFree( (void *) this->f_matrix );
(void) astFree( (void *) this->i_matrix );
this->f_matrix = fmat;
this->i_matrix = imat;
/* Update the storage form. */
this->form = FULL;
/* If either of the new matrices could not be allocated, ensure that
both have been freed. */
} else {
fmat = (double *) astFree( (void *) fmat );
imat = (double *) astFree( (void *) imat );
}
return;
}
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 "matrix.h"
* int FindString( int n, const char *list[], const char *test,
* const char *text, const char *method, const char *class, int *status )
* Class Membership:
* MatrixMap 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 ) {
astError( AST__RDERR, "%s(%s): Illegal value '%s' supplied for %s.", status,
method, class, test, text );
ret = -1;
}
/* Return the answer. */
return ret;
}
static int GetIsLinear( AstMapping *this_mapping, int *status ){
/*
* Name:
* GetIsLinear
* Purpose:
* Return the value of the IsLinear attribute for a MatrixMap.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* void GetIsLinear( AstMapping *this, int *status )
* Class Membership:
* MatrixMap member function (over-rides the protected astGetIsLinear
* method inherited from the Mapping class).
* Description:
* This function returns the value of the IsLinear attribute for a
* Frame, which is always one.
* Parameters:
* this
* Pointer to the MatrixMap.
* status
* Pointer to the inherited status variable.
*/
return 1;
}
static int Ustrcmp( const char *a, const char *b, int *status ){
/*
* Name:
* Ustrncmp
* Purpose:
* A case blind version of strcmp.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* int Ustrcmp( const char *a, const char *b )
* Class Membership:
* MatrixMap 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.
* 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;
}
void astInitMatrixMapVtab_( AstMatrixMapVtab *vtab, const char *name, int *status ) {
/*
*+
* Name:
* astInitMatrixMapVtab
* Purpose:
* Initialise a virtual function table for a MatrixMap.
* Type:
* Protected function.
* Synopsis:
* #include "matrixmap.h"
* void astInitMatrixMapVtab( AstMatrixMapVtab *vtab, const char *name )
* Class Membership:
* MatrixMap vtab initialiser.
* Description:
* This function initialises the component of a virtual function
* table which is used by the MatrixMap 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 */
AstMappingVtab *mapping; /* Pointer to Mapping 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. */
astInitMappingVtab( (AstMappingVtab *) vtab, name );
/* Store a unique "magic" value in the virtual function table. This
will be used (by astIsAMatrixMap) 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 = &(((AstMappingVtab *) vtab)->id);
/* Initialise member function pointers. */
/* ------------------------------------ */
/* Store pointers to the member functions (implemented here) that provide
virtual methods for this class. */
vtab->MtrRot = MtrRot;
vtab->MtrMult = MtrMult;
/* Save the inherited pointers to methods that will be extended, and
replace them with pointers to the new member functions. */
object = (AstObjectVtab *) vtab;
mapping = (AstMappingVtab *) vtab;
parent_transform = mapping->Transform;
mapping->Transform = Transform;
parent_mapsplit = mapping->MapSplit;
mapping->MapSplit = MapSplit;
/* Store replacement pointers for methods which will be over-ridden by
new member functions implemented here. */
object->Equal = Equal;
mapping->GetIsLinear = GetIsLinear;
mapping->GetTranForward = GetTranForward;
mapping->GetTranInverse = GetTranInverse;
mapping->MapMerge = MapMerge;
mapping->Rate = Rate;
/* Declare the destructor and copy constructor. */
astSetDelete( (AstObjectVtab *) vtab, Delete );
astSetCopy( (AstObjectVtab *) vtab, Copy );
/* Declare the class dump function. */
astSetDump( vtab, Dump, "MatrixMap", "Matrix transformation" );
/* 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 *InvertMatrix( int form, int nrow, int ncol, double *matrix, int *status ){
/*
* Name:
* InvertMatrix
* Purpose:
* Invert a suplied matrix.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* double *InvertMatrix( int form, int nrow, int ncol, double *matrix, int *status )
* Class Membership:
* MatrixMap member function.
* Description:
* This function returns a pointer to a matrix holding the inverse of
* the supplied matrix, or a NULL pointer if the inverse is not defined.
* The memory to store the inverse matrix is allocated internally, and
* should be freed using astFree when no longer required.
*
* The correspondence between a full matrix and its inverse is only
* unique if the matrix is square, and so a NULL pointer is returned if
* the supplied matrix is not square.
* Parameters:
* form
* The form of the MatrixMap; UNIT, DIAGONAL or FULL.
* nrow
* Number of rows in the supplied matrix.
* ncol
* Number of columns in the supplied matrix.
* matrix
* A pointer to the input matrix. Elements should be stored in row
* order (i.e. (row 1,column 1 ), (row 1,column 2 )... (row 2,column 1),
* etc).
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the output matrix.
* Notes:
* - A NULL pointer is returned if a unit matrix is supplied.
* - 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.
* - No error is reported if the inverse is not defined.
*/
/* Local Variables: */
double det; /* Determinant of supplied matrix */
double mval; /* Matrix element value */
double *out; /* Pointer to returned inverse matrix */
double *vector; /* Pointer to vector used by palDmat */
int i; /* Matrix element number */
int *iw; /* Pointer to workspace used by palDmat */
int nel; /* No. of elements in square matrix */
int ndiag; /* No. of diagonal elements */
int ok; /* Zero if any bad matrix values found */
int sing; /* Zero if matrix is not singular */
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Return a NULL pointer if the input matrix is NULL. */
if( !matrix ) return NULL;
/* If a unit matrix map has been supplied, return NULL. */
if( form == UNIT ){
return NULL;
/* If a diagonal matrix has been supplied, allocate an array to hold
the diagonal terms of the inverse matrix. Store the reciprocal
of the input matrix diagonal terms in it. If any of the input diagonal
terms are zero or BAD, set the associated elements of the inverse matrix
BAD. */
} else if( form == DIAGONAL ){
if( nrow > ncol ) {
ndiag = ncol;
} else {
ndiag = nrow;
}
out = (double *) astMalloc( sizeof( double )*(size_t)ndiag );
if( out ) {
for( i = 0; i < ndiag; i++ ) {
mval = matrix[ i ];
if( mval != 0.0 && mval != AST__BAD ){
out[ i ] = 1.0/mval;
} else {
out[ i ] = AST__BAD;
}
}
}
/* If a full matrix has been supplied, initialise the returned pointer. */
} else {
out = NULL;
/* Check that the matrix is square. */
if( nrow == ncol ){
/* Find the number of elements in the matrix. */
nel = nrow*ncol;
/* See if there are any bad values in the matrix. */
ok = 1;
for ( i=0; iform == UNIT && nin == nout ){
map2 = (AstMapping *) astUnitMap( nin, "", status );
/* If the MatrixMap is a square diagonal matrix with equal diagonal
terms, then it can be replaced by a ZoomMap. */
} else if( mm->form == DIAGONAL && nin == nout &&
mm->f_matrix && mm->i_matrix &&
(mm->f_matrix)[ 0 ] != AST__BAD ){
zoom = 1;
b = mm->f_matrix + 1;
for( i = 1; i < nin; i++ ){
if( !EQUAL( *b, *( b - 1 ) ) ){
zoom = 0;
break;
}
b++;
}
if( zoom ){
if( ( *invert_list )[ where ] ){
map2 = (AstMapping *) astZoomMap( nin, (mm->i_matrix)[ 0 ], "", status );
} else {
map2 = (AstMapping *) astZoomMap( nin, (mm->f_matrix)[ 0 ], "", status );
}
}
/* If the MatrixMap is a full matrix but all off-diagnal elements are
zero, it can be replaced by a diagonal MatrixMap. */
} else if( mm->form == FULL && nin == nout && mm->f_matrix ){
new_mat = astMalloc( sizeof( double )*nin );
b = mm->f_matrix;
for( i = 0; i < nin && new_mat; i++ ){
for( j = 0; j < nout; j++,b++ ){
if( i == j ) {
new_mat[ i ] = *b;
} else if( *b != 0.0 ) {
new_mat = astFree( new_mat );
break;
}
}
}
if( new_mat ) {
map2 = (AstMapping *) astMatrixMap( nin, nout, 1, new_mat, "",
status );
new_mat = astFree( new_mat );
}
}
/* If the MatrixMap can be replaced, annul the MatrixMap pointer in the
list and replace it with the new Mapping pointer, and indicate that the
forward transformation of the returned Mapping should be used. */
if( map2 ){
(void) astAnnul( ( *map_list )[ where ] );
( *map_list )[ where ] = map2;
( *invert_list )[ where ] = 0;
/* Return the index of the first modified element. */
result = where;
/* If the MatrixMap itself could not be simplified, see if it can be merged
with the Mappings on either side of it in the list. */
/*==========================================================================*/
} else {
/* Store the classes of the neighbouring Mappings in the list. */
class1 = ( where > 0 ) ? astGetClass( ( *map_list )[ where - 1 ] ) : NULL;
class2 = ( where < *nmap - 1 ) ? astGetClass( ( *map_list )[ where + 1 ] ) : NULL;
/* In series. */
/* ========== */
if ( series ) {
/* We first look to see if the MatrixMap can be merged with one of its
neighbours, resulting in a reduction of one in the number of Mappings
in the list. MatrixMaps can merge directly with another MatrixMap, a
ZoomMap, an invertable PermMap, or a UnitMap. */
if( class1 && ( !strcmp( class1, "MatrixMap" ) ||
!strcmp( class1, "ZoomMap" ) ||
!strcmp( class1, "PermMap" ) ||
!strcmp( class1, "UnitMap" ) ) ){
nclass = class1;
i1 = where - 1;
i2 = where;
} else if( class2 && ( !strcmp( class2, "MatrixMap" ) ||
!strcmp( class2, "ZoomMap" ) ||
!strcmp( class2, "PermMap" ) ||
!strcmp( class2, "UnitMap" ) ) ){
nclass = class2;
i1 = where;
i2 = where + 1;
} else {
nclass = NULL;
}
/* Only some PermMaps can be merged with (those which have consistent
forward and inverse mappings). If this is not one of them, set nclass
NULL to indicate this. */
if( nclass && !strcmp( nclass, "PermMap" ) &&
!PermOK( ( *map_list )[ (i1==where)?i2:i1 ], status ) ) nclass = NULL;
/* If the MatrixMap can merge with one of its neighbours, create the merged
Mapping. */
if( nclass ){
if( !strcmp( nclass, "MatrixMap" ) ){
newmm = MatMat( ( *map_list )[ i1 ], ( *map_list )[ i2 ],
( *invert_list )[ i1 ], ( *invert_list )[ i2 ], status );
invert = 0;
} else if( !strcmp( nclass, "ZoomMap" ) ){
if( i1 == where ){
newmm = MatZoom( (AstMatrixMap *)( *map_list )[ i1 ],
(AstZoomMap *)( *map_list )[ i2 ],
( *invert_list )[ i1 ], ( *invert_list )[ i2 ], status );
} else {
newmm = MatZoom( (AstMatrixMap *)( *map_list )[ i2 ],
(AstZoomMap *)( *map_list )[ i1 ],
( *invert_list )[ i2 ], ( *invert_list )[ i1 ], status );
}
invert = 0;
} else if( !strcmp( nclass, "PermMap" ) ){
if( i1 == where ){
newmm = MatPerm( (AstMatrixMap *)( *map_list )[ i1 ],
(AstPermMap *)( *map_list )[ i2 ],
( *invert_list )[ i1 ], ( *invert_list )[ i2 ], 1, status );
} else {
newmm = MatPerm( (AstMatrixMap *)( *map_list )[ i2 ],
(AstPermMap *)( *map_list )[ i1 ],
( *invert_list )[ i2 ], ( *invert_list )[ i1 ], 0, status );
}
invert = 0;
} else {
newmm = astClone( ( *map_list )[ where ] );
invert = ( *invert_list )[ where ];
}
/* If succesfull... */
if( astOK ){
/* Annul the first of the two Mappings, and replace it with the merged
MatrixMap. Also set the invert flag. */
(void) astAnnul( ( *map_list )[ i1 ] );
( *map_list )[ i1 ] = (AstMapping *) newmm;
( *invert_list )[ i1 ] = invert;
/* Annul the second of the two Mappings, and shuffle down the rest of the
list to fill the gap. */
(void) astAnnul( ( *map_list )[ i2 ] );
for ( i = i2 + 1; i < *nmap; i++ ) {
( *map_list )[ i - 1 ] = ( *map_list )[ i ];
( *invert_list )[ i - 1 ] = ( *invert_list )[ i ];
}
/* Clear the vacated element at the end. */
( *map_list )[ *nmap - 1 ] = NULL;
( *invert_list )[ *nmap - 1 ] = 0;
/* Decrement the Mapping count and return the index of the first
modified element. */
( *nmap )--;
result = i1;
}
/* If the MatrixMap could not merge directly with either of its neighbours,
we consider whether it would be worthwhile to swap the MatrixMap with
either of its neighbours. This can only be done for certain classes
of Mapping (WinMaps and some PermMaps), and will usually require both
Mappings to be modified (unless they are commutative). The advantage of
swapping the order of the Mappings is that it may result in the MatrixMap
being adjacent to a Mapping with which it can merge directly on the next
invocation of this function, thus reducing the number of Mappings
in the list. */
} else {
/* Set a flag if we could swap the MatrixMap with its higher neighbour. "do2"
is returned if swapping the Mappings would simplify either of the Mappings. */
if( where + 1 < *nmap ){
swaphi = CanSwap( ( *map_list )[ where ],
( *map_list )[ where + 1 ],
( *invert_list )[ where ],
( *invert_list )[ where + 1 ], &do2, status );
} else {
swaphi = 0;
do2 = 0;
}
/* If so, step through each of the Mappings which follow the MatrixMap,
looking for a Mapping with which the MatrixMap could merge directly. Stop
when such a Mapping is found, or if a Mapping is found with which the
MatrixMap could definitely not swap. Note the number of Mappings which
separate the MatrixMap from the Mapping with which it could merge (if
any). */
nstep2 = -1;
if( swaphi ){
for( i2 = where + 1; i2 < *nmap; i2++ ){
/* See if we can merge with this Mapping. If so, note the number of steps
between the two Mappings and leave the loop. */
nclass = astGetClass( ( *map_list )[ i2 ] );
if( !strcmp( nclass, "MatrixMap" ) ||
!strcmp( nclass, "ZoomMap" ) ||
( !strcmp( nclass, "PermMap" ) && PermOK( ( *map_list )[ i2 ], status ) ) ||
!strcmp( nclass, "UnitMap" ) ) {
nstep2 = i2 - where - 1;
break;
}
/* If there is no chance that we can swap with this Mapping, leave the loop
with -1 for the number of steps to indicate that no merging is possible.
MatrixMaps can swap with WinMaps and some permmaps. */
if( strcmp( nclass, "WinMap" ) &&
strcmp( nclass, "PermMap" ) ) {
break;
}
}
}
/* Do the same working forward from the MatrixMap towards the start of the map
list. */
if( where > 0 ){
swaplo = CanSwap( ( *map_list )[ where - 1 ],
( *map_list )[ where ],
( *invert_list )[ where - 1 ],
( *invert_list )[ where ], &do1, status );
} else {
swaplo = 0;
do1 = 0;
}
nstep1 = -1;
if( swaplo ){
for( i1 = where - 1; i1 >= 0; i1-- ){
nclass = astGetClass( ( *map_list )[ i1 ] );
if( !strcmp( nclass, "MatrixMap" ) ||
( !strcmp( nclass, "PermMap" ) && PermOK( ( *map_list )[ i1 ], status ) ) ||
!strcmp( nclass, "ZoomMap" ) ||
!strcmp( nclass, "UnitMap" ) ) {
nstep1 = where - 1 - i1;
break;
}
if( strcmp( nclass, "WinMap" ) &&
strcmp( nclass, "PermMap" ) ) {
break;
}
}
}
/* Choose which neighbour to swap with so that the MatrixMap moves towards the
nearest Mapping with which it can merge. */
if( do1 || (
nstep1 != -1 && ( nstep2 == -1 || nstep2 > nstep1 ) ) ){
nclass = class1;
i1 = where - 1;
i2 = where;
} else if( do2 || nstep2 != -1 ){
nclass = class2;
i1 = where;
i2 = where + 1;
} else {
nclass = NULL;
}
/* If there is a target Mapping in the list with which the MatrixMap could
merge, consider replacing the supplied Mappings with swapped Mappings to
bring the MatrixMap closer to the target Mapping. */
if( nclass ){
/* Swap the Mappings. */
if (!strcmp( nclass, "WinMap" ) ){
MatWin( (*map_list) + i1, (*invert_list) + i1, where - i1, status );
} else if( !strcmp( nclass, "PermMap" ) ){
MatPermSwap( (*map_list) + i1, (*invert_list) + i1, where - i1, status );
}
/* And then merge them. */
if( where == i1 && where + 1 < *nmap ) { /* Merging upwards */
map2 = astClone( (*map_list)[ where + 1 ] );
nmapt = *nmap - where - 1;
maplt = *map_list + where + 1;
invlt = *invert_list + where + 1;
(void) astMapMerge( map2, 0, series, &nmapt, &maplt, &invlt );
map2 = astAnnul( map2 );
*nmap = where + 1 + nmapt;
} else if( where - 2 >= 0 ) { /* Merging downwards */
map2 = astClone( (*map_list)[ where - 2 ] );
nmapt = *nmap - where + 2;
maplt = *map_list + where - 2 ;
invlt = *invert_list + where - 2;
(void) astMapMerge( map2, 0, series, &nmapt, &maplt, &invlt );
map2 = astAnnul( map2 );
*nmap = where - 2 + nmapt;
}
result = i1;
/* If there is no Mapping available for merging, it may still be
advantageous to swap with a neighbour because the swapped Mapping may
be simpler than the original Mappings. For instance, a PermMap may
strip rows of the MatrixMap leaving only a UnitMap. */
} else if( swaphi || swaplo ) {
/* Try swapping with each possible neighbour in turn. */
for( i = 0; i < 2; i++ ) {
/* Set up the class and pointers for the mappings to be swapped, first
the lower neighbour, then the upper neighbour. */
if( i == 0 && swaplo ){
nclass = class1;
i1 = where - 1;
i2 = where;
} else if( i == 1 && swaphi ){
nclass = class2;
i1 = where;
i2 = where + 1;
} else {
nclass = NULL;
}
/* If we have a Mapping to swap with... */
if( nclass ) {
/* Take copies of the Mapping and Invert flag arrays so we do not change
the supplied values. */
mc[ 0 ] = (AstMapping *) astCopy( ( (*map_list) + i1 )[0] );
mc[ 1 ] = (AstMapping *) astCopy( ( (*map_list) + i1 )[1] );
ic[ 0 ] = ( (*invert_list) + i1 )[0];
ic[ 1 ] = ( (*invert_list) + i1 )[1];
/* Swap these Mappings. */
if( !strcmp( nclass, "WinMap" ) ){
MatWin( mc, ic, where - i1, status );
} else if( !strcmp( nclass, "PermMap" ) ){
MatPermSwap( mc, ic, where - i1, status );
}
/* If neither of the swapped Mappings can be simplified further, then there
is no point in swapping the Mappings, so just annul the map copies. */
smc0 = astSimplify( mc[0] );
smc1 = astSimplify( mc[1] );
if( astGetClass( smc0 ) == astGetClass( mc[0] ) &&
astGetClass( smc1 ) == astGetClass( mc[1] ) ) {
mc[ 0 ] = (AstMapping *) astAnnul( mc[ 0 ] );
mc[ 1 ] = (AstMapping *) astAnnul( mc[ 1 ] );
/* If one or both of the swapped Mappings could be simplified, then annul
the supplied Mappings and return the swapped mappings, storing the index
of the first modified Mapping. */
} else {
(void ) astAnnul( ( (*map_list) + i1 )[0] );
(void ) astAnnul( ( (*map_list) + i1 )[1] );
( (*map_list) + i1 )[0] = mc[ 0 ];
( (*map_list) + i1 )[1] = mc[ 1 ];
( (*invert_list) + i1 )[0] = ic[ 0 ];
( (*invert_list) + i1 )[1] = ic[ 1 ];
result = i1;
break;
}
/* Annul the simplied Mappings */
smc0 = astAnnul( smc0 );
smc1 = astAnnul( smc1 );
}
}
}
}
}
}
/* Return the result. */
return result;
}
static int *MapSplit( AstMapping *this_map, int nin, const int *in, AstMapping **map, int *status ){
/*
* Name:
* MapSplit
* Purpose:
* Create a Mapping representing a subset of the inputs of an existing
* MatrixMap.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* int *MapSplit( AstMapping *this, int nin, const int *in, AstMapping **map, int *status )
* Class Membership:
* MatrixMap method (over-rides the protected astMapSplit method
* inherited from the Mapping class).
* Description:
* This function creates a new Mapping by picking specified inputs from
* an existing MatrixMap. This is only possible if the specified inputs
* correspond to some subset of the MatrixMap outputs. That is, there
* must exist a subset of the MatrixMap outputs for which each output
* depends only on the selected MatrixMap inputs, and not on any of the
* inputs which have not been selected. In addition, outputs that are
* not in this subset must not depend on any selected inputs. If these
* conditions are not met by the supplied MatrixMap, then a NULL Mapping
* is returned.
* Parameters:
* this
* Pointer to the MatrixMap to be split (the MatrixMap is not actually
* modified by this function).
* nin
* The number of inputs to pick from "this".
* in
* Pointer to an array of indices (zero based) for the inputs which
* are to be picked. This array should have "nin" elements. If "Nin"
* is the number of inputs of the supplied MatrixMap, then each element
* should have a value in the range zero to Nin-1.
* map
* Address of a location at which to return a pointer to the new
* Mapping. This Mapping will have "nin" inputs (the number of
* outputs may be different to "nin"). A NULL pointer will be
* returned if the supplied MatrixMap has no subset of outputs which
* depend only on the selected inputs.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to a dynamically allocated array of ints. The number of
* elements in this array will equal the number of outputs for the
* returned Mapping. Each element will hold the index of the
* corresponding output in the supplied MatrixMap. The array should be
* freed using astFree when no longer needed. A NULL pointer will
* be returned if no output Mapping can be created.
* Notes:
* - If this function is invoked with the global error status set,
* or if it should fail for any reason, then NULL values will be
* returned as the function value and for the "map" pointer.
*/
/* Local Variables: */
AstMatrixMap *this; /* Pointer to MatrixMap structure */
double *mat; /* Pointer to matrix for supplied MatrixMap */
double *pmat; /* Pointer to row start in returned matrix */
double *prow; /* Pointer to row start in supplied matrix */
double *rmat; /* Pointer to matrix for returned MatrixMap */
double el; /* Next element value in supplied matrix */
int *result; /* Pointer to returned array */
int good; /* Would new matrix contain any good values/ */
int i; /* Loop count */
int icol; /* Column index within supplied MatrixMap */
int iel; /* Index of next element from the input matrix */
int irow; /* Row index within supplied MatrixMap */
int isel; /* Does output depend on any selected inputs? */
int ncol; /* Number of columns (inputs) in supplied MatrixMap */
int nout; /* Number of outputs in returned MatrixMap */
int nrow; /* Number of rows (outputs) in supplied MatrixMap */
int ok; /* Are input indices OK? */
int sel; /* Does any output depend on selected inputs? */
int unsel; /* Does any output depend on unselected inputs? */
/* Initialise */
result = NULL;
*map = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Invoke the parent astMapSplit method to see if it can do the job. */
result = (*parent_mapsplit)( this_map, nin, in, map, status );
/* If not, we provide a special implementation here. */
if( !result ) {
/* Get a pointer to the MatrixMap structure. */
this = (AstMatrixMap *) this_map;
/* Get the number of inputs and outputs. */
ncol = astGetNin( this );
nrow = astGetNout( this );
/* Check the supplied input indices are usable. */
ok = 1;
for( i = 0; i < nin; i++ ) {
if( in[ i ] < 0 || in[ i ] >= ncol ) {
ok = 0;
break;
}
}
if( ok ) {
/* Ensure the MatrixMap is stored in full form. */
ExpandMatrix( this, status );
/* Allocate the largest array that could be necessary to hold the
returned array of Mapping outputs. */
result = astMalloc( sizeof(int)*(size_t) nrow );
/* Allocate the largest array that could be necessary to hold the
matrix representing the returned MatrixMap. */
rmat = astMalloc( sizeof(double)*(size_t) (nrow*ncol) );
/* Get the matrix which defines the current forward transformation. This
takes into account whether the MatrixMap has been inverted or not. */
if( astGetInvert( this ) ) {
mat = this->i_matrix;
} else {
mat = this->f_matrix;
}
/* We cannot create the require Mapping if the matrix is undefined. */
if( !mat || !astOK ) {
ok = 0;
nout = 0;
good = 0;
/* Otherwise, loop round all the rows in the matrix. */
} else {
nout = 0;
good = 0;
pmat = rmat;
iel = 0;
for( irow = 0; irow < nrow; irow++ ) {
/* Indicate that this output (i.e. row of the matrix) depends on neither
selected nor unselected inputs as yet. */
sel = 0;
unsel = 0;
/* Save a pointer to the first element of this row in the MatrixMap
matrix. */
prow = mat + iel;
/* Loop round all the elements in the current row of the matrix. */
for( icol = 0; icol < ncol; icol++ ) {
/* If this element is non-zero and non-bad, then output "irow" depends on
input "icol". */
el = mat[ iel++ ];
if( el != 0.0 && el != AST__BAD ) {
/* Is input "icol" one of the selected inputs? */
isel = 0;
for( i = 0; i < nin; i++ ) {
if( in[ i ] == icol ) {
isel = 1;
break;
}
}
/* If so, note that this output depends on selected inputs. Otherwise note
it depends on unselected inputs. */
if( isel ) {
sel = 1;
} else {
unsel = 1;
}
}
}
/* If this output depends only on selected inputs, we can include it in
the returned Mapping.*/
if( sel && !unsel ) {
/* Store the index of the output within the original MatrixMap. */
result[ nout ] = irow;
/* Increment the number of outputs in the returned Mapping. */
nout++;
/* Copy the elements of the current matrix row which correspond to the
selected inputs into the new matrix. */
for( i = 0; i < nin; i++ ) {
if( astISGOOD( prow[ in[ i ] ] ) ) {
*(pmat++) = prow[ in[ i ] ];
good = 1;
}
}
}
/* If this output depends on a selected input, but also depends on an
unselected input, we cannot split the MatrixMap. */
if( sel && unsel ) {
ok = 0;
break;
}
}
}
/* If the returned Mapping can be created, create it. */
if( ok && nout > 0 && good ) {
*map = (AstMapping *) astMatrixMap( nin, nout, 0, rmat, "", status );
/* Otherwise, free the returned array. */
} else {
result = astFree( result );
}
/* Free resources. */
rmat = astFree( rmat );
/* Re-compress the supplied MatrixMap. */
CompressMatrix( this, status );
}
}
/* Free returned resources if an error has occurred. */
if( !astOK ) {
result = astFree( result );
*map = astAnnul( *map );
}
/* Return the list of output indices. */
return result;
}
static AstMatrixMap *MatMat( AstMapping *map1, AstMapping *map2, int inv1,
int inv2, int *status ){
/*
* Name:
* MatMat
* Purpose:
* Create a merged MatrixMap from two supplied MatrixMaps.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* AstMatrixMap *MatMat( AstMapping *map1, AstMapping *map2, int inv1,
* int inv2, int *status )
* Class Membership:
* MatrixMap member function
* Description:
* This function creates a new MatrixMap which performs a mapping
* equivalent to applying the two supplied MatrixMaps in series, in the
* directions specified by the "invert" flags (the Invert attributes of
* the supplied MatrixMaps are ignored).
* Parameters:
* map1
* A pointer to the MatrixMap to apply first.
* map2
* A pointer to the MatrixMap to apply second.
* inv1
* The invert flag to use with map1. A value of zero causes the forward
* mapping to be used, and a non-zero value causes the inverse
* mapping to be used.
* inv2
* The invert flag to use with map2.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the new MatrixMap.
* Notes:
* - The forward direction of the returned MatrixMap is equivalent to the
* combined effect of the two supplied MatrixMap, operating in the
* directions specified by "inv1" and "inv2".
* - 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: */
AstMatrixMap *result; /* Pointer to output MatrixMap */
int invert[ 2 ]; /* Original invert flags */
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Initialise the returned pointer. */
result = NULL;
/* Temporarily set their Invert attributes to the supplied values. */
invert[ 0 ] = astGetInvert( map1 );
astSetInvert( map1, inv1 );
invert[ 1 ] = astGetInvert( map2 );
astSetInvert( map2, inv2 );
/* Create a new MatrixMap by multiplying them together. */
result = astMtrMult( (AstMatrixMap *) map1, (AstMatrixMap *) map2 );
/* Re-instate the original settings of the Invert attributes for the
supplied MatrixMaps. */
astSetInvert( map1, invert[ 0 ] );
astSetInvert( map2, invert[ 1 ] );
/* If an error has occurred, annull the returned MatrixMap. */
if( !astOK ) result = astAnnul( result );
/* Return a pointer to the output MatrixMap. */
return result;
}
static AstMatrixMap *MatPerm( AstMatrixMap *mm, AstPermMap *pm, int minv,
int pinv, int mat1, int *status ){
/*
* Name:
* MatPerm
* Purpose:
* Create a MatrixMap by merging a MatrixMap and a PermMap.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* AstMatrixMap *MatPerm( AstMatrixMap *mm, AstPermMap *pm, int minv,
* int pinv, int mat1, int *status )
* Class Membership:
* MatrixMap member function
* Description:
* This function creates a new MatrixMap which performs a mapping
* equivalent to applying the two supplied Mappings in series in the
* directions specified by the "invert" flags (the Invert attributes of
* the supplied MatrixMaps are ignored).
* Parameters:
* mm
* A pointer to the MatrixMap.
* pm
* A pointer to the PermMap.
* minv
* The invert flag to use with mm. A value of zero causes the forward
* mapping to be used, and a non-zero value causes the inverse
* mapping to be used.
* pinv
* The invert flag to use with pm.
* mat1
* If non-zero, then "mm" is applied first followed by "pm". Otherwise,
* "pm" is applied first followed by "mm".
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the new MatrixMap.
* Notes:
* - The forward direction of the returned MatrixMap is equivalent to the
* combined effect of the two supplied Mappings, operating in the
* directions specified by "pinv" and "minv".
* - 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: */
AstMatrixMap *mm2; /* Pointer to an intermediate MatrixMap */
AstMatrixMap *result; /* Pointer to output MatrixMap */
AstPointSet *pset1; /* Pointer to a PointSet holding unpermuted unit vectors */
AstPointSet *pset2; /* Pointer to a PointSet holding permuted unit vectors */
double *matrix; /* Pointer to a matrix representing the PermMap */
double *p; /* Pointer to next matrix element */
double **ptr1; /* Pointer to the data in pset1 */
double **ptr2; /* Pointer to the data in pset2 */
int i; /* Axis index */
int j; /* Point index */
int nax; /* No. of axes in the PermMap */
int old_minv; /* Original setting of MatrixMap Invert attribute */
int old_pinv; /* Original setting of PermMap Invert attribute */
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Initialise the returned pointer. */
result = NULL;
/* Temporarily set the Invert attributes of both Mappings to the supplied
values. */
old_minv = astGetInvert( mm );
astSetInvert( mm, minv );
old_pinv = astGetInvert( pm );
astSetInvert( pm, pinv );
/* Get the number of axes in the PermMap. The PermMap will have the same
number of input and output axes because a check has already been made on
it to ensure that this is so (in function PermOK). */
nax = astGetNin( pm );
/* We first represent the PermMap as a MatrixMap containing elements with
values zero or one. Each row of this matrix is obtained by transforming a
unit vector along each axis using the inverse PermMap. Allocate memory
to hold the matrix array, and create a PointSet holding the unit
vectors. */
matrix = (double *) astMalloc( sizeof( double )*(size_t)( nax*nax ) );
pset1 = astPointSet( nax, nax, "", status );
ptr1 = astGetPoints( pset1 );
pset2 = astPointSet( nax, nax, "", status );
ptr2 = astGetPoints( pset2 );
if( astOK ){
for( i = 0; i < nax; i++ ){
for( j = 0; j < nax; j++ ) ptr1[ i ][ j ] = 0.0;
ptr1[ i ][ i ] = 1.0;
}
/* Transform these unit vectors using the inverse PermMap. */
(void) astTransform( pm, pset1, 0, pset2 );
/* Copy the transformed vectors into the matrix array. */
p = matrix;
for( j = 0; j < nax; j++ ){
for( i = 0; i < nax; i++ ) *(p++) = ptr2[ i ][ j ];
}
/* Create a MatrixMap holding this array. */
mm2 = astMatrixMap( nax, nax, 0, matrix, "", status );
/* Create a new MatrixMap equal to the product of the supplied MatrixMap
and the MatrixMap just created from the PermMap. */
if( mat1 ){
result = astMtrMult( mm, mm2 );
} else {
result = astMtrMult( mm2, mm );
}
/* Free everything. */
mm2 = astAnnul( mm2 ) ;
}
pset2 = astAnnul( pset2 );
pset1 = astAnnul( pset1 );
matrix = (double *) astFree( (void *) matrix );
/* Re-instate the original settings of the Invert attribute for the
supplied Mappings. */
astSetInvert( mm, old_minv );
astSetInvert( pm, old_pinv );
/* If an error has occurred, annull the returned MatrixMap. */
if( !astOK ) result = astAnnul( result );
/* Return a pointer to the output MatrixMap. */
return result;
}
static void MatPermSwap( AstMapping **maps, int *inverts, int imm, int *status ){
/*
* Name:
* MatPermSwap
* Purpose:
* Swap a PermMap and a MatrixMap.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* void MatPermSwap( AstMapping **maps, int *inverts, int imm )
* Class Membership:
* MatrixMap member function
* Description:
* A list of two Mappings is supplied containing a PermMap and a
* MatrixMap. These Mappings are annulled, and replaced with
* another pair of Mappings consisting of a PermMap and a MatrixMap
* in the opposite order. These Mappings are chosen so that their
* combined effect is the same as the original pair of Mappings.
* Parameters:
* maps
* A pointer to an array of two Mapping pointers.
* inverts
* A pointer to an array of two invert flags.
* imm
* The index within "maps" of the MatrixMap.
* Notes:
* - There are restictions on the sorts of PermMaps which can be
* swapped with a MatrixMap -- see function CanSwap. It is assumed
* that the supplied MatrixMap and PermMap satisfy these requirements.
*/
/* Local Variables: */
AstMatrixMap *mm; /* Pointer to the supplied MatrixMap */
AstMatrixMap *mmnew; /* Pointer to new MatrixMap */
AstMatrixMap *smmnew; /* Pointer to new simplified MatrixMap */
AstPermMap *pm; /* Pointer to the supplied PermMap */
AstPermMap *pmnew; /* Pointer to new PermMap */
AstPermMap *spmnew; /* Pointer to new simplified PermMap */
double *consts; /* Pointer to constants array */
double *matrix; /* Supplied array of matrix elements */
double *out_el; /* Pointer to next element of new MatrixMap */
double *out_mat; /* Matrix elements for new MatrixMap */
double c; /* Constant */
double matel; /* Matrix element */
int *inperm; /* Pointer to input axis permutation array */
int *outperm; /* Pointer to output axis permutation array */
int col; /* Index of matrix column */
int i; /* Axis count */
int k; /* Axis count */
int nin; /* No. of axes in supplied PermMap */
int nout; /* No. of axes in returned PermMap */
int old_pinv; /* Invert value for the supplied PermMap */
int row; /* Index of matrix row */
/* Check the global error status. */
if ( !astOK ) return;
/* Initialise variables to avoid "used of uninitialised variable"
messages from dumb compilers. */
mmnew = NULL;
pmnew = NULL;
/* Store pointers to the supplied PermMap and the MatrixMap. */
pm = (AstPermMap *) maps[ 1 - imm ];
mm = (AstMatrixMap *) maps[ imm ];
/* Temporarily set the Invert attribute of the supplied PermMap to the
supplied value. */
old_pinv = astGetInvert( pm );
astSetInvert( pm, inverts[ 1 - imm ] );
/* Ensure the MatrixMap is stored in full form. */
ExpandMatrix( mm, status );
/* Store a pointer to the required array of matrix elements. */
if( inverts[ imm ] ) {
matrix = mm->i_matrix;
} else {
matrix = mm->f_matrix;
}
/* Get the number of input and output axes of the PermMap. */
nin = astGetNin( pm );
nout = astGetNout( pm );
/* Allocate memory to hold the matrix elements for the swapped MatrixMap.
The number of rows and olumns in the new matrix must equal the number of
input or output axes for the PermMap, depending on whether the PermMap
or MatrixMap is applied first. */
if( imm == 0 ) {
out_mat = (double *) astMalloc( sizeof( double )*(size_t)( nout*nout ) );
} else {
out_mat = (double *) astMalloc( sizeof( double )*(size_t)( nin*nin ) );
}
/* We need to know the axis permutation arrays and constants array for
the PermMap. */
PermGet( pm, &outperm, &inperm, &consts, status );
if( astOK ) {
/* First deal with cases where the MatrixMap is applied first. */
if( imm == 0 ) {
/* Consider each output axis of the PermMap. */
for( i = 0; i < nout; i++ ) {
/* If this output is connected to one of the input axes... */
row = outperm[ i ];
if( row >= 0 && row < nin ) {
/* Permute the row of the supplied matrix which feeds the corresponding
PermMap input axis (i.e. axis outperm[k] ) using the forward PermMap.
Store zeros for any output axes which are assigned constants. This forms
row i of the new MatrixMap. */
out_el = out_mat + nout*i;
for( k = 0; k < nout; k++ ){
col = outperm[ k ];
if( col >= 0 && col < nin ) {
*(out_el++) = *( matrix + nin*row + col );
} else {
*(out_el++) = 0.0;
}
}
/* If this output is asigned a constant value, use a "diagonal" vector for
row i of the new MatrixMap (i.e. all zeros except for a 1.0 in column
i ). */
} else {
out_el = out_mat + nout*i;
for( k = 0; k < nout; k++ ) {
if( k != i ) {
*(out_el++) = 0.0;
} else {
*(out_el++) = 1.0;
}
}
}
}
/* Create the new MatrixMap. */
mmnew = astMatrixMap( nout, nout, 0, out_mat, "", status );
/* Any PermMap inputs which are assigned a constant value need to be
changed now, since they will no longer be scaled by the inverse
MatrixMap. CanSwap ensures that the inverse MatrixMap produces a
simple scaling for constant axes, so we change the PermMap constant
to be the constant AFTER scaling by the inverse MatrixMap.
Consider each input axis of the PermMap. */
for( i = 0; i < nin; i++ ) {
/* If this input is assigned a constant value... */
if( inperm[ i ] < 0 ) {
/* Divide the supplied constant value by the corresponding diagonal term
in the supplied MatrixMap. */
c = consts[ -inperm[ i ] - 1 ];
if( c != AST__BAD ) {
matel = matrix[ i*( nin + 1 ) ];
if( matel != 0.0 && matel != AST__BAD ) {
consts[ -inperm[ i ] - 1 ] /= matel;
} else {
consts[ -inperm[ i ] - 1 ] = AST__BAD;
}
}
}
}
/* Now deal with cases where the PermMap is applied first. */
} else {
/* Consider each input axis of the PermMap. */
for( i = 0; i < nin; i++ ) {
/* If this input is connected to one of the output axes... */
row = inperm[ i ];
if( row >= 0 && row < nout ) {
/* Permute the row of the supplied matrix which feeds the corresponding
PermMap output axis (i.e. axis inperm[k] ) using the inverse PermMap.
Store zeros for any input axes which are assigned constants. This forms
row i of the new MatrixMap. */
out_el = out_mat + nin*i;
for( k = 0; k < nin; k++ ){
col = inperm[ k ];
if( col >= 0 && col < nout ) {
*(out_el++) = *( matrix + nout*row + col );
} else {
*(out_el++) = 0.0;
}
}
/* If this input is asigned a constant value, use a "diagonal" vector for
row i of the new MatrixMap (i.e. all zeros except for a 1.0 in column
i ). */
} else {
out_el = out_mat + nin*i;
for( k = 0; k < nin; k++ ) {
if( k != i ) {
*(out_el++) = 0.0;
} else {
*(out_el++) = 1.0;
}
}
}
}
/* Create the new MatrixMap. */
mmnew = astMatrixMap( nin, nin, 0, out_mat, "", status );
/* Any PermMap outputs which are assigned a constant value need to be
changed now, since they will no longer be scaled by the forward
MatrixMap. CanSwap ensures that the forward MatrixMap produces a
simple scaling for constant axes, so we change the PermMap constant
to be the constant AFTER scaling by the forward MatrixMap.
Consider each output axis of the PermMap. */
for( i = 0; i < nout; i++ ) {
/* If this output is assigned a constant value... */
if( outperm[ i ] < 0 ) {
/* Multiple the supplied constant value by the corresponding diagonal term in
the supplied MatrixMap. */
c = consts[ -outperm[ i ] - 1 ];
if( c != AST__BAD ) {
matel = matrix[ i*( nout + 1 ) ];
if( matel != AST__BAD ) {
consts[ -outperm[ i ] - 1 ] *= matel;
} else {
consts[ -outperm[ i ] - 1 ] = AST__BAD;
}
}
}
}
}
/* Create a new PermMap (since the constants may have changed). */
pmnew = astPermMap( nin, inperm, nout, outperm, consts, "", status );
/* Free the axis permutation and constants arrays. */
outperm = (int *) astFree( (void *) outperm );
inperm = (int *) astFree( (void *) inperm );
consts = (double *) astFree( (void *) consts );
}
/* Free the memory used to hold the new matrix elements. */
out_mat = (double *) astFree( (void *) out_mat );
/* Ensure the supplied MatrixMap is stored back in compressed form. */
CompressMatrix( mm, status );
/* Re-instate the original value of the Invert attribute of the supplied
PermMap. */
astSetInvert( pm, old_pinv );
if( astOK ) {
/* Annul the supplied PermMap. */
(void) astAnnul( pm );
/* Simplify the returned Mappings. */
spmnew = astSimplify( pmnew );
pmnew = astAnnul( pmnew );
smmnew = astSimplify( mmnew );
mmnew = astAnnul( mmnew );
/* Store a pointer to the new PermMap in place of the supplied MatrixMap. This
PermMap should be used in its forward direction. */
maps[ imm ] = (AstMapping *) spmnew;
inverts[ imm ] = astGetInvert( spmnew );
/* Annul the supplied matrixMap. */
(void) astAnnul( mm );
/* Store a pointer to the new MatrixMap. This MatrixMap should be used in
its forward direction. */
maps[ 1 - imm ] = (AstMapping *) smmnew;
inverts[ 1 - imm ] = astGetInvert( smmnew );
}
/* Return. */
return;
}
static void MatWin( AstMapping **maps, int *inverts, int imm, int *status ){
/*
* Name:
* MatWin
* Purpose:
* Swap a WinMap and a MatrixMap.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* void MatWin( AstMapping **maps, int *inverts, int imm, int *status )
* Class Membership:
* WinMap member function
* Description:
* A list of two Mappings is supplied containing a WinMap and a
* MatrixMap. These Mappings are annulled, and replaced with
* another pair of Mappings consisting of a WinMap and a MatrixMap
* in the opposite order. These Mappings are chosen so that their
* combined effect is the same as the original pair of Mappings.
* The scale factors in the returned WinMap are always unity (i.e.
* the differences in scaling get absorbed into the returned
* MatrixMap).
* Parameters:
* maps
* A pointer to an array of two Mapping pointers.
* inverts
* A pointer to an array of two invert flags.
* imm
* The index within "maps" of the MatrixMap.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
AstMatrixMap *m1; /* Pointer to Diagonal scale factor MatrixMap */
AstMatrixMap *m2; /* Pointer to returned MatrixMap */
AstMatrixMap *sm2; /* Pointer to simplified returned MatrixMap */
AstMatrixMap *mm; /* Pointer to the supplied MatrixMap */
AstPointSet *pset1; /* Shift terms from supplied WinMap */
AstPointSet *pset2; /* Shift terms for returned WinMap */
AstWinMap *w1; /* Pointer to the returned WinMap */
AstWinMap *sw1; /* Pointer to the simplified returned WinMap */
AstWinMap *wm; /* Pointer to the supplied WinMap */
double **ptr1; /* Pointer to pset1 data */
double **ptr2; /* Pointer to pset2 data */
double *a; /* Array of shift terms from supplied WinMap */
double *aa; /* Pointer to next shift term */
double *b; /* Array of scale terms from supplied WinMap */
double *bb; /* Pointer to next scale term */
int i; /* Axis count */
int nin; /* No. of axes in supplied WinMap */
int nout; /* No. of axes in returned WinMap */
int old_minv; /* Invert value for the supplied MatrixMap */
int old_winv; /* Invert value for the supplied WinMap */
/* Check the global error status. */
if ( !astOK ) return;
/* Store pointers to the supplied WinMap and the MatrixMap. */
wm = (AstWinMap *) maps[ 1 - imm ];
mm = (AstMatrixMap *) maps[ imm ];
/* Temporarily set the Invert attribute of the supplied Mappings to the
supplied values. */
old_winv = astGetInvert( wm );
astSetInvert( wm, inverts[ 1 - imm ] );
old_minv = astGetInvert( mm );
astSetInvert( mm, inverts[ imm ] );
/* Get copies of the shift and scale terms used by the WinMap. This
also returns the number of axes in the WinMap. */
nin = astWinTerms( wm, &a, &b );
/* Create a diagonal MatrixMap holding the scale factors from the
supplied WinMap. */
m1 = astMatrixMap( nin, nin, 1, b, "", status );
/* Create a PointSet holding a single position given by the shift terms
in the supplied WinMap. */
pset1 = astPointSet( 1, nin, "", status );
ptr1 = astGetPoints( pset1 );
if( astOK ){
aa = a;
for( i = 0; i < nin; i++ ) ptr1[ i ][ 0 ] = *(aa++);
}
/* First deal with cases when the WinMap is applied first, followed by
the MatrixMap. */
if( imm == 1 ){
/* Multiply the diagonal matrix holding the WinMap scale factors by the
supplied matrix. The resulting MatrixMap is the one to return in the
map list. */
m2 = astMtrMult( m1, mm );
/* Transform the position given by the shift terms from the supplied
WinMap using the supplied MatrixMap to get the shift terms for
the returned WinMap. */
pset2 = astTransform( mm, pset1, 1, NULL );
/* Now deal with cases when the MatrixMap is applied first, followed by
the WinMap. */
} else {
/* Multiply the supplied MatrixMap by the diagonal matrix holding scale
factors from the supplied WinMap. The resulting MatrixMap is the one to
return in the map list. */
m2 = astMtrMult( mm, m1 );
/* Transform the position given by the shift terms from the supplied
WinMap using the inverse of the returned MatrixMap to get the shift
terms for the returned WinMap. */
pset2 = astTransform( m2, pset1, 0, NULL );
}
/* Re-instate the original value of the Invert attributes of the supplied
Mappings. */
astSetInvert( wm, old_winv );
astSetInvert( mm, old_minv );
/* Get pointers to the shift terms for the returned WinMap. */
ptr2 = astGetPoints( pset2 );
/* Create the returned WinMap, initially with undefined corners. The number of
axes in the WinMap must equal the number of shift terms. */
nout = astGetNcoord( pset2 );
w1 = astWinMap( nout, NULL, NULL, NULL, NULL, "", status );
/* If succesful, store the scale and shift terms in the WinMap. The scale
terms are always unity. */
if( astOK ){
bb = w1->b;
aa = w1->a;
for( i = 0; i < nout; i++ ) {
*(bb++) = 1.0;
*(aa++) = ptr2[ i ][ 0 ];
}
/* Replace the supplied Mappings and invert flags with the ones found
above. Remember that the order of the Mappings is now swapped */
(void) astAnnul( maps[ 0 ] );
(void) astAnnul( maps[ 1 ] );
sw1 = astSimplify( w1 );
w1 = astAnnul( w1 );
maps[ imm ] = (AstMapping *) sw1;
inverts[ imm ] = astGetInvert( sw1 );
sm2 = astSimplify( m2 );
m2 = astAnnul( m2 );
maps[ 1 - imm ] = (AstMapping *) sm2;
inverts[ 1 - imm ] = astGetInvert( sm2 );
}
/* Annul the MatrixMap and PointSet holding the scale and shift terms from the
supplied WinMap. */
m1 = astAnnul( m1 );
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
/* Free the copies of the scale and shift terms from the supplied WinMap. */
b = (double *) astFree( (void *) b );
a = (double *) astFree( (void *) a );
/* Return. */
return;
}
static AstMatrixMap *MatZoom( AstMatrixMap *mm, AstZoomMap *zm, int minv,
int zinv, int *status ){
/*
* Name:
* MatZoom
* Purpose:
* Create a MatrixMap by merging a MatrixMap and a ZoomMap.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* AstMatrixMap *MatZoom( AstMatrixMap *mm, AstZoomMap *zm, int minv,
* int zinv, int *status )
* Class Membership:
* MatrixMap member function
* Description:
* This function creates a new MatrixMap which performs a mapping
* equivalent to applying the two supplied Mappings in series in the
* directions specified by the "invert" flags (the Invert attributes of
* the supplied MatrixMaps are ignored).
* Parameters:
* mm
* A pointer to the MatrixMap.
* zm
* A pointer to the ZoomMap.
* minv
* The invert flag to use with mm. A value of zero causes the forward
* mapping to be used, and a non-zero value causes the inverse
* mapping to be used.
* zinv
* The invert flag to use with zm.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the new MatrixMap.
* Notes:
* - The forward direction of the returned MatrixMap is equivalent to the
* combined effect of the two supplied Mappings, operating in the
* directions specified by "zinv" and "minv".
* - 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: */
AstMatrixMap *mm2; /* Pointer to intermediate MatrixMap */
AstMatrixMap *result; /* Pointer to output MatrixMap */
double *matrix; /* Pointer to diagonal matrix elements array */
double zfac; /* Zoom factor */
int i; /* Axis index */
int nrow; /* No. of rows in the MatrixMap */
int old_minv; /* Original setting of MatrixMap Invert attribute */
int old_zinv; /* Original setting of ZoomMap Invert attribute */
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Initialise the returned pointer. */
result = NULL;
/* Temporarily set the Invert attributes of both Mappings to the supplied
values. */
old_minv = astGetInvert( mm );
astSetInvert( mm, minv );
old_zinv = astGetInvert( zm );
astSetInvert( zm, zinv );
/* Get the number of rows in the MatrixMap (i.e. the number of output
axes). */
nrow = astGetNout( mm );
/* Get the zoom factor implemented by the ZoomMap. Invert it if necessary
since astGetZoom does not take account of the Invert setting. */
zfac = astGetZoom( zm );
if( zinv ) zfac = 1.0 / zfac;
/* Create a diagonal matrix map in which each diagonal element is equal
to the zoom factor. */
matrix = (double *) astMalloc( sizeof( double )*(size_t) nrow );
if( astOK ) {
for( i = 0; i < nrow; i++ ) matrix[ i ] = zfac;
}
mm2 = astMatrixMap( nrow, nrow, 1, matrix, "", status );
matrix = (double *) astFree( (void *) matrix );
/* Create a new MatrixMap holding the product of the supplied MatrixMap
and the diagonal MatrixMap just created. */
result = astMtrMult( mm, mm2 );
mm2 = astAnnul( mm2 );
/* Re-instate the original settings of the Invert attribute for the
supplied Mappings. */
astSetInvert( mm, old_minv );
astSetInvert( zm, old_zinv );
/* If an error has occurred, annull the returned MatrixMap. */
if( !astOK ) result = astAnnul( result );
/* Return a pointer to the output MatrixMap. */
return result;
}
/* 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). */
static AstMatrixMap *MtrMult( AstMatrixMap *this, AstMatrixMap *a, int *status ){
/*
*+
* Name:
* astMtrMult
* Purpose:
* Multiply a MatrixMap by another MatrixMap.
* Type:
* Protected virtual function.
* Synopsis:
* #include "matrixmap.h"
* AstMatrixMap *MtrMult( astMatrixMap *this, astMatrixMap *a )
* Class Membership:
* MatrixMap method
* Description:
* This function multiples the matrices given by "this" and "a", returning
* a pointer to a new MatrixMap holding the product "a x this".
*
* The number of columns in the "a" matrix must match the number of
* rows in the "this" matrix. The number of rows in the returned
* MatrixMap is equal to the number of rows in "a", and the number of
* columns is the same as the number of rows in "this".
* Parameters:
* this
* Pointer to the first MatrixMap.
* a
* Pointer to a second MatrixMap.
* Returned Value:
* A pointer to the product MatrixMap.
* Notes:
* - An error is reported if the two MatrixMaps have incompatible
* shapes, or if either MatrixMap does not have a defined forward
* transformation.
* - A null Object pointer will also be returned if this function
* is invoked with the AST error status set, or if it should fail
* for any reason.
*-
*/
/* Local variables. */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstMatrixMap *new; /* New MatrixMap holding the product matrix */
double *a_matrix; /* Pointer to the forward "a" matrix */
double *a_row; /* Pointer to start of current row in "a" */
double a_val; /* Current element value from "a" */
double factor; /* Diagonal matrix term */
double *new_matrix; /* Pointer to the new forward "this" matrix */
double *new_val; /* Pointer to current output element value */
double sum; /* Dot product value */
double *this_col; /* Pointer to start of current column in "this" */
double *this_matrix; /* Pointer to the forward "this" matrix */
double this_val; /* Current element value from "this" */
int col; /* Current output column number */
int i; /* Loop count */
int minrow; /* Min. number of rows in "a" or "this" */
int ncol_a; /* No. of columns in the "a" MatrixMap */
int ncol_this; /* No. of columns in the "this" MatrixMap */
int nrow_a; /* No. of rows in the "a" MatrixMap */
int nrow_this; /* No. of rows in the "this" MatrixMap */
int row; /* Current output row number */
/* Return a NULL pointer if an error has already occurred. */
if ( !astOK ) return NULL;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(NULL);
/* Initialise */
new = NULL;
/* Report an error if eitherof the MatrixMaps doe snot have a defined
forward transformation.*/
if( !astGetTranForward( this ) ){
astError( AST__MTRML, "astMtrMult(%s): Cannot find the product of 2 "
"MatrixMaps- the first MatrixMap has no forward transformation.", status,
astClass(this) );
return NULL;
}
if( !astGetTranInverse( this ) ){
astError( AST__MTRML, "astMtrMult(%s): Cannot find the product of 2 "
"MatrixMaps- the second MatrixMap has no forward transformation.", status,
astClass(this) );
return NULL;
}
/* Report an error if the shapes of the two matrices are incompatible. */
nrow_a = astGetNout( a );
ncol_a = astGetNin( a );
nrow_this = astGetNout( this );
ncol_this = astGetNin( this );
if( ncol_a != nrow_this && astOK ){
astError( AST__MTRML, "astMtrMult(%s): Number of rows in the first "
"MatrixMap (%d) does not equal number of columns in the "
"second MatrixMap (%d).", status, astClass(this), nrow_this, ncol_a );
return NULL;
}
/* Store the minimum number of rows in either matrix for later use. */
if( nrow_a < nrow_this ){
minrow = nrow_a;
} else {
minrow = nrow_this;
}
/* Ensure that "this" is stored in FULL form (i.e. with all elements
stored explicitly, even if the matrix is a unit or diagonal matrix). */
ExpandMatrix( this, status );
/* Store pointers to the current forward matrices (taking into
account the current states of the Mapping inversion flags ). */
this_matrix = astGetInvert( this ) ? this->i_matrix : this->f_matrix;
a_matrix = astGetInvert( a ) ? a->i_matrix : a->f_matrix;
/* Get memory to hold the product matrix in full form. */
new_matrix = (double *) astMalloc( sizeof( double )*
(size_t)( nrow_a*ncol_this ) );
if( astOK ){
/* First deal with cases where the "a" MatrixMap represents a unit
matrix. */
if( a->form == UNIT ){
/* Copy the required number of rows from "this" to "new". */
(void) memcpy( (void *) new_matrix, (const void *) this_matrix,
sizeof(double)*(size_t)( minrow*ncol_this ) );
/* If there are insufficient rows in "this", append some zero-filled rows. */
if( minrow < nrow_a ){
for( i = minrow*ncol_this; i < nrow_a*ncol_this; i++ ){
new_matrix[ i ] = 0.0;
}
}
/* Now deal with cases where the "a" MatrixMap represents a diagonal
matrix. */
} else if( a->form == DIAGONAL ){
/* Scale the required number of rows from "this" storing them in "new",
and checking for bad values. */
i = 0;
for( row = 0; row < minrow; row++ ){
factor = a_matrix[ row ];
if( factor != AST__BAD ){
for( col = 0; col < ncol_this; col++ ){
this_val = this_matrix[ i ];
if( this_val != AST__BAD ){
new_matrix[ i ] = this_val*factor;
} else {
new_matrix[ i ] = AST__BAD;
}
i++;
}
} else {
for( col = 0; col < ncol_this; col++ ){
new_matrix[ i++ ] = AST__BAD;
}
}
}
/* If there are insufficient rows in "this", append some zero-filled rows. */
if( minrow < nrow_a ){
for( i = minrow*ncol_this; i < nrow_a*ncol_this; i++ ){
new_matrix[ i ] = 0.0;
}
}
/* Now deal with cases where the "a" MatrixMap represents a full, non-diagonal
matrix. */
} else {
/* Initialise a pointer to the next element in the product matrix. */
new_val = new_matrix;
/* Get a pointer to the start of each row of the "a" matrix. */
for( row = 0; row < nrow_a; row++ ){
a_row = a_matrix + ncol_a*row;
/* Get a pointer to the start of each column of the "this" matrix. */
for( col = 0; col < ncol_this; col++ ){
this_col = this_matrix + col;
/* Form the dot product of the current row from "a", and the current
column from "this", checking for bad values. */
sum = 0.0;
for( i = 0; i < ncol_a; i++ ){
a_val = a_row[ i ];
this_val = this_col[ i*ncol_this ];
if( a_val != AST__BAD && this_val != AST__BAD ){
sum += a_val*this_val;
} else {
sum = AST__BAD;
break;
}
}
/* Store the output matrix element value. */
*(new_val++) = sum;
}
}
}
/* Create the new MatrixMap. */
new = astInitMatrixMap( NULL, sizeof( AstMatrixMap ), !class_init,
&class_vtab, "MatrixMap", ncol_this, nrow_a,
FULL, new_matrix );
/* If possible, compress the new MatrixMap by removing off-diagonal zero
elements. */
CompressMatrix( new, status );
/* Re-compress the original "this" MatrixMap. */
CompressMatrix( this, status );
}
/* Free the memory used to hold the product matrix in full form. */
new_matrix = (double *) astFree( (void *) new_matrix );
return new;
}
static AstMatrixMap *MtrRot( AstMatrixMap *this, double theta,
const double axis[], int *status ){
/*
*+
* Name:
* astMtrRot
* Purpose:
* Multiply a MatrixMap by a rotation matrix.
* Type:
* Protected virtual function.
* Synopsis:
* #include "matrixmap.h"
* AstMatrixMap *astMtrRot( astMatrixMap *this, double theta,
* const double axis[] )
* Class Membership:
* MatrixMap method.
* Description:
* This function creates a new MatrixMap which is a copy of "this",
* rotated by a specified angle. It can only be used on MatrixMaps which
* have either 2 or 3 output coordinates. In the 3-D case, the rotation
* is about an arbitrary axis passing through the origin.
* Parameters:
* this
* Pointer to the MatrixMap.
* theta
* The angle by which to rotate the matrix, in radians. If the matrix
* is applied to a 2-D vector position, the resulting vector is
* rotated clockwise about the origin (i.e. from the positive direction
* of the second axis to the positive direction of the first axis). If
* the vector positions are three dimensional, the rotation is clockwise
* when looking along the vector given by "axis". Note, "theta" measures
f when looking along the vector given by AXIS. Note, THETA measures
* the movemement of the vectors relative to a fixed reference frame.
* Alternatively, the reference frame can be thought of as rotating by
* (-theta) relative to the fixed vectors.
* axis
* A 3-D vector specifying the axis of rotation. This parameter is
* ignored if the output from MatrixMap is 2-dimensional.
* Returned Value:
* A pointer to the rotated MatrixMap.
* Notes:
* - A null Object pointer will also be returned if this function
* is invoked with the AST error status set, or if it should fail
* for any reason.
*-
*/
/* Local variables. */
AstMatrixMap *new; /* New MatrixMap holding the rotated matrix */
double as,a,b,c,d,e,f,g; /* Intermediate quantities */
double axlen; /* Length of axis vector */
double axlen2; /* Squared length of axis vector */
double costh; /* Cos(rotation angle) */
double sinth; /* Sin(rotation angle) */
double rotmat[9]; /* Rotation matrix */
double work[3]; /* Work space for matrix multiplication */
int ncol; /* No. of columns in the MatrixMap */
int nrow; /* No. of rows in the MatrixMap */
/* Return a NULL pointer if an error has already occurred. */
if ( !astOK ) return NULL;
/* Initialise the returned MarixMap to be a copy of the supplied MatrixMap. */
new = astCopy( this );
/* Save the cos and sin of the rotation angle for future use. */
costh = cos( theta );
sinth = sin( theta );
/* Return without changing the MatrixMap if the rotation angle is a
multiple of 360 degrees. */
if ( costh == 1.0 ) return new;
/* Get the dimensions of the MatrixMap. */
nrow = astGetNout( new );
ncol = astGetNin( new );
/* First do rotation of a plane about the origin. */
if( nrow == 2 ){
/* Ensure that the MatrixMap is stored in full form rather than
compressed form. */
ExpandMatrix( new, status );
/* Form the 2x2 forward rotation matrix. Theta is the clockwise angle
of rotation. */
rotmat[0] = costh;
rotmat[1] = sinth;
rotmat[2] = -sinth;
rotmat[3] = costh;
/* Post-multiply the current forward matrix (depending on whether or not
the MatrixMap has been inverted) by the forward rotation matrix. */
if( !astGetInvert( new ) ){
SMtrMult( 1, 2, ncol, rotmat, new->f_matrix, work, status );
} else {
SMtrMult( 1, 2, ncol, rotmat, new->i_matrix, work, status );
}
/* Now form the 2x2 inverse rotation matrix (the diagonal elements
don't change). */
rotmat[1] = -sinth;
rotmat[2] = sinth;
/* Pre-multiply the current inverse matrix (depending on whether or
not the MatrixMap has been inverted) by the inverse rotation matrix. */
if( !astGetInvert( new ) ){
SMtrMult( 0, ncol, 2, rotmat, new->i_matrix, work, status );
} else {
SMtrMult( 0, ncol, 2, rotmat, new->f_matrix, work, status );
}
/* See if the matrix can be stored as a UNIT or DIAGONAL matrix. */
CompressMatrix( new, status );
/* Now do rotation of a volume about an axis passing through the origin. */
} else if( nrow == 3 ){
/* Find the length of the axis vector. Report an error if it has zero
length or has not been supplied. */
if( axis ) {
axlen2 = axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2];
} else {
axlen2 = 0.0;
}
if( axlen2 <= 0.0 ) {
astError( AST__MTRAX, "astMtrRot(%s): NULL or zero length "
"axis vector supplied.", status, astClass(new) );
}
axlen = sqrt( axlen2 );
/* Ensure that the MatrixMap is stored in full form rather than
compressed form. */
ExpandMatrix( new, status );
/* Form commonly used terms in the rotation matrix. */
as = sinth/axlen;
a = (1.0 - costh)/axlen2;
b = a*axis[0]*axis[1];
c = as*axis[2];
d = a*axis[0]*axis[2];
e = as*axis[1];
f = a*axis[1]*axis[2];
g = as*axis[0];
/* Form the 3x3 forward rotation matrix. Theta is the clockwise angle
of rotation looking in the direction of the axis vector. */
rotmat[0] = a*axis[0]*axis[0] + costh;
rotmat[1] = b - c;
rotmat[2] = d + e;
rotmat[3] = b + c;
rotmat[4] = a*axis[1]*axis[1] + costh;
rotmat[5] = f - g;
rotmat[6] = d - e;
rotmat[7] = f + g;
rotmat[8] = a*axis[2]*axis[2] + costh;
/* Post-multiply the current forward matrix (depending on whether or not
the MatrixMap has been inverted) by the forward rotation matrix. */
if( !astGetInvert( new ) ){
SMtrMult( 1, 3, ncol, rotmat, new->f_matrix, work, status );
} else {
SMtrMult( 1, 3, ncol, rotmat, new->i_matrix, work, status );
}
/* Now form the 3x3 inverse rotation matrix (the diagonal elements
don't change). */
rotmat[1] = b + c;
rotmat[2] = d - e;
rotmat[3] = b - c;
rotmat[5] = f + g;
rotmat[6] = d + e;
rotmat[7] = f - g;
/* Pre-multiply the current inverse matrix (depending on whether or
not the MatrixMap has been inverted) by the inverse rotation matrix. */
if( !astGetInvert( new ) ){
SMtrMult( 0, ncol, 3, rotmat, new->i_matrix, work, status );
} else {
SMtrMult( 0, ncol, 3, rotmat, new->f_matrix, work, status );
}
/* See if the matrix can be stored as a UNIT or DIAGONAL matrix. */
CompressMatrix( new, status );
/* Report an error if the matrix is not suitable for rotation. */
} else {
astError( AST__MTR23, "astMtrRot(%s): Cannot rotate a %dx%d"
" MatrixMap.", status, astClass(new), nrow, ncol );
}
/* Delete the new MatrixMap if an error has occurred. */
if( !astOK ) new = astDelete( new );
return new;
}
static void PermGet( AstPermMap *map, int **outperm, int **inperm,
double **consts, int *status ){
/*
* Name:
* PermGet
* Purpose:
* Get the axis permutation and constants array for a PermMap.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* void PermGet( AstPermMap *map, int **outperm, int **inperm,
* double **const, int *status )
* Class Membership:
* MatrixMap member function
* Description:
* This function returns axis permutation and constants arrays which can
* be used to create a PermMap which is equivalent to the supplied PermMap.
* Parameters:
* map
* The PermMap.
* outperm
* An address at which to return a popinter to an array of ints
* holding the output axis permutation array. The array should be
* released using astFree when no longer needed.
* inperm
* An address at which to return a popinter to an array of ints
* holding the input axis permutation array. The array should be
* released using astFree when no longer needed.
* consts
* An address at which to return a popinter to an array of doubles
* holding the constants array. The array should be released using
* astFree when no longer needed.
* status
* Pointer to the inherited status variable.
* Notes:
* - NULL pointers are returned if an error has already occurred, or if
* this function should fail for any reason.
*/
/* Local Variables: */
AstPointSet *pset1; /* PointSet holding input positions for PermMap */
AstPointSet *pset2; /* PointSet holding output positions for PermMap */
double **ptr1; /* Pointer to pset1 data */
double **ptr2; /* Pointer to pset2 data */
double *cnst; /* Pointer to constants array */
double cn; /* Potential new constant value */
double ip; /* Potential output axis index */
double op; /* Potential input axis index */
int *inprm; /* Pointer to input axis permutation array */
int *outprm; /* Pointer to output axis permutation array */
int i; /* Axis count */
int nc; /* Number of constants stored so far */
int nin; /* No. of input coordinates for the PermMap */
int nout; /* No. of output coordinates for the PermMap */
/* Initialise. */
if( outperm ) *outperm = NULL;
if( inperm ) *inperm = NULL;
if( consts ) *consts = NULL;
/* Check the global error status and the supplied pointers. */
if ( !astOK || !outperm || !inperm || !consts ) return;
/* Get the number of input and output axes for the supplied PermMap. */
nin = astGetNin( map );
nout = astGetNout( map );
/* Allocate the memory for the returned arrays. */
outprm = (int *) astMalloc( sizeof( int )* (size_t) nout );
inprm = (int *) astMalloc( sizeof( int )* (size_t) nin );
cnst = (double *) astMalloc( sizeof( double )* (size_t) ( nout + nin ) );
/* Returned the pointers to these arrays.*/
*outperm = outprm;
*inperm = inprm;
*consts = cnst;
/* Create two PointSets, each holding two points, which can be used for
input and output positions with the PermMap. */
pset1 = astPointSet( 2, nin, "", status );
pset2 = astPointSet( 2, nout, "", status );
/* Set up the two input positions to be [0,1,2...] and [-1,-1,-1,...]. The
first position is used to enumerate the axes, and the second is used to
check for constant axis values. */
ptr1 = astGetPoints( pset1 );
if( astOK ){
for( i = 0; i < nin; i++ ){
ptr1[ i ][ 0 ] = ( double ) i;
ptr1[ i ][ 1 ] = -1.0;
}
}
/* Use the PermMap to transform these positions in the forward direction. */
(void) astTransform( map, pset1, 1, pset2 );
/* No constant axis valeus found yet. */
nc = 0;
/* Look at the mapped positions to determine the output axis permutation
array. */
ptr2 = astGetPoints( pset2 );
if( astOK ){
/* Do each output axis. */
for( i = 0; i < nout; i++ ){
/* If the output axis value is copied from an input axis value, the index
of the appropriate input axis will be in the mapped first position. */
op = ptr2[ i ][ 0 ];
/* If the output axis value is assigned a constant value, the result of
mapping the two different input axis values will be the same. */
cn = ptr2[ i ][ 1 ];
if( op == cn ) {
/* We have found another constant. Store it in the constants array, and
store the index of the constant in the output axis permutation array. */
cnst[ nc ] = cn;
outprm[ i ] = -( nc + 1 );
nc++;
/* If the output axis values are different, then the output axis value
must be copied from the input axis value. */
} else {
outprm[ i ] = (int) ( op + 0.5 );
}
}
}
/* Now do the same thing to determine the input permutation array. */
if( astOK ){
for( i = 0; i < nout; i++ ){
ptr2[ i ][ 0 ] = ( double ) i;
ptr2[ i ][ 1 ] = -1.0;
}
}
(void) astTransform( map, pset2, 0, pset1 );
if( astOK ){
for( i = 0; i < nin; i++ ){
ip = ptr1[ i ][ 0 ];
cn = ptr1[ i ][ 1 ];
if( ip == cn ) {
cnst[ nc ] = cn;
inprm[ i ] = -( nc + 1 );
nc++;
} else {
inprm[ i ] = (int) ( ip + 0.5 );
}
}
}
/* Annul the PointSets. */
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
/* If an error has occurred, attempt to free the returned arrays. */
if( !astOK ) {
*outperm = (int *) astFree( (void *) *outperm );
*inperm = (int *) astFree( (void *) *inperm );
*consts = (double *) astFree( (void *) *consts );
}
/* Return. */
return;
}
static int PermOK( AstMapping *pm, int *status ){
/*
* Name:
* PermOK
* Purpose:
* Determine if a PermMap can be merged with a MatrixMap.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* int PermOK( AstMapping *pm, int *status )
* Class Membership:
* PermMap member function
* Description:
* This function returns a flag indicating if the supplied PermMap
* could be merged with a MatrixMap. For thios to be possible, the
* PermMap must have the same number of input and output axes, and the
* inverse and forward mappings must be consistent.
* Parameters:
* pm
* The PermMap.
* status
* Pointer to the inherited status variable.
* Returned Value:
* 1 if the PermMap can be merged, 0 otherwise.
* Notes:
* - A value of 0 is returned if an error has already occurred, or if
* this function should fail for any reason.
*/
/* Local Variables: */
AstPointSet *pset1; /* PointSet holding input positions for PermMap */
AstPointSet *pset2; /* PointSet holding output positions for PermMap */
double **ptr1; /* Pointer to pset1 data */
int i; /* Loop count */
int nin; /* No. of input coordinates for the PermMap */
int nout; /* No. of output coordinates for the PermMap */
int ret; /* Returned flag */
/* Check the global error status. */
if ( !astOK ) return 0;
/* Initialise */
ret = 0;
/* The PermMap must have the same number of input and output coordinates. */
nin = astGetNin( pm );
nout = astGetNout( pm );
if( nin == nout ){
/* Create two PointSets, each holding two points, which can be used for
the input and output positions with the PermMap. */
pset1 = astPointSet( 2, nin, "", status );
pset2 = astPointSet( 2, nout, "", status );
/* Set up the two input positions to be [1,2,3...] and [0,-1,-2,...] */
ptr1 = astGetPoints( pset1 );
if( astOK ){
for( i = 0; i < nin; i++ ){
ptr1[ i ][ 0 ] = ( double )( i + 1 );
ptr1[ i ][ 1 ] = ( double )( -i );
}
}
/* Use the PermMap to transform these positions in the forward direction. */
(void) astTransform( pm, pset1, 1, pset2 );
/* Now transform the results back again using the inverse PermMap. */
(void) astTransform( pm, pset2, 0, pset1 );
/* See if the input positions have changed. If they have, then the PermMap
does not have a consistent pair of transformations. If they have not,
then the transformations must be consistent because we used two
different input positions and only one could come out unchanged by
chance. */
if( astOK ){
ret = 1;
for( i = 0; i < nin; i++ ){
if( ptr1[ i ][ 0 ] != ( double )( i + 1 ) ||
ptr1[ i ][ 1 ] != ( double )( -i ) ){
ret = 0;
break;
}
}
}
/* Annul the PointSets. */
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
}
/* Return the answer. */
return astOK ? ret : 0;
}
static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){
/*
* Name:
* Rate
* Purpose:
* Calculate the rate of change of a Mapping output.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status )
* Class Membership:
* MatrixMap member function (overrides the astRate method inherited
* from the Mapping class ).
* Description:
* This function returns the rate of change of a specified output of
* the supplied Mapping with respect to a specified input, at a
* specified input position.
* Parameters:
* this
* Pointer to the Mapping to be applied.
* at
* The address of an array holding the axis values at the position
* at which the rate of change is to be evaluated. The number of
* elements in this array should equal the number of inputs to the
* Mapping.
* ax1
* The index of the Mapping output for which the rate of change is to
* be found (output numbering starts at 0 for the first output).
* ax2
* The index of the Mapping input which is to be varied in order to
* find the rate of change (input numbering starts at 0 for the first
* input).
* status
* Pointer to the inherited status variable.
* Returned Value:
* The rate of change of Mapping output "ax1" with respect to input
* "ax2", evaluated at "at", or AST__BAD if the value cannot be
* calculated.
*/
/* Local Variables: */
AstMatrixMap *map;
double *matrix;
double result;
/* Check inherited status */
if( !astOK ) return AST__BAD;
/* Get a pointer to the MatrixMap structure. */
map = (AstMatrixMap *) this;
/* Get a pointer to the array holding the required matrix elements, according
to whether the MatrixMap has been inverted. */
if( !astGetInvert( this ) ) {
matrix = map->f_matrix;
} else {
matrix = map->i_matrix;
}
/* First deal with full MatrixMaps in which all matrix elements are stored. */
if( map->form == FULL ){
result = matrix[ ax1*astGetNin( this ) + ax2 ];
/* For unit matrices, the rate is unity if the input and output axes are
equal, and zero otherwise. */
} else if( map->form == UNIT ){
result = (ax1 == ax2 ) ? 1.0 : 0.0;
/* For diagonal matrices, the rate is zero for off diagonal elements and
the matrix array stored the on-diagonal rates. */
} else if( ax1 == ax2 ) {
result = matrix[ ax1 ];
} else {
result = 0.0;
}
/* Return the result. */
return result;
}
static void SMtrMult( int post, int m, int n, const double *mat1,
double *mat2, double *work, int *status ){
/*
* Name:
* SMtrMult
* Purpose:
* Multiply a square matrix and a non-square matrix.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* void SMtrMult( int post, int m, int n, const double *mat1,
* double *mat2, double *work, int *status )
* Class Membership:
* MatrixMap member function.
* Description:
* The matrix pointed to by "mat2" is modified by multiplying it by
* the square matrix pointed to by "mat1". If "post" is 1, then:
*
* mat2 -> mat1*mat2 (mat1 is mxm and mat2 is mxn)
*
* If "post" is zero, then:
*
* mat2 -> mat2*mat1 (mat1 is nxn and mat2 is mxn)
*
* The restriction that "mat1" must be square is imposed so that the
* returned matrix will have the same shape as the supplied matrix (mat1).
* Parameters:
* post
* Specifies whether to post- or pre- multiply mat2 by mat1.
* m
* The number of rows in mat2.
* n
* The number of columns in mat2.
* mat1
* The multiplier matrix. It must be square of size m or n, depending
* on "post".
* mat2
* The multiplicand matrix.
* work
* Pointer to work space containing room for m doubles (if "post"
* is 1), or n doubles (if "post" is 0).
* status
* Pointer to the inherited status variable.
* Notes:
* - No error is reported if "mat2" is supplied NULL. In this case
* it will also be returned NULL.
*/
/* Local Variables */
double dot; /* Output matrix element value */
const double *mat1_col; /* Pointer to start of current column of mat1 */
const double *mat1_row; /* Pointer to start of current row of mat1 */
double *mat2_col; /* Pointer to start of current column of mat2 */
double *mat2_row; /* Pointer to start of current row of mat2 */
double cel; /* Column element value */
double rel; /* Row element value */
int i; /* Index of current output row */
int j; /* Index of current output column */
int k; /* Dot product index */
/* Do nothing if mat2 is NULL */
if ( mat2 ){
/* First deal with cases where the supplied matrix is post-multiplied
(i.e. the returned matrix is mat1*mat2). */
if( post ){
/* Loop round each column of the output matrix, storing a pointer to
the start of the corresponding column of mat2. */
for( j=0; jform == UNIT ) {
result = 1;
/* Otherwise, check that the appropriate array is defined in the
MatrixMap. */
} else {
/* Determine if the Mapping has been inverted. */
invert = astGetInvert( this );
/* If OK, obtain the result. */
if ( astOK ) {
if( invert ){
result = ( map->i_matrix != NULL );
} else {
result = ( map->f_matrix != NULL );
}
}
}
/* Return the result. */
return result;
}
static int GetTranInverse( AstMapping *this, int *status ) {
/*
*
* Name:
* GetTranInverse
* Purpose:
* Determine if a MatrixMap defines an inverse coordinate transformation.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* int GetTranInverse( AstMapping *this, int *status )
* Class Membership:
* MatrixMap member function (over-rides the astGetTranInverse method
* inherited from the Mapping class).
* Description:
* This function returns a value indicating if the MatrixMap is able
* to perform an inverse coordinate transformation.
* Parameters:
* this
* Pointer to the MatrixMap.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Zero if the inverse coordinate transformation is not defined, or 1 if it
* is.
* 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: */
AstMatrixMap *map; /* Pointer to MatrixMap to be queried */
int invert; /* Has the mapping been inverted? */
int result; /* The returned value */
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain a pointer to the MatrixMap. */
map = (AstMatrixMap *) this;
/* All unit MatrixMaps are defined in both directions. */
if( map->form == UNIT ) {
result = 1;
/* Otherwise, check that the appropriate array is defined in the
MatrixMap. */
} else {
/* Determine if the Mapping has been inverted. */
invert = astGetInvert( this );
/* If OK, obtain the result. */
if ( astOK ) {
if( invert ){
result = ( map->f_matrix != NULL );
} else {
result = ( map->i_matrix != NULL );
}
}
}
/* Return the result. */
return result;
}
static AstPointSet *Transform( AstMapping *this, AstPointSet *in,
int forward, AstPointSet *out, int *status ) {
/*
* Name:
* Transform
* Purpose:
* Apply a MatrixMap to transform a set of points.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* AstPointSet *Transform( AstMapping *this, AstPointSet *in,
* int forward, AstPointSet *out, int *status )
* Class Membership:
* MatrixMap member function (over-rides the astTransform protected
* method inherited from the Mapping class).
* Description:
* This function takes a MatrixMap and a set of points encapsulated in a
* PointSet and transforms the points by multiplying them by the matrix.
* Parameters:
* this
* Pointer to the MatrixMap.
* in
* Pointer to the PointSet holding the input coordinate data.
* forward
* A non-zero value indicates that the forward coordinate transformation
* should be applied, while a zero value requests the inverse
* transformation.
* out
* Pointer to a PointSet which will hold the transformed (output)
* coordinate values. A NULL value may also be given, in which case a
* new PointSet will be created by this function.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the output (possibly new) PointSet.
* 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.
* - The number of coordinate values per point in the input PointSet must
* match the number of columns in the MatrixMap being applied.
* - The number of coordinate values per point in the output PointSet will
* equal the number of rows in the MatrixMap being applied.
* - If an output PointSet is supplied, it must have space for sufficient
* number of points and coordinate values per point to accommodate the
* result. Any excess space will be ignored.
*/
/* Local Variables: */
AstPointSet *result; /* Pointer to output PointSet */
AstMatrixMap *map; /* Pointer to MatrixMap to be applied */
double diag_term; /* Current diagonal element value */
double *indata; /* Pointer to next input data value */
double *matrix; /* Pointer to start of matrix element array */
double *matrix_element; /* Pointer to current matrix element value */
double *outdata; /* Pointer to next output data value */
double **ptr_in; /* Pointer to input coordinate data */
double **ptr_out; /* Pointer to output coordinate data */
double sum; /* Partial output value */
double val; /* Data value */
int in_coord; /* Index of output coordinate */
int nax; /* Output axes for which input axes exist */
int ncoord_in; /* Number of coordinates per input point */
int ncoord_out; /* Number of coordinates per output point */
int npoint; /* Number of points */
int out_coord; /* Index of output coordinate */
int point; /* Loop counter for points */
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Obtain a pointer to the MatrixMap. */
map = (AstMatrixMap *) this;
/* Apply the parent mapping using the stored pointer to the Transform member
function inherited from the parent Mapping class. This function validates
all arguments and generates an output PointSet if necessary, but does not
actually transform any coordinate values. */
result = (*parent_transform)( this, in, forward, out, status );
/* We will now extend the parent astTransform method by performing the
calculations needed to generate the output coordinate values. */
/* Determine the numbers of points and coordinates per point from the input
and output PointSets and obtain pointers for accessing the input and
output coordinate values. */
ncoord_in = astGetNcoord( in );
ncoord_out = astGetNcoord( result );
npoint = astGetNpoint( in );
ptr_in = astGetPoints( in );
ptr_out = astGetPoints( result );
/* Determine whether to apply the forward or inverse mapping, according to the
direction specified and whether the mapping has been inverted. */
if ( astGetInvert( map ) ) forward = !forward;
/* Get a pointer to the array holding the required matrix elements, according
to the direction of mapping required. */
if ( forward ) {
matrix = map->f_matrix;
} else {
matrix = map->i_matrix;
}
/* Perform coordinate arithmetic. */
/* ------------------------------ */
if ( astOK ) {
/* First deal with full MatrixMaps in which all matrix elements are stored. */
if( map->form == FULL ){
/* Loop to apply the matrix to each point in turn, checking for
(and propagating) bad values in the process. The matrix elements are
accessed sequentially in row order. The next matrix element to be
used is identified by a pointer which is initialised to point to the
first element of the matrix prior to processing each point. */
for ( point = 0; point < npoint; point++ ) {
matrix_element = matrix;
/* Each output co-ordinate value is created by summing the product of the
corresponding input co-ordinates and the elements of one row of the
matrix. */
for ( out_coord = 0; out_coord < ncoord_out; out_coord++ ) {
sum = 0.0;
for ( in_coord = 0; in_coord < ncoord_in; in_coord++ ) {
/* Check the current input coordinate value and the current matrix element.
If the coordinate value is bad, then the output value will also be
bad unless the matrix element is zero. That is, a zero matrix element
results in the input coordinate value being ignored, even if it is bad.
This prevents bad input values being propagated to output axes which
are independant of the bad input axis. A bad matrix element always results
in the output value being bad. In either of these cases, break out of the
loop, remembering to advance the pointer to the next matrix element so
that it points to the start of the next row ready for doing the next
output coordinate. */
if ( ( ptr_in[ in_coord ][ point ] == AST__BAD &&
(*matrix_element) != 0.0 ) ||
(*matrix_element) == AST__BAD ) {
sum = AST__BAD;
matrix_element += ncoord_in - in_coord;
break;
/* If the input coordinate and the current matrix element are both
valid, increment the sum by their product, and step to the next matrix
element pointer If we arrive here with a bad input value, then the
matrix element must be zero, in which case the running sum is left
unchanged. */
} else {
if ( ptr_in[ in_coord ][ point ] != AST__BAD ) {
sum += ptr_in[ in_coord ][ point ] * (*matrix_element);
}
matrix_element++;
}
}
/* Store the output coordinate value. */
ptr_out[ out_coord ][ point ] = sum;
}
}
/* Now deal with unit and diagonal MatrixMaps. */
} else {
/* Find the number of output axes for which input data is available. */
if( ncoord_in < ncoord_out ){
nax = ncoord_in;
} else {
nax = ncoord_out;
}
/* For unit matrices, copy the input axes to the corresponding output axes. */
if( map->form == UNIT ){
for( out_coord = 0; out_coord < nax; out_coord++ ) {
(void) memcpy( ptr_out[ out_coord ],
(const void *) ptr_in[ out_coord ],
sizeof( double )*(size_t)npoint );
}
/* For diagonal matrices, scale each input axis using the appropriate
diagonal element from the matrix, and store in the output. */
} else {
for( out_coord = 0; out_coord < nax; out_coord++ ){
diag_term = matrix[ out_coord ];
outdata = ptr_out[ out_coord ];
indata = ptr_in[ out_coord ];
if( diag_term != AST__BAD ){
for( point = 0; point < npoint; point++ ){
val = *(indata++);
if( val != AST__BAD ){
*(outdata++) = diag_term*val;
} else {
*(outdata++) = AST__BAD;
}
}
} else {
for( point = 0; point < npoint; point++ ){
*(outdata++) = AST__BAD;
}
}
}
}
/* If there are any remaining output axes, fill the first one with zeros. */
if( nax < ncoord_out ){
outdata = ptr_out[ nax ];
for( point = 0; point < npoint; point++ ) *(outdata++) = 0.0;
/* Copy this axis to any remaining output axes. */
outdata = ptr_out[ nax ];
for( out_coord = nax + 1; out_coord < ncoord_out; out_coord++ ) {
(void) memcpy( ptr_out[ out_coord ], (const void *) outdata,
sizeof( double )*(size_t)npoint );
}
}
}
}
/* Return a pointer to the output PointSet. */
return result;
}
static int ScalingRowCol( AstMatrixMap *map, int axis, int *status ){
/*
* Name:
* ScalingRowCol
* Purpose:
* Determine if a given row and column of a MatrixMap are zeros
* with a non-zero diagonal term.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* int ScalingRowCol( AstMatrixMap *map, int axis, int *status )
* Class Membership:
* MatrixMap member function
* Description:
* This function returns a flag indicating if a MatrixMap presents a
* simple scaling for a given axis in both directions. The MatrixMap
* must be square. A value of one is returned if every element of the
* row and column corresponding to the given axis is zero, except for
* the diagonal term which must be non-zero.
* Parameters:
* map
* The MatrixMap.
* axis
* The zero-based index of the axis to check.
* status
* Pointer to the inherited status variable.
* Returned Value:
* 1 if the row/column produces a simple scaling, 0 otherwise.
*/
/* Local Variables: */
double *el; /* Pointer to matrix element */
int i; /* Element count */
int ncol; /* No. of input coordinates */
int ret; /* Returned flag */
/* Initialise */
ret = 0;
/* Check the global error status. */
if ( !astOK ) return ret;
/* If a unit or diagonal MatrixMap has been supplied, return 1. */
if( map->form != FULL ){
ret = 1;
/* If a full matrix has been supplied... */
} else {
/* Assume the row/column gives a unit mapping. */
ret = 1;
/* Get the number of input axes for the MatrixMap. */
ncol = astGetNin( map );
/* Check that all elements of the "axis"th row are effectively zero, except
for the "axis"th element which must be non-zero. */
el = map->f_matrix + axis*ncol;
for( i = 0; i < ncol; i++ ) {
if( i == axis ) {
if( fabs( *el ) <= DBL_EPSILON ) {
ret = 0;
break;
}
} else if( fabs( *el ) > DBL_EPSILON ) {
ret = 0;
break;
}
el++;
}
/* Check that all elements of the "axis"th column are effectively zero, except
for the "axis"th element which must be non-zero. */
if( ret ) {
el = map->f_matrix + axis;
for( i = 0; i < ncol; i++ ) {
if( i == axis ) {
if( fabs( *el ) <= DBL_EPSILON ) {
ret = 0;
break;
}
} else if( fabs( *el ) > DBL_EPSILON ) {
ret = 0;
break;
}
el += ncol;
}
}
}
/* Return the answer. */
return astOK ? ret : 0;
}
/* 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). */
/* Copy constructor. */
/* ----------------- */
static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
/*
* Name:
* Copy
* Purpose:
* Copy constructor for MatrixMap objects.
* Type:
* Private function.
* Synopsis:
* void Copy( const AstObject *objin, AstObject *objout, int *status )
* Description:
* This function implements the copy constructor for MatrixMap objects.
* Parameters:
* objin
* Pointer to the object to be copied.
* objout
* Pointer to the object being constructed.
* status
* Pointer to the inherited status variable.
* Returned Value:
* void
* Notes:
* - This constructor makes a deep copy, including a copy of the matrix
* element values associated with the input MatrixMap.
*/
/* Local Variables: */
AstMatrixMap *in; /* Pointer to input MatrixMap */
AstMatrixMap *out; /* Pointer to output MatrixMap */
int nel; /* No. of elements in the matrix */
int nin; /* No. of input coordinates */
int nout; /* No. of output coordinates */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain pointers to the input and output MatrixMaps. */
in = (AstMatrixMap *) objin;
out = (AstMatrixMap *) objout;
/* Nullify the pointers stored in the output object since these will
currently be pointing at the input data (since the output is a simple
byte-for-byte copy of the input). Otherwise, the input data could be
freed by accidient if the output object is deleted due to an error
occuring in this function. */
out->f_matrix = NULL;
out->i_matrix = NULL;
/* If the input MatrixMap is a unit mapping, then no matrix elements are
stored with it, so do nothing in this case. */
if( out->form != UNIT ){
/* Obtain the number of stored values in the MatrixMap. This is independant of
whether the Mapping has been inverted or not. If the MatrixMap is diagonal,
only the diagonal terms are stored. */
nin = astGetNin( in );
nout = astGetNout( in );
if( out->form == DIAGONAL ){
if( nin < nout ){
nel = nin;
} else {
nel = nout;
}
} else {
nel = nin*nout;
}
/* Store the forward matrix elements in the output MatrixMap. */
out->f_matrix = (double *) astStore( NULL, (void *) in->f_matrix,
sizeof( double )*(size_t) nel );
/* Store the inverse matrix elements (if defined) in the output
MatrixMap. */
if( in->i_matrix ){
out->i_matrix = (double *) astStore( NULL, (void *) in->i_matrix,
sizeof( double )*(size_t) nel );
}
/* If an error has occurred, free the output MatrixMap arrays. */
if( !astOK ) {
out->f_matrix = (double *) astFree( (void *) out->f_matrix );
out->i_matrix = (double *) astFree( (void *) out->i_matrix );
}
}
return;
}
/* Destructor. */
/* ----------- */
static void Delete( AstObject *obj, int *status ) {
/*
* Name:
* Delete
* Purpose:
* Destructor for MatrixMap objects.
* Type:
* Private function.
* Synopsis:
* void Delete( AstObject *obj, int *status )
* Description:
* This function implements the destructor for MatrixMap objects.
* Parameters:
* obj
* Pointer to the object to be deleted.
* status
* Pointer to the inherited status variable.
* Returned Value:
* void
* Notes:
* This function attempts to execute even if the global error status is
* set.
*/
/* Local Variables: */
AstMatrixMap *this; /* Pointer to MatrixMap */
/* Obtain a pointer to the MatrixMap structure. */
this = (AstMatrixMap *) obj;
/* Free the arrays used to store element values for forward and inverse
matrices. */
this->f_matrix = (double *) astFree( (void *) this->f_matrix );
this->i_matrix = (double *) astFree( (void *) this->i_matrix );
}
/* Dump function. */
/* -------------- */
static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
/*
* Name:
* Dump
* Purpose:
* Dump function for MatrixMap 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 MatrixMap class to an output Channel.
* Parameters:
* this
* Pointer to the MatrixMap whose data are being written.
* channel
* Pointer to the Channel to which the data are being written.
* status
* Pointer to the inherited status variable.
*/
#define KEY_LEN 50 /* Maximum length of a keyword */
/* Local Variables: */
AstMatrixMap *this; /* Pointer to the MatrixMap structure */
char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */
int el; /* Element index */
int nel; /* No. of elements in the matrix */
int nin; /* No. of input coords */
int nout; /* No. of output coords */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the MatrixMap structure. */
this = (AstMatrixMap *) this_object;
/* Find the number of elements stored for each matrix. */
nin = astGetNin( this );
nout = astGetNout( this );
if( this->form == FULL ){
nel = nin*nout;
} else if( this->form == DIAGONAL ){
nel = MIN( nin, nout );
} else {
nel = 0;
}
/* Write out values representing the instance variables for the
MatrixMap class. */
/* The forward matrix. The inverse matrix not written out since it can be
re-calculated when the MatrixMap is read back in. Note BAD values are
not written out as the AST__BAD value may differ on different machines.
If a matrix element is not found when reading the matrix back in
again (in astLoadMatrixMap), then it is assigned a default value of
AST__BAD. */
if( this->f_matrix ){
for( el = 0; el < nel; el++ ){
if( (this->f_matrix)[ el ] != AST__BAD ) {
(void) sprintf( buff, "M%d", el );
astWriteDouble( channel, buff, 1, 1, (this->f_matrix)[ el ],
"Forward matrix value" );
}
}
}
/* The matrix storage form. */
astWriteString( channel, "Form", 1, 1, Form[ this->form ],
"Matrix storage form" );
/* Undefine macros local to this function. */
#undef KEY_LEN
}
/* Standard class functions. */
/* ========================= */
/* Implement the astIsAMatrixMap and astCheckMatrixMap functions using the macros
defined for this purpose in the "object.h" header file. */
astMAKE_ISA(MatrixMap,Mapping)
astMAKE_CHECK(MatrixMap)
AstMatrixMap *astMatrixMap_( int nin, int nout, int form,
const double matrix[], const char *options, int *status, ...){
/*
*++
* Name:
c astMatrixMap
f AST_MATRIXMAP
* Purpose:
* Create a MatrixMap.
* Type:
* Public function.
* Synopsis:
c #include "matrixmap.h"
c AstMatrixMap *astMatrixMap( int nin, int nout, int form,
c const double matrix[],
c const char *options, ... )
f RESULT = AST_MATRIXMAP( NIN, NOUT, FORM, MATRIX, OPTIONS, STATUS )
* Class Membership:
* MatrixMap constructor.
* Description:
* This function creates a new MatrixMap and optionally initialises
* its attributes.
*
* A MatrixMap is a form of Mapping which performs a general linear
* transformation. Each set of input coordinates, regarded as a
* column-vector, are pre-multiplied by a matrix (whose elements
* are specified when the MatrixMap is created) to give a new
* column-vector containing the output coordinates. If appropriate,
* the inverse transformation may also be performed.
* Parameters:
c nin
f NIN = INTEGER (Given)
* The number of input coordinates, which determines the number
* of columns in the matrix.
c nout
f NOUT = INTEGER (Given)
* The number of output coordinates, which determines the number
* of rows in the matrix.
c form
f FORM = INTEGER (Given)
* An integer which indicates the form in which the matrix
* elements will be supplied.
*
c A value of zero indicates that a full "nout" x "nin" matrix
f A value of zero indicates that a full NOUT x NIN matrix
c of values will be supplied via the "matrix" parameter
f of values will be supplied via the MATRIX argument
* (below). In this case, the elements should be given in row
* order (the elements of the first row, followed by the
* elements of the second row, etc.).
*
* A value of 1 indicates that only the diagonal elements of the
* matrix will be supplied, and that all others should be
c zero. In this case, the elements of "matrix" should contain
f zero. In this case, the elements of MATRIX should contain
* only the diagonal elements, stored consecutively.
*
* A value of 2 indicates that a "unit" matrix is required,
* whose diagonal elements are set to unity (with all other
c elements zero). In this case, the "matrix" parameter is
c ignored and a NULL pointer may be supplied.
f elements zero). In this case, the MATRIX argument is not used.
c matrix
f MATRIX( * ) = DOUBLE PRECISION (Given)
* The array of matrix elements to be used, stored according to
c the value of "form".
f the value of FORM.
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 MatrixMap. 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 MatrixMap. 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 astMatrixMap()
f AST_MATRIXMAP = INTEGER
* A pointer to the new MatrixMap.
* Notes:
* - In general, a MatrixMap's forward transformation will always
* be available (as indicated by its TranForward attribute), but
* its inverse transformation (TranInverse attribute) will only be
* available if the associated matrix is square and non-singular.
* - As an exception to this, the inverse transformation is always
* available if a unit or diagonal matrix is specified. In this
* case, if the matrix is not square, one or more of the input
* coordinate values may not be recoverable from a set of output
* coordinates. Any coordinates affected in this way will simply be
* set to the value zero.
* - A null Object pointer (AST__NULL) will be returned if this
c function is invoked with the AST error status set, or if it
f function is invoked with STATUS set to an error value, or if it
* should fail for any reason.
* Status Handling:
* The protected interface to this function includes an extra
* parameter at the end of the parameter list descirbed above. This
* parameter is a pointer to the integer inherited status
* variable: "int *status".
*--
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstMatrixMap *new; /* Pointer to new MatrixMap */
va_list args; /* Variable argument list */
/* Check the global status. */
if ( !astOK ) return NULL;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(NULL);
/* Initialise the MatrixMap, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitMatrixMap( NULL, sizeof( AstMatrixMap ), !class_init,
&class_vtab, "MatrixMap", nin, nout, form, matrix);
/* 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 MatrixMap'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 MatrixMap. */
return new;
}
AstMatrixMap *astMatrixMapId_( int nin, int nout, int form, const double matrix[],
const char *options, ... ) {
/*
* Name:
* astMatrixMapId_
* Purpose:
* Create a MatrixMap.
* Type:
* Private function.
* Synopsis:
* #include "matrixmap.h"
* AstMatrixMap *astMatrixMapId_( int nin, int nout, int form,
* const double matrix[], const char *options,
* ... )
* Class Membership:
* MatrixMap constructor.
* Description:
* This function implements the external (public) interface to the
* astMatrixMap constructor function. It returns an ID value (instead
* of a true C pointer) to external users, and must be provided
* because astMatrixMap_ 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 astMatrixMap_ 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 astMatrixMap_.
* Returned Value:
* The ID value associated with the new MatrixMap.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstMatrixMap *new; /* Pointer to new MatrixMap */
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 MatrixMap, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitMatrixMap( NULL, sizeof( AstMatrixMap ), !class_init, &class_vtab,
"MatrixMap", nin, nout, form, matrix );
/* 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 MatrixMap'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 MatrixMap. */
return astMakeId( new );
}
AstMatrixMap *astInitMatrixMap_( void *mem, size_t size, int init,
AstMatrixMapVtab *vtab, const char *name,
int nin, int nout, int form,
const double *matrix, int *status ) {
/*
*+
* Name:
* astInitMatrixMap
* Purpose:
* Initialise a MatrixMap.
* Type:
* Protected function.
* Synopsis:
* #include "matrixmap.h"
* AstMatrixMap *astInitMatrixMap( void *mem, size_t size, int init,
* AstMatrixMapVtab *vtab, const char *name,
* int nin, int nout, int form,
* const double *matrix )
* Class Membership:
* MatrixMap initialiser.
* Description:
* This function is provided for use by class implementations to initialise
* a new MatrixMap object. It allocates memory (if necessary) to accommodate
* the MatrixMap plus any additional data associated with the derived class.
* It then initialises a MatrixMap structure at the start of this memory. If
* the "init" flag is set, it also initialises the contents of a virtual
* function table for a MatrixMap at the start of the memory passed via the
* "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory in which the MatrixMap is to be initialised.
* This must be of sufficient size to accommodate the MatrixMap data
* (sizeof(MatrixMap)) 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 MatrixMap (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 MatrixMap
* structure, so a valid value must be supplied even if not required for
* allocating memory.
* init
* A logical flag indicating if the MatrixMap'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 MatrixMap.
* 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).
* nin
* The number of input coordinate values per point. This is the
* same as the number of columns in the matrix.
* nout
* The number of output coordinate values per point. This is the
* same as the number of rows in the matrix.
* form
* If "form" is 2 or larger, then a unit MatrixMap is created. In this
* case "matrix" is ignored and can be supplied as NULL. If "form" is
* 1, then a diagonal MatrixMap is created. In this case, the number of
* values in "matrix" should be equal to the minimum of nin and nout,
* and "matrix" should contain the corresponding diagonal terms, in row
* order. If "form" is 0 or less, then a full MatrixMap is created, and
* "matrix" should contain all nin*nout element values.
* matrix
* A pointer to an array of matrix element values. The values should be
* supplied in row order. The content of this array is determined by
* "form". If a full MatrixMap is to be created then the array starts
* with (row 1, column 1), then comes (row 1, column 2), up to (row 1,
* column nin), then (row 2, column 1), (row 2, column 2), and so on,
* finishing with (row nout, column nin) ). An error is reported if a
* NULL value is supplied unless "form" is 2 or more.
* Returned Value:
* A pointer to the new MatrixMap.
* 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: */
AstMatrixMap *new; /* Pointer to new MatrixMap */
double *fmat; /* Pointer to the forward matrix */
double *imat; /* Pointer to the inverse matrix */
int i; /* Loop count */
int nel; /* No. of elements in matrix array */
int nuse; /* Number of usable matrix elements */
int used_form; /* Form limited to 0, 1 or 2 */
/* Check the global status. */
if ( !astOK ) return NULL;
/* If necessary, initialise the virtual function table. */
if ( init ) astInitMatrixMapVtab( vtab, name );
/* Initialise. */
new = NULL;
/* Report an error if a NULL matrix was supplied, unless a unit MatrixMap
has been requested. */
if( form < 2 && !matrix ){
astError( AST__MTRMT, "astInitMatrixMap(%s): NULL matrix supplied.", status,
name );
} else {
/* Initialise a Mapping structure (the parent class) as the first component
within the MatrixMap structure, allocating memory if necessary. Specify that
the Mapping should be defined in both the forward and inverse directions. */
new = (AstMatrixMap *) astInitMapping( mem, size, 0,
(AstMappingVtab *) vtab, name,
nin, nout, 1, 1 );
if ( astOK ) {
/* Initialise the MatrixMap data. */
/* ---------------------------- */
/* If a unit MatrixMap is being created, then no additional storage is
required. */
if( form > 1 ){
nel = 0;
used_form = UNIT;
/* If a diagonal MatrixMap is being created, then memory is needed to hold
the diagonal terms. */
} else if( form == 1 ){
if( nin < nout ){
nel = nin;
} else {
nel = nout;
}
used_form = DIAGONAL;
/* If a full MatrixMap is being created, then memory is needed to hold
all the terms. */
} else {
nel = nin*nout ;
used_form = FULL;
}
/* Allocate memory for the forward matrix, storing the supplied matrix
values in it. */
fmat = (double *) astStore( NULL, (void *) matrix,
sizeof(double)*(size_t)nel );
/* Replace any NaNs by AST__BAD and count the number of usable values. */
if( nel > 0 ) {
nuse = 0;
for( i = 0; i < nel; i++ ) {
if( !astISFINITE(fmat[ i ]) ) {
fmat[ i ] = AST__BAD;
} else if( fmat[ i ] != AST__BAD ) {
nuse++;
}
}
/* Report an error if there are no usable values. */
if( nuse == 0 && astOK ) {
astError( AST__MTRMT, "astInitMatrixMap(%s): Supplied matrix "
"contains only bad values.", status, name );
}
}
/* Create an inverse matrix if possible. */
imat = InvertMatrix( used_form, nout, nin, fmat, status );
/* Store the matrix arrays. */
new->form = used_form;
new->f_matrix = fmat;
new->i_matrix = imat;
/* Attempt to compress the MatrixMap into DIAGONAL or UNIT form. */
CompressMatrix( new, status );
/* If an error occurred, clean up by deleting the new MatrixMap. */
if ( !astOK ) new = astDelete( new );
}
}
/* Return a pointer to the new MatrixMap. */
return new;
}
AstMatrixMap *astLoadMatrixMap_( void *mem, size_t size,
AstMatrixMapVtab *vtab, const char *name,
AstChannel *channel, int *status ) {
/*
*+
* Name:
* astLoadMatrixMap
* Purpose:
* Load a MatrixMap.
* Type:
* Protected function.
* Synopsis:
* #include "matrixmap.h"
* AstMatrixMap *astLoadMatrixMap( void *mem, size_t size,
* AstMatrixMapVtab *vtab, const char *name,
* AstChannel *channel )
* Class Membership:
* MatrixMap loader.
* Description:
* This function is provided to load a new MatrixMap 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
* MatrixMap 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 a MatrixMap at the start of the memory
* passed via the "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory into which the MatrixMap is to be
* loaded. This must be of sufficient size to accommodate the
* MatrixMap data (sizeof(MatrixMap)) 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 MatrixMap (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 MatrixMap 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(AstMatrixMap) is used instead.
* vtab
* Pointer to the start of the virtual function table to be
* associated with the new MatrixMap. If this is NULL, a pointer
* to the (static) virtual function table for the MatrixMap 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 "MatrixMap" is used instead.
* Returned Value:
* A pointer to the new MatrixMap.
* 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.
*-
*/
#define KEY_LEN 50 /* Maximum length of a keyword */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
/* Local Variables: */
AstMatrixMap *new; /* Pointer to the new MatrixMap */
char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */
const char *form; /* String form */
int def; /* Is the matrix defined? */
int el; /* Element index */
int nel; /* No. of elements in the matrix */
int nin; /* No. of input coords */
int nout; /* No. of output coords */
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(channel);
/* Initialise. */
new = NULL;
/* Check the global error status. */
if ( !astOK ) return new;
/* If a NULL virtual function table has been supplied, then this is
the first loader to be invoked for this MatrixMap. In this case the
MatrixMap belongs to this class, so supply appropriate values to be
passed to the parent class loader (and its parent, etc.). */
if ( !vtab ) {
size = sizeof( AstMatrixMap );
vtab = &class_vtab;
name = "MatrixMap";
/* If required, initialise the virtual function table for this class. */
if ( !class_init ) {
astInitMatrixMapVtab( 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 MatrixMap. */
new = astLoadMapping( mem, size, (AstMappingVtab *) 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, "MatrixMap" );
/* Now obtain the Matrix storage form from this list. */
form = astReadString( channel, "form", Form[FULL] );
new->form = FindString( 3, Form, form, "the MatrixMap component 'Form'",
"astRead", astGetClass( channel ), status );
form = astFree( (void *) form );
/* Find the number of elements stored for each matrix. */
nin = astGetNin( (AstMapping *) new );
nout = astGetNout( (AstMapping *) new );
if( new->form == FULL ){
nel = nin*nout;
} else if( new->form == DIAGONAL ){
nel = MIN( nin, nout );
} else {
nel = 0;
}
/* Allocate memory to hold the forward matrix. */
new->f_matrix = (double *) astMalloc( sizeof(double)*(size_t)nel );
/* Now read the other data items from the list and use them to
initialise the appropriate instance variable(s) for this class. */
/* The forward matrix. */
if( new->f_matrix ){
def = 0;
for( el = 0; el < nel; el++ ){
(void) sprintf( buff, "m%d", el );
(new->f_matrix)[ el ] = astReadDouble( channel, buff, AST__BAD );
if( (new->f_matrix)[ el ] != AST__BAD ) def = 1;
}
/* Store a NULL pointer if no elements of the matrix were found. */
if( !def ) new->f_matrix = (double *) astFree( (void *) new->f_matrix );
}
/* Create an inverse matrix if possible, otherwise store a NULL pointer. */
if( new->f_matrix ){
new->i_matrix = InvertMatrix( new->form, nout, nin, new->f_matrix, status );
} else {
new->i_matrix = NULL;
}
/* If an error occurred, clean up by deleting the new MatrixMap. */
if ( !astOK ) new = astDelete( new );
}
/* Return the new MatrixMap pointer. */
return new;
/* Undefine macros local to this function. */
#undef KEY_LEN
}
/* 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. */
AstMatrixMap *astMtrRot_( AstMatrixMap *this, double theta,
const double axis[], int *status ){
if( !astOK ) return NULL;
return (**astMEMBER(this,MatrixMap,MtrRot))( this, theta, axis, status );
}
AstMatrixMap *astMtrMult_( AstMatrixMap *this, AstMatrixMap *a, int *status ){
if( !astOK ) return NULL;
return (**astMEMBER(this,MatrixMap,MtrMult))( this, a, status );
}
ast-8.0.7/wcsmath.h 0000664 0001750 0001750 00000004136 12610415012 011053 0000000 0000000 /*=============================================================================
*
* WCSLIB - an implementation of the FITS WCS proposal.
* Copyright (C) 1995-2002, Mark Calabretta
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Library General Public License as published
* by the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This library 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 Library
* General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street,Fifth Floor, Boston, MA 02110-1301, USA
*
* Correspondence concerning WCSLIB may be directed to:
* Internet email: mcalabre@atnf.csiro.au
* Postal address: Dr. Mark Calabretta,
* Australia Telescope National Facility,
* P.O. Box 76,
* Epping, NSW, 2121,
* AUSTRALIA
*
* Author: Mark Calabretta, Australia Telescope National Facility
* $Id$
*=============================================================================
*
* This version of wcstrig.h is based on the version in wcslib-2.9, but has
* been modified in the following ways by the Starlink project (e-mail:
* ussc@star.rl.ac.uk):
* - Changed the name of the WCSLIB_MATH macro to WCSLIB_MATH_INCLUDED
*===========================================================================*/
#ifndef WCSLIB_MATH_INCLUDED
#define WCSLIB_MATH_INCLUDED
#ifdef PI
#undef PI
#endif
#ifdef D2R
#undef D2R
#endif
#ifdef R2D
#undef R2D
#endif
#ifdef SQRT2
#undef SQRT2
#endif
#ifdef SQRT2INV
#undef SQRT2INV
#endif
#define PI 3.141592653589793238462643
#define D2R PI/180.0
#define R2D 180.0/PI
#define SQRT2 1.4142135623730950488
#define SQRT2INV 1.0/SQRT2
#endif /* WCSLIB_MATH_INCLUDED */
ast-8.0.7/specfluxframe.c 0000664 0001750 0001750 00000226137 12610415012 012253 0000000 0000000 /*
*class++
* Name:
* SpecFluxFrame
* Purpose:
* Compound spectrum/flux Frame.
* Constructor Function:
c astSpecFluxFrame
f AST_SPECFLUXFRAME
* Description:
* A SpecFluxFrame combines a SpecFrame and a FluxFrame into a single
* 2-dimensional compound Frame. Such a Frame can for instance be used
* to describe a Plot of a spectrum in which the first axis represents
* spectral position and the second axis represents flux.
* Inheritance:
* The SpecFluxFrame class inherits from the CmpFrame class.
* Attributes:
* The SpecFluxFrame class does not define any new attributes beyond
* those which are applicable to all CmpFrames. However, the attributes
* of the component Frames can be accessed as if they were attributes
* of the SpecFluxFrame. For instance, the SpecFluxFrame will recognise
* the "StdOfRest" attribute and forward access requests to the component
* SpecFrame. An axis index can optionally be appended to the end of any
* attribute name, in which case the request to access the attribute will
* be forwarded to the primary Frame defining the specified axis.
* Functions:
c The SpecFluxFrame class does not define any new functions beyond those
f The SpecFluxFrame class does not define any new routines beyond those
* which are applicable to all CmpFrames.
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* 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
* .
* Authors:
* DSB: David S. Berry (Starlink)
* History:
* 8-DEC-2004 (DSB):
* Original version.
* 29-APR-2011 (DSB):
* Prevent astFindFrame from matching a subclass template against a
* superclass target.
*class--
*/
/* Module Macros. */
/* ============== */
/* Set the name of the class we are implementing. This indicates to
the header files that define class interfaces that they should make
"protected" symbols available. */
#define astCLASS SpecFluxFrame
/* Define the first and last acceptable System values. */
#define FIRST_SYSTEM AST__COMP
#define LAST_SYSTEM AST__COMP
/* 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 "globals.h" /* Thread-safe global data access */
#include "object.h" /* Base Object class */
#include "mapping.h" /* Coordinate Mappings */
#include "unitmap.h" /* Unit Mappings */
#include "permmap.h" /* Coordinate permutation Mappings */
#include "cmpmap.h" /* Compound Mappings */
#include "axis.h" /* Coordinate axes */
#include "cmpframe.h" /* Parent CmpFrame class */
#include "tranmap.h" /* Separated transformation Mappings */
#include "mathmap.h" /* Algebraic Mappings */
#include "ratemap.h" /* Differential Mappings */
#include "specframe.h" /* SpecFrame class */
#include "fluxframe.h" /* FluxFrame class */
#include "specfluxframe.h" /* Interface definition for this class */
/* Error code definitions. */
/* ----------------------- */
#include "ast_err.h" /* AST error codes */
/* C header files. */
/* --------------- */
#include
#include
#include
#include
#include
#include
#include
#include
/* 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 int (* parent_match)( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * );
static int (* parent_subframe)( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * );
static const char *(* parent_gettitle)( AstFrame *, int * );
/* 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->GetTitle_Buff[ 0 ] = 0;
/* Create the function that initialises global data for this module. */
astMAKE_INITGLOBALS(SpecFluxFrame)
/* Define macros for accessing each item of thread specific global data. */
#define class_init astGLOBAL(SpecFluxFrame,Class_Init)
#define class_vtab astGLOBAL(SpecFluxFrame,Class_Vtab)
#define gettitle_buff astGLOBAL(SpecFluxFrame,GetTitle_Buff)
/* If thread safety is not needed, declare and initialise globals at static
variables. */
#else
static char gettitle_buff[ 101 ];
/* Define the class virtual function table and its initialisation flag
as static variables. */
static AstSpecFluxFrameVtab 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. */
AstSpecFluxFrame *astSpecFluxFrameId_( void *, void *, const char *, ... );
/* Prototypes for Private Member Functions. */
/* ======================================== */
static AstFluxFrame *GetFluxFrame( AstSpecFluxFrame *, int, int * );
static AstMapping *MakeMap2( AstSpecFluxFrame *, int * );
static AstMapping *MakeMap3( AstSpecFluxFrame *, AstSpecFluxFrame *, int * );
static AstMapping *MakeMapF( AstFluxFrame *, AstSpecFrame *, AstFluxFrame *, AstSpecFrame *, int * );
static AstMapping *MakeMapI( AstFluxFrame *, AstSpecFrame *, AstFluxFrame *, AstSpecFrame *, int * );
static AstSpecFrame *GetSpecFrame( AstSpecFluxFrame *, int, int * );
static const char *GetTitle( AstFrame *, int * );
static int MakeSFMapping( AstSpecFluxFrame *, AstSpecFluxFrame *, AstMapping **, int * );
static int Match( AstFrame *, AstFrame *, int, int **, int **, AstMapping **, AstFrame **, int * );
static int SubFrame( AstFrame *, AstFrame *, int, const int *, const int *, AstMapping **, AstFrame **, int * );
static void Dump( AstObject *, AstChannel *, int * );
/* Member functions. */
/* ================= */
static AstFluxFrame *GetFluxFrame( AstSpecFluxFrame *this, int std, int *status ){
/*
* Name:
* GetFluxFrame
* Purpose:
* Return a pointer to the FluxFrame in a FluxSpecFrame.
* Type:
* Private function.
* Synopsis:
* #include "specfluxframe.h"
* AstFluxFrame *GetFluxFrame( AstSpecFluxFrame *this, int std, int *status )
* Class Membership:
* SpecFluxFrame member function.
* Description:
* Returns a pointer to the FluxFrame in a SpecFluxFrame.
* Parameters:
* this
* Pointer to the SpecFluxFrame.
* std
* If non zero, then the returned FluxFrame is a standardised copy of
* the FluxFrame in the supplied SpecFluxFrame, in which the System has
* been set explicitly (rather than potentially being defaulted), and
* the Units have been cleared to use default units appropriate to
* the flux System.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the FluxFrame. Should be freed using astAnnul when no
* longer needed.
* Notes:
* NULL is returned if this function is invoked with the global error
* status set or if it should fail for any reason.
*/
/* Local Variables; */
AstFluxFrame *ff;
AstFluxFrame *ret;
/* Check the global error status. */
if ( !astOK ) return NULL;
/* The FluxFrame is always the second Frame in the parent CmpFrame. */
ff = (AstFluxFrame *) ((AstCmpFrame *)this)->frame2;
/* Produce a standardised copy of the FluxFrame if required, or clone the
above pointer otherwise. */
if( std ) {
ret = astCopy( ff );
astSetSystem( ret, astGetSystem( ff ) );
astClearUnit( ret, 0 );
} else {
ret = astClone( ff );
}
/* Annul the returned pointer if anything went wrong. */
if( !astOK ) ret = astAnnul( ret );
/* Return the result. */
return ret;
}
static AstSpecFrame *GetSpecFrame( AstSpecFluxFrame *this, int std, int *status ){
/*
* Name:
* GetSpecFrame
* Purpose:
* Return a pointer to the SpecFrame in a FluxSpecFrame.
* Type:
* Private function.
* Synopsis:
* #include "specfluxframe.h"
* AstSpecFrame *GetSpecFrame( AstSpecFluxFrame *this, int std, int *status )
* Class Membership:
* SpecFluxFrame member function.
* Description:
* Returns a pointer to the SpecFrame in a SpecFluxFrame.
* Parameters:
* this
* Pointer to the SpecFluxFrame.
* std
* If non zero, then the returned SpecFrame is a standardised copy of
* the SpecFrame in the supplied SpecFluxFrame, in which the System
* and Units have been set explicitly to the values appropriate to the
* flux system in use in the FluxFrame in the supplied SpecFluxFrame.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the FluxFrame. Should be freed using astAnnul when no
* longer needed.
* Notes:
* NULL is returned if this function is invoked with the global error
* status set or if it should fail for any reason.
*/
/* Local Variables; */
AstFluxFrame *ff;
AstSpecFrame *ret;
AstSpecFrame *sf;
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Get a pointer to the SpecFrame (the first Frame in the parent CmpFrame). */
sf = (AstSpecFrame *) ((AstCmpFrame *)this)->frame1;
/* If we want a standardised version of the SpecFrame... */
if( std ) {
/* The FluxFrame is always the second Frame in the parent CmpFrame. */
ff = (AstFluxFrame *) ((AstCmpFrame *)this)->frame2;
/* Produce a copy of the SpecFrame and set its System and Units
appropriate to the flux system (expressed in default units). */
ret = astCopy( sf );
astSetSystem( ret, astGetDensitySystem( ff ) );
astSetUnit( ret, 0, astGetDensityUnit( ff ) );
/* If we are not standardising the SpecFrame, just return a clone of the
pointer in the parent CmpFrame. */
} else {
ret = astClone( sf );
}
/* Annul the returned pointer if anything went wrong. */
if( !astOK ) ret = astAnnul( ret );
/* Return the result. */
return ret;
}
static const char *GetTitle( AstFrame *this_frame, int *status ) {
/*
* Name:
* GetTitle
* Purpose:
* Obtain a pointer to the Title string for a SpecFluxFrame.
* Type:
* Private function.
* Synopsis:
* #include "specfluxframe.h"
* const char *GetTitle( AstFrame *this_frame, int *status )
* Class Membership:
* SpecFluxFrame member function (over-rides the astGetTitle method
* inherited from the CmpFrame class).
* Description:
* This function returns a pointer to the Title string for a SpecFluxFrame.
* A pointer to a suitable default string is returned if no Title value has
* previously been set.
* Parameters:
* this
* Pointer to the SpecFluxFrame.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to a null-terminated character string containing the requested
* information.
* 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
AstSpecFluxFrame *this;
AstSpecFrame *sf;
AstFluxFrame *ff;
const char *result;
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(this_frame);
/* Initialise. */
result = NULL;
/* Obtain a pointer to the SpecFluxFrame structure. */
this = (AstSpecFluxFrame *) this_frame;
/* See if a Title string has been set. If so, use the parent astGetTitle
method to obtain a pointer to it. */
if ( astTestTitle( this ) ) {
result = (*parent_gettitle)( this_frame, status );
/* Otherwise, we will generate a default Title string. Obtain the values of the
SpecFrame's attributes that determine what this string will be. */
} else {
ff = GetFluxFrame( this, 0, status );
sf = GetSpecFrame( this, 0, status );
if( astOK ) {
sprintf( gettitle_buff, "%s versus %s", astGetLabel( ff, 0 ),
astGetLabel( sf, 0 ) );
gettitle_buff[ 0 ] = toupper( gettitle_buff[ 0 ] );
result = gettitle_buff;
}
ff = astAnnul( ff );
sf = astAnnul( sf );
}
/* If an error occurred, clear the returned pointer value. */
if ( !astOK ) result = NULL;
/* Return the result. */
return result;
}
void astInitSpecFluxFrameVtab_( AstSpecFluxFrameVtab *vtab, const char *name, int *status ) {
/*
*+
* Name:
* astInitSpecFluxFrameVtab
* Purpose:
* Initialise a virtual function table for a SpecFluxFrame.
* Type:
* Protected function.
* Synopsis:
* #include "specfluxframe.h"
* void astInitSpecFluxFrameVtab( AstSpecFluxFrameVtab *vtab, const char *name )
* Class Membership:
* SpecFluxFrame vtab initialiser.
* Description:
* This function initialises the component of a virtual function
* table which is used by the SpecFluxFrame 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 */
AstFrameVtab *frame; /* Pointer to Frame component of Vtab */
AstMappingVtab *mapping; /* Pointer to Mapping 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. */
astInitCmpFrameVtab( (AstCmpFrameVtab *) vtab, name );
/* Store a unique "magic" value in the virtual function table. This
will be used (by astIsASpecFluxFrame) 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 = &(((AstCmpFrameVtab *) vtab)->id);
/* Initialise member function pointers. */
/* ------------------------------------ */
/* Store pointers to the member functions (implemented here) that
provide virtual methods for this class. */
/* Save the inherited pointers to methods that will be extended, and
replace them with pointers to the new member functions. */
object = (AstObjectVtab *) vtab;
frame = (AstFrameVtab *) vtab;
mapping = (AstMappingVtab *) vtab;
/* Store replacement pointers for methods which will be over-ridden by
new member functions implemented here. */
parent_match = frame->Match;
frame->Match = Match;
parent_subframe = frame->SubFrame;
frame->SubFrame = SubFrame;
parent_gettitle = frame->GetTitle;
frame->GetTitle = GetTitle;
/* Declare the copy constructor, destructor and class dump
function. */
astSetDump( vtab, Dump, "SpecFluxFrame",
"Compound spectral/flux coordinate system description" );
/* 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 AstMapping *MakeMap2( AstSpecFluxFrame *this, int *status ){
/*
* Name:
* MakeMap2
* Purpose:
* Generate the second Mapping required by MakeSFMapping
* Type:
* Private function.
* Synopsis:
* #include "specfluxframe.h"
* AstMapping *MakeMap2( AstSpecFluxFrame *this, int *status )
* Class Membership:
* SpecFluxFrame member function.
* Description:
* The second Mapping used by MakeSFMapping contains three Mappings in
* parallel which converts v1 (flux value) and x1 (spectral position) into
* default units, and passes the third axis (a copy of flux value)
* unchanged.
* Parameters:
* this
* Pointer to the SpecFluxFrame to use.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the required Mapping, or NULL if the Mapping cannot be
* created. The Mapping will have 3 inputs and 3 outputs.
* Notes:
* NULL is returned if this function is invoked with the global error
* status set or if it should fail for any reason.
*/
/* Local Variables: */
AstFrame *f1;
AstFrame *f2;
AstFrameSet *fs;
AstMapping *ax1_map;
AstMapping *ax2_map;
AstMapping *ax3_map;
AstMapping *ret;
AstMapping *tmap;
/* Initialise. */
ret = NULL;
/* Check the global error status. */
if ( !astOK ) return ret;
/* Input 0 is the supplied FluxFrame value and output 0 is the corresponding
value in the default units for the FluxFrame system. Take a copy of the
supplied FluxFrame, and fix its System value (which may be a default value
based on the Units string), and then clear the Units so that it represents
default units for the System. */
f1 = (AstFrame *) GetFluxFrame( this, 0, status );
f2 = (AstFrame *) GetFluxFrame( this, 1, status );
/* Now, if conversion was possible, get the Mapping from the supplied
FluxFrame to the default units FluxFrame. */
fs = astConvert( f1, f2, "" );
f1 = astAnnul( f1 );
f2 = astAnnul( f2 );
if( fs ) {
ax1_map = astGetMapping( fs, AST__BASE, AST__CURRENT );
fs = astAnnul( fs );
/* Input 1 is the supplied SpecFrame value and output 1 is the corresponding
value in the spectral system used by the flux system (wavelength or
frequency). Take a copy of the supplied SpecFrame, and fix its System
value to wavelength or frequency (depending on the System value of the
FluxFrame), and set up units of Hz or Angstrom (these are the spectral
position units used within the default flux units for a FluxFrame). */
f1 = (AstFrame *) GetSpecFrame( this, 0, status );
f2 = (AstFrame *) GetSpecFrame( this, 1, status );
/* Now, if conversion was possible, get the Mapping from the supplied
SpecFrame to the required SpecFrame. */
fs = astConvert( f1, f2, "" );
f1 = astAnnul( f1 );
f2 = astAnnul( f2 );
if( fs ) {
ax2_map = astGetMapping( fs, AST__BASE, AST__CURRENT );
fs = astAnnul( fs );
/* Create a UnitMap for the 3rd axis. */
ax3_map = (AstMapping *) astUnitMap( 1, "", status );
/* Create a parallel CmpMap containing the three Mappings. */
tmap = (AstMapping *) astCmpMap( ax1_map, ax2_map, 0, "", status );
ret = (AstMapping *) astCmpMap( tmap, ax3_map, 0, "", status );
/* Free remaining resources. */
tmap = astAnnul( tmap );
ax2_map = astAnnul( ax2_map );
ax3_map = astAnnul( ax3_map );
}
ax1_map = astAnnul( ax1_map );
}
/* If an error has occurred, return NULL. */
if( !astOK ) ret = astAnnul( ret );
/* Return the result */
return ret;
}
static AstMapping *MakeMap3( AstSpecFluxFrame *target, AstSpecFluxFrame *result, int *status ){
/*
* Name:
* MakeMap3
* Purpose:
* Generate the third Mapping required by MakeSFMapping
* Type:
* Private function.
* Synopsis:
* #include "specfluxframe.h"
* AstMapping *MakeMap3( AstSpecFluxFrame *target, AstSpecFluxFrame *result, int *status )
* Class Membership:
* SpecFluxFrame member function.
* Description:
* The third Mapping used by MakeSFMapping converts input (v1,x1) in
* default units to output (v2,x2) in default units. The third axis (x1)
* in original units is converted to x2 in original units.
* Parameters:
* target
* Pointer to the first SpecFluxFrame.
* result
* Pointer to the second SpecFluxFrame.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the required Mapping, or NULL if the Mapping cannot be
* created. The Mapping will have 3 inputs and 3 outputs.
* Notes:
* NULL is returned if this function is invoked with the global error
* status set or if it should fail for any reason.
*/
/* Local Variables: */
AstFluxFrame *ff2;
AstFluxFrame *ff1;
AstFrameSet *fs;
AstMapping *fmap;
AstMapping *imap;
AstMapping *mapa;
AstMapping *mapb;
AstMapping *ret;
AstSpecFrame *sf2;
AstSpecFrame *sf1;
/* Initialise */
ret = NULL;
/* Check the global error status. */
if ( !astOK ) return ret;
/* The first two inputs and outputs are related by a TranMap which
converts between standardised (v1,x1) and standardised (v2,x2). Get
pointers to the standardised SpecFrames and FluxFrames in the two
supplied SpecFluxFrames. */
ff1 = GetFluxFrame( target, 1, status );
sf1 = GetSpecFrame( target, 1, status );
ff2 = GetFluxFrame( result, 1, status );
sf2 = GetSpecFrame( result, 1, status );
/* Create the Mapping which defines the forward transformation of the
required TranMap. The forward transformation of this Mapping goes from
(v1,x1) to (v2,x2). */
fmap = MakeMapF( ff1, sf1, ff2, sf2, status );
/* Create the Mapping which defines the inverse transformation of the
required TranMap. The inverse transformation of this Mapping goes from
(v2,x2) to (v1,x1). */
imap = MakeMapI( ff1, sf1, ff2, sf2, status );
/* Combine these into a TranMap */
if( fmap && imap ) {
mapa = (AstMapping *) astTranMap( fmap, imap, "", status );
} else {
mapa = NULL;
}
/* Free resources. */
ff1 = astAnnul( ff1 );
sf1 = astAnnul( sf1 );
ff2 = astAnnul( ff2 );
sf2 = astAnnul( sf2 );
if( fmap ) fmap = astAnnul( fmap );
if( imap ) imap = astAnnul( imap );
/* The third input and output are related by a Mapping which converts
between supplied (x1) and supplied (x2). Get pointers to the original
unmodified SpecFrames in the two supplied SpecFluxFrames. */
sf1 = GetSpecFrame( target, 0, status );
sf2 = GetSpecFrame( result, 0, status );
/* Find the Mapping from the first to the second. */
fs = astConvert( sf1, sf2, "" );
if( fs ) {
mapb = astGetMapping( fs, AST__BASE, AST__CURRENT );
fs = astAnnul( fs );
} else {
mapb = NULL;
}
/* Free resources. */
sf1 = astAnnul( sf1 );
sf2 = astAnnul( sf2 );
/* Combine the two Mappings in parallel. */
if( mapa && mapb ) ret = (AstMapping *) astCmpMap( mapa, mapb, 0, "", status );
if( mapa ) mapa = astAnnul( mapa );
if( mapb ) mapb = astAnnul( mapb );
/* If an error has occurred, return NULL. */
if( !astOK ) ret = astAnnul( ret );
/* Return the result */
return ret;
}
static AstMapping *MakeMapF( AstFluxFrame *v1, AstSpecFrame *x1,
AstFluxFrame *v2, AstSpecFrame *x2, int *status ){
/*
* Name:
* MakeMapF
* Purpose:
* Generate the forward part of the third Mapping required by MakeSFMapping
* Type:
* Private function.
* Synopsis:
* #include "specfluxframe.h"
* AstMapping *MakeMapF( AstFluxFrame *v1, AstSpecFrame *x1,
* AstFluxFrame *v2, AstSpecFrame *x2, int *status )
* Class Membership:
* SpecFluxFrame member function.
* Description:
* Theis creates a 2-input 2-output Mapping which transforms
* input (v1,x1) in default units to output (v2,x2) in default units.
* Parameters:
* v1
* Pointer to the standardised input FluxFrame.
* x1
* Pointer to the standardised input SpecFrame.
* v2
* Pointer to the standardised output FluxFrame.
* x2
* Pointer to the standardised output SpecFrame.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the required Mapping, or NULL if the Mapping cannot be
* created.
* Notes:
* NULL is returned if this function is invoked with the global error
* status set or if it should fail for any reason.
*/
/* Local Variables: */
AstCmpMap *cmap1;
AstCmpMap *cmap2;
AstCmpMap *cmap3;
AstFrameSet *fs;
AstMapping *m;
AstMapping *ret;
AstMathMap *div;
AstPermMap *perm;
AstRateMap *rate;
AstUnitMap *unit;
const char *fwd[1];
const char *inv[2];
int inperm[ 2 ];
int outperm[ 3 ];
/* Initialise */
ret = NULL;
/* Check the global error status. */
if ( !astOK ) return ret;
/* First create the required component Mappings.
--------------------------------------------- */
/* A Mapping which maps input spectral position (x1) into output spectral
position (x2). */
fs = astConvert( x1, x2, "" );
if( fs ) {
m = astGetMapping( fs, AST__BASE, AST__CURRENT );
/* A 1-input 1-output Mapping in which the input is spectral position (x1)
and the output is the rate of change of output spectral position (x2)
with respect to input spectral position (x1). */
rate = astRateMap( m, 0, 0, "", status );
/* A MathMap which is used to divide the flux value (v1) by the absolute rate
of change of x2 wrt x1 */
fwd[ 0 ] = "out=in0/abs(in1)";
inv[ 0 ] = "in0";
inv[ 1 ] = "in1";
div = astMathMap( 2, 1, 1, fwd, 2, inv, "", status );
/* A 1D UnitMap used to copy v1. */
unit = astUnitMap( 1, "", status );
/* A PermMap which is used to produce an extra output copy of x1. */
inperm[ 0 ] = 0;
inperm[ 1 ] = 2;
outperm[ 0 ] = 0;
outperm[ 1 ] = 1;
outperm[ 2 ] = 1;
perm = astPermMap( 2, inperm, 3, outperm, NULL, "", status );
/* Now combine these component Mappings together.
--------------------------------------------- */
/* First put the UnitMap and the RateMap in parallel. This produces a 2-in
2-out Mapping in which the inputs are (v1,x1) and the outputs are
(v1,dx2/dx1). */
cmap1 = astCmpMap( unit, rate, 0, "", status );
/* Now put this in series with the dividing MathMap. This results in a
2-in, 1-out Mapping in which the inputs are v1 and x1 and the single
output is v2. */
cmap2 = astCmpMap( cmap1, div, 1, "", status );
/* Now put this in parallel with the x1->x2 Mapping. This results in a
3-in, 2-out Mapping in which the inputs are (v1,x1,x1) and the outputs
are (v2,x2). */
cmap3 = astCmpMap( cmap2, m, 0, "", status );
/* Finally put this in series with the PermMap. This results in a 2-in,
2-out Mapping in which the inputs are (v1,x1) and the outputs are
(v2,x2). */
ret = (AstMapping *) astCmpMap( perm, cmap3, 1, "", status );
/* Free resources. */
fs = astAnnul( fs );
m = astAnnul( m );
rate = astAnnul( rate );
div= astAnnul( div );
unit = astAnnul( unit );
perm = astAnnul( perm );
cmap1 = astAnnul( cmap1 );
cmap2 = astAnnul( cmap2 );
cmap3 = astAnnul( cmap3 );
}
/* If an error has occurred, return NULL. */
if( !astOK ) ret = astAnnul( ret );
/* Return the result */
return ret;
}
static AstMapping *MakeMapI( AstFluxFrame *v1, AstSpecFrame *x1,
AstFluxFrame *v2, AstSpecFrame *x2, int *status ){
/*
* Name:
* MakeMapI
* Purpose:
* Generate the inverse part of the third Mapping required by MakeSFMapping
* Type:
* Private function.
* Synopsis:
* #include "specfluxframe.h"
* AstMapping *MakeMapI( AstFluxFrame *v1, AstSpecFrame *x1,
* AstFluxFrame *v2, AstSpecFrame *x2 )
* Class Membership:
* SpecFluxFrame member function.
* Description:
* This creates a 2-input 2-output Mapping in which the inverse
* transformation transforms "outputs" representing (v2,x2) into
* "inputs" representing (v1,x1).
* Parameters:
* v1
* Pointer to the standardised input FluxFrame.
* x1
* Pointer to the standardised input SpecFrame.
* v2
* Pointer to the standardised output FluxFrame.
* x2
* Pointer to the standardised output SpecFrame.
* Returned Value:
* A pointer to the required Mapping, or NULL if the Mapping cannot be
* created.
* Notes:
* NULL is returned if this function is invoked with the global error
* status set or if it should fail for any reason.
*/
/* Local Variables: */
AstCmpMap *cmap1;
AstCmpMap *cmap2;
AstCmpMap *cmap3;
AstCmpMap *cmap4;
AstCmpMap *cmap5;
AstFrameSet *fs;
AstMapping *m;
AstMapping *ret;
AstMathMap *mult;
AstPermMap *perm;
AstRateMap *rate;
AstUnitMap *unit;
const char *fwd[1];
const char *inv[2];
int inperm[ 2 ];
int outperm[ 3 ];
/* Initialise */
ret = NULL;
/* Check the global error status. */
if ( !astOK ) return ret;
/* We create a CmpMap in which the forward transformation foes from
(v2,x2) to (v1,x1) and we finally invert this Mapping to get the
required Mapping in which the *inverse* transformation goes from
(v2,x2) to (v1,x1).
First create the required component Mappings.
--------------------------------------------- */
/* A Mapping which maps spectral position x1 into spectral position x2. */
fs = astConvert( x1, x2, "" );
if( fs ) {
m = astGetMapping( fs, AST__BASE, AST__CURRENT );
/* A 1-input 1-output Mapping in which the input is spectral position x1
and the output is the rate of change of spectral position x2 with
respect to spectral position x1. */
rate = astRateMap( m, 0, 0, "", status );
/* Now invert "m" so that its forward transformation goes from x2 to x1.
The RateMap created above retains a copy of the original Invert flag
for "m" and uses it in preference to the current value when transforming
points. */
astInvert( m );
/* A MathMap which is used to multiple the flux value v2 by the
absolute rate of change of x2 wrt x1 */
fwd[ 0 ] = "out=in0*abs(in1)";
inv[ 0 ] = "in0";
inv[ 1 ] = "in1";
mult = astMathMap( 2, 1, 1, fwd, 2, inv, "", status );
/* A 1D UnitMap used to copy various values. */
unit = astUnitMap( 1, "", status );
/* A PermMap which is used to produce an extra copy of x1. */
inperm[ 0 ] = 0;
inperm[ 1 ] = 2;
outperm[ 0 ] = 0;
outperm[ 1 ] = 1;
outperm[ 2 ] = 1;
perm = astPermMap( 2, inperm, 3, outperm, NULL, "", status );
/* Now combine these component Mappings together.
--------------------------------------------- */
/* First put the UnitMap and the RateMap in parallel. This produces a 2-in
2-out Mapping in which the inputs are (v2,x1) and the outputs are
(v2,dx2/dx1). */
cmap1 = astCmpMap( unit, rate, 0, "", status );
/* Now put this in series with the multiplying MathMap. This results in a
2-in, 1-out Mapping in which the inputs are (v2,x1) and the single
output is v1. */
cmap2 = astCmpMap( cmap1, mult, 1, "", status );
/* Now put this in parallel with the UnitMap to get a 3-in, 2-out Mapping
in which the inputs are (v2,x1,x1) and the outputs are (v1,x1). */
cmap3 = astCmpMap( cmap2, unit, 0, "", status );
/* Now put this in series with the PermMap to get a 2-in, 2-out Mapping
in which the inputs are (v2,x1) and the outputs are (v1,x1). */
cmap4 = astCmpMap( perm, cmap3, 1, "", status );
/* Now put the UnitMap in parallel with the (x2->x1 Mapping to get a
2-in, 2-out Mapping in which the inputs are (v2,x2) and the outputs are
(v2,x1). */
cmap5 = astCmpMap( unit, m, 0, "", status );
/* Finally put this in series with "cmap4" to get a 2-in 2-out Mapping
from (v2,x2) to (v1,x1). */
ret = (AstMapping *) astCmpMap( cmap5, cmap4, 1, "", status );
/* Invert this so that the inverse transformation goes from (v2,x2) to
(v1,x1). */
astInvert( ret );
/* Free resources. */
fs = astAnnul( fs );
m = astAnnul( m );
rate = astAnnul( rate );
mult = astAnnul( mult );
unit = astAnnul( unit );
perm = astAnnul( perm );
cmap1 = astAnnul( cmap1 );
cmap2 = astAnnul( cmap2 );
cmap3 = astAnnul( cmap3 );
cmap4 = astAnnul( cmap4 );
cmap5 = astAnnul( cmap5 );
}
/* If an error has occurred, return NULL. */
if( !astOK ) ret = astAnnul( ret );
/* Return the result */
return ret;
}
static int MakeSFMapping( AstSpecFluxFrame *target, AstSpecFluxFrame *result,
AstMapping **map, int *status ){
/*
* Name:
* MakeSFMapping
* Purpose:
* Generate a Mapping between two SpecFluxFrames.
* Type:
* Private function.
* Synopsis:
* #include "specfluxframe.h"
* int MakeSFMapping( AstSpecFluxFrame *target, AstSpecFluxFrame *result,
* AstMapping **map, int *status )
* Class Membership:
* SpecFluxFrame member function.
* Description:
* This function takes two SpecFluxFrames and generates a Mapping that
* converts between them, taking account of differences in their
* coordinate systems, systems, units, etc. (but not allowing for any
* axis permutations).
* Parameters:
* target
* Pointer to the first SpecFluxFrame.
* result
* Pointer to the second SpecFluxFrame.
* map
* Pointer to a location which is to receive a pointer to the
* returned Mapping. The forward transformation of this Mapping
* will convert from "target" coordinates to "result"
* coordinates, and the inverse transformation will convert in
* the opposite direction (all coordinate values in radians).
* status
* Pointer to the inherited status variable.
* Returned Value:
* Non-zero if the Mapping could be generated, or zero if the two
* SpecFluxFrames are sufficiently un-related that no meaningful Mapping
* can be produced.
* Notes:
* A value of zero is returned if this function is invoked with the
* global error status set or if it should fail for any reason.
*/
/* Local Variables: */
AstMapping *map1;
AstMapping *map2;
AstMapping *map3;
AstMapping *map4;
AstMapping *map5;
AstMapping *tmap1;
AstMapping *tmap2;
AstMapping *tmap3;
AstMapping *tmap4;
int inperm[2];
int match;
int outperm[3];
/* Check the global error status. */
if ( !astOK ) return 0;
/* Initialise the returned values. */
match = 0;
*map = NULL;
/* Initialise other things. */
map1 = NULL;
map2 = NULL;
map3 = NULL;
map4 = NULL;
map5 = NULL;
tmap1 = NULL;
tmap2 = NULL;
tmap3 = NULL;
tmap4 = NULL;
/* At the top level, the required Mapping consists of five Mappings in
series. Inputs 0 and 1 of the total Mapping correspond to the SpecFrame
and FluxFrame in the target SpecFluxFrame. These are referred to as X1
and V1. Outputs 0 and 1 of the total Mapping correspond to the SpecFrame
and FluxFrame in the result SpecFluxFrame. These are referred to as X2
and V2. */
/* Map1 is a PermMap which copies v1 to its first output and x1 to its
second and third outputs. The inverse transformation copies v1 from
its first output and x1 from its third output. */
inperm[ 0 ] = 2;
inperm[ 1 ] = 0;
outperm[ 0 ] = 1;
outperm[ 1 ] = 0;
outperm[ 2 ] = 0;
map1 = (AstMapping *) astPermMap( 2, inperm, 3, outperm, NULL, "", status );
/* Map2 contains three Mappings in parallel which converts v1 and x1 into
default units, and passes the third axis unchanged. */
map2 = MakeMap2( target, status );
/* Map3 converts ( v1,x1) in default units to (v2,x2) in default units.
The third axis (x1) in original units is convert to x2 in original
units. */
map3 = map2 ? MakeMap3( target, result, status ) : NULL;
/* Map4 converts (v2,x2) in default units to (v2,x2) in original units
and passes the third axis unchanged. This is similar to Map2 but based
on the result ratherthan the target, and in the opposite direction. */
if( map3 ) {
map4 = MakeMap2( result, status );
if( map4 ) astInvert( map4 );
} else {
map4 = NULL;
}
/* Map5 is a PermMap which is the inverse of Map1. */
map5 = map4 ? astCopy( map1 ) : NULL;
if( map5 ) astInvert( map5 );
/* Combine all 6 Mappings in series. */
if( map5 ) {
tmap1 = (AstMapping *) astCmpMap( map1, map2, 1, "", status );
tmap2 = (AstMapping *) astCmpMap( tmap1, map3, 1, "", status );
tmap3 = (AstMapping *) astCmpMap( tmap2, map4, 1, "", status );
tmap4 = (AstMapping *) astCmpMap( tmap3, map5, 1, "", status );
/* Return the simplified total Mapping. */
*map = astSimplify( tmap4 );
match = 1;
}
/* Free resources. */
if( map1 ) map1 = astAnnul( map1 );
if( map2 ) map2 = astAnnul( map2 );
if( map3 ) map3 = astAnnul( map3 );
if( map4 ) map4 = astAnnul( map4 );
if( map5 ) map5 = astAnnul( map5 );
if( tmap1 ) tmap1 = astAnnul( tmap1 );
if( tmap2 ) tmap2 = astAnnul( tmap2 );
if( tmap3 ) tmap3 = astAnnul( tmap3 );
if( tmap4 ) tmap4 = astAnnul( tmap4 );
/* If an error occurred, annul the returned Mapping and clear the
returned values. */
if ( !astOK ) {
*map = astAnnul( *map );
match = 0;
}
/* Return the result. */
return match;
}
static int Match( AstFrame *template_frame, AstFrame *target, int matchsub,
int **template_axes, int **target_axes,
AstMapping **map, AstFrame **result, int *status ) {
/*
* Name:
* Match
* Purpose:
* Determine if conversion is possible between two coordinate systems.
* Type:
* Private function.
* Synopsis:
* #include "specfluxframe.h"
* int Match( AstFrame *template, AstFrame *target, int matchsub,
* int **template_axes, int **target_axes,
* AstMapping **map, AstFrame **result, int *status )
* Class Membership:
* SpecFluxFrame member function (over-rides the protected astMatch
* method inherited from the Frame class).
* Description:
* This function matches a "template" SpecFluxFrame to a "target" Frame
* and determines whether it is possible to convert coordinates
* between them. If it is, a Mapping that performs the
* transformation is returned along with a new Frame that describes
* the coordinate system that results when this Mapping is applied
* to the "target" coordinate system. In addition, information is
* returned to allow the axes in this "result" Frame to be
* associated with the corresponding axes in the "target" Frame and
* "template" SpecFluxFrame from which they are derived.
* Parameters:
* template
* Pointer to the template SpecFluxFrame. This describes the
* coordinate system (or set of possible coordinate systems)
* into which we wish to convert our coordinates.
* target
* Pointer to the target Frame. This describes the coordinate
* system in which we already have coordinates.
* matchsub
* If zero then a match only occurs if the template is of the same
* class as the target, or of a more specialised class. If non-zero
* then a match can occur even if this is not the case.
* template_axes
* Address of a location where a pointer to int will be returned
* if the requested coordinate conversion is possible. This
* pointer will point at a dynamically allocated array of
* integers with one element for each axis of the "result" Frame
* (see below). It must be freed by the caller (using astFree)
* when no longer required.
*
* For each axis in the result Frame, the corresponding element
* of this array will return the (zero-based) index of the
* template SpecFluxFrame axis from which it is derived. If it is not
* derived from any template axis, a value of -1 will be
* returned instead.
* target_axes
* Address of a location where a pointer to int will be returned
* if the requested coordinate conversion is possible. This
* pointer will point at a dynamically allocated array of
* integers with one element for each axis of the "result" Frame
* (see below). It must be freed by the caller (using astFree)
* when no longer required.
*
* For each axis in the result Frame, the corresponding element
* of this array will return the (zero-based) index of the
* target Frame axis from which it is derived. If it is not
* derived from any target axis, a value of -1 will be returned
* instead.
* map
* Address of a location where a pointer to a new Mapping will
* be returned if the requested coordinate conversion is
* possible. If returned, the forward transformation of this
* Mapping may be used to convert coordinates between the
* "target" Frame and the "result" Frame (see below) and the
* inverse transformation will convert in the opposite
* direction.
* result
* Address of a location where a pointer to a new Frame will be
* returned if the requested coordinate conversion is
* possible. If returned, this Frame describes the coordinate
* system that results from applying the returned Mapping
* (above) to the "target" coordinate system. In general, this
* Frame will combine attributes from (and will therefore be
* more specific than) both the target Frame and the template
* SpecFluxFrame. In particular, when the template allows the
* possibility of transformaing to any one of a set of
* alternative coordinate systems, the "result" Frame will
* indicate which of the alternatives was used.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A non-zero value is returned if the requested coordinate
* conversion is possible. Otherwise zero is returned (this will
* not in itself result in an error condition).
* Notes:
* - By default, the "result" Frame will have its number of axes
* and axis order determined by the "template" SpecFluxFrame. However,
* if the PreserveAxes attribute of the template SpecFluxFrame is
* non-zero, then the axis count and axis order of the "target"
* Frame will be used instead.
* - 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: */
AstSpecFluxFrame *template; /* Pointer to template SpecFluxFrame structure */
int match; /* Coordinate conversion possible? */
int swap1; /* Template axes swapped? */
int swap2; /* Target axes swapped? */
int swap; /* Additional axis swap needed? */
/* Initialise the returned values. */
*template_axes = NULL;
*target_axes = NULL;
*map = NULL;
*result = NULL;
match = 0;
/* Check the global error status. */
if ( !astOK ) return match;
/* Obtain a pointer to the template SpecFluxFrame structure. */
template = (AstSpecFluxFrame *) template_frame;
/* If the target is not a SpecFluxFrame, use the results returned by the
parent Match method inherited from the CmpFrame class. */
if( !astIsASpecFluxFrame( target ) ) {
match = (*parent_match)( template_frame, target, matchsub, template_axes,
target_axes, map, result, status );
/* If the target is a SpecFluxFrame, see if we can convert between target
and template */
} else {
/* We must now decide how the order of the axes in the result Frame relates to
the order of axes in the target Frame. There are two factors involved. The
first depends on whether the axis permutation array for the template
SpecFluxFrame (whose method we are executing) causes an axis
reversal. Determine this by permuting axis index zero. */
swap1 = ( astValidateAxis( template, 0, 1, "astMatch" ) != 0 );
/* The second factor depends on whether the axes of the target SpecFluxFrame
causes an axis reversal. Determine this by permuting axis index zero. */
swap2 = ( astValidateAxis( target, 0, 1, "astMatch" ) != 0 );
/* Combine these to determine if an additional axis swap will be
needed. */
swap = ( swap1 != swap2 );
/* Now check to see if this additional swap is permitted by the template's
Permute attribute. */
match = ( !swap || astGetPermute( template ) );
/* Allocate the target and template axes arrays. */
*template_axes = astMalloc( sizeof(int)*2 );
*target_axes = astMalloc( sizeof(int)*2 );
/* If the Frames still match, we next set up the axis association
arrays. */
if ( astOK && match ) {
/* If the target axis order is to be preserved, then the target axis
association involves no permutation but the template axis
association may involve an axis swap. */
if ( astGetPreserveAxes( template ) ) {
(*template_axes)[ 0 ] = swap;
(*template_axes)[ 1 ] = !swap;
(*target_axes)[ 0 ] = 0;
(*target_axes)[ 1 ] = 1;
/* Otherwise, any swap applies to the target axis association
instead. */
} else {
(*template_axes)[ 0 ] = 0;
(*template_axes)[ 1 ] = 1;
(*target_axes)[ 0 ] = swap;
(*target_axes)[ 1 ] = !swap;
}
/* Use the target's "astSubFrame" method to create a new Frame (the
result Frame) with copies of the target axes in the required
order. This process also overlays the template attributes on to the
target Frame and returns a Mapping between the target and result
Frames which effects the required coordinate conversion. */
match = astSubFrame( target, template, 2, *target_axes, *template_axes,
map, result );
/* If an error occurred, or conversion to the result Frame's
coordinate system was not possible, then free all memory, annul the
returned objects, and reset the returned value. */
if ( !astOK || !match ) {
*template_axes = astFree( *template_axes );
*target_axes = astFree( *target_axes );
if( *map ) *map = astAnnul( *map );
if( *result ) *result = astAnnul( *result );
match = 0;
}
}
}
/* Return the result. */
return match;
}
static int SubFrame( AstFrame *target_frame, AstFrame *template,
int result_naxes, const int *target_axes,
const int *template_axes, AstMapping **map,
AstFrame **result, int *status ) {
/*
* Name:
* SubFrame
* Purpose:
* Select axes from a SpecFluxFrame and convert to the new coordinate system.
* Type:
* Private function.
* Synopsis:
* #include "specfluxframe.h"
* int SubFrame( AstFrame *target, AstFrame *template,
* int result_naxes, const int *target_axes,
* const int *template_axes, AstMapping **map,
* AstFrame **result, int *status )
* Class Membership:
* SpecFluxFrame member function (over-rides the protected astSubFrame
* method inherited from the Frame class).
* Description:
* This function selects a requested sub-set (or super-set) of the
* axes from a "target" SpecFluxFrame and creates a new Frame with
* copies of the selected axes assembled in the requested order. It
* then optionally overlays the attributes of a "template" Frame on
* to the result. It returns both the resulting Frame and a Mapping
* that describes how to convert between the coordinate systems
* described by the target and result Frames. If necessary, this
* Mapping takes account of any differences in the Frames'
* attributes due to the influence of the template.
* Parameters:
* target
* Pointer to the target SpecFluxFrame, from which axes are to be selected.
* template
* Pointer to the template Frame, from which new attributes for
* the result Frame are to be obtained. Optionally, this may be
* NULL, in which case no overlaying of template attributes will
* be performed.
* result_naxes
* Number of axes to be selected from the target Frame. This
* number may be greater than or less than the number of axes in
* this Frame (or equal).
* target_axes
* Pointer to an array of int with result_naxes elements, giving
* a list of the (zero-based) axis indices of the axes to be
* selected from the target SpecFluxFrame. The order in which these
* are given determines the order in which the axes appear in
* the result Frame. If any of the values in this array is set
* to -1, the corresponding result axis will not be derived from
* the target Frame, but will be assigned default attributes
* instead.
* template_axes
* Pointer to an array of int with result_naxes elements. This
* should contain a list of the template axes (given as
* zero-based axis indices) with which the axes of the result
* Frame are to be associated. This array determines which axes
* are used when overlaying axis-dependent attributes of the
* template on to the result. If any element of this array is
* set to -1, the corresponding result axis will not receive any
* template attributes.
*
* If the template argument is given as NULL, this array is not
* used and a NULL pointer may also be supplied here.
* map
* Address of a location to receive a pointer to the returned
* Mapping. The forward transformation of this Mapping will
* describe how to convert coordinates from the coordinate
* system described by the target SpecFluxFrame to that described by
* the result Frame. The inverse transformation will convert in
* the opposite direction.
* result
* Address of a location to receive a pointer to the result Frame.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A non-zero value is returned if coordinate conversion is
* possible between the target and the result Frame. Otherwise zero
* is returned and *map and *result are returned as NULL (but this
* will not in itself result in an error condition). In general,
* coordinate conversion should always be possible if no template
* Frame is supplied but may not always be possible otherwise.
* 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.
* Implementation Deficiencies:
* - It is not clear that the method of handling "extra" axes is
* the best one, nor is the method of setting the "following" flag
* necessarily correct. However, it is also not obvious that this
* feature will ever be needed, so improvements have been left
* until the requirement is clearer.
*/
/* Local Variables: */
AstMapping *tmpmap; /* Temporary Mapping pointer */
AstPermMap *permmap; /* Pointer to PermMap */
AstSpecFluxFrame *target; /* Pointer to target SpecFluxFrame structure */
int match; /* Coordinate conversion is possible? */
int perm[ 2 ]; /* Permutation array for axis swap */
int result_swap; /* Swap result SpecFluxFrame coordinates? */
int target_swap; /* Swap target SpecFluxFrame coordinates? */
/* Initialise the returned values. */
*map = NULL;
*result = NULL;
match = 0;
/* Check the global error status. */
if ( !astOK ) return match;
/* If the template is not a SpecFluxFrame we use the parent SubFrame
method inherited form the CmpFrame class. */
if( !template || !astIsASpecFluxFrame( template ) || result_naxes != 2 ) {
match = (*parent_subframe)( target_frame, template, result_naxes,
target_axes, template_axes, map, result, status );
/* Otherwise... */
} else {
/* Obtain a pointer to the target SpecFluxFrame structure. */
target = (AstSpecFluxFrame *) target_frame;
/* Form the result from a copy of the target and then permute its axes
into the order required. */
*result = astCopy( target );
astPermAxes( *result, target_axes );
/* Overlay the template attributes on to the result SpecFrame. */
astOverlay( template, template_axes, *result );
/* Generate a Mapping that takes account of changes in the coordinate
system (system, units, etc.) between the target SpecFluxFrame and the
result SpecFluxFrame. If this Mapping can be generated, set "match" to
indicate that coordinate conversion is possible. */
match = MakeSFMapping( target, (AstSpecFluxFrame *) *result, map, status );
/* If a Mapping has been obtained, it will expect coordinate values to be
supplied in (flux,spec) pairs. Test whether we need to swap the
order of the target SpecFluxFrame coordinates to conform with this. */
if ( astOK && match ) {
target_swap = ( astValidateAxis( target, 0, 1, "astSubFrame" ) != 0 );
/* Coordinates will also be delivered in (flux,spec) pairs, so check
to see whether the result SpecFluxFrame coordinate order should be
swapped. */
result_swap = ( target_swap != ( target_axes[ 0 ] != 0 ) );
/* If either set of coordinates needs swapping, create a PermMap that
will swap a pair of coordinates. */
permmap = NULL;
if ( target_swap || result_swap ) {
perm[ 0 ] = 1;
perm[ 1 ] = 0;
permmap = astPermMap( 2, perm, 2, perm, NULL, "", status );
}
/* If necessary, prefix this PermMap to the main Mapping. */
if ( target_swap ) {
tmpmap = (AstMapping *) astCmpMap( permmap, *map, 1, "", status );
*map = astAnnul( *map );
*map = tmpmap;
}
/* Also, if necessary, append it to the main Mapping. */
if ( result_swap ) {
tmpmap = (AstMapping *) astCmpMap( *map, permmap, 1, "", status );
*map = astAnnul( *map );
*map = tmpmap;
}
/* Annul the pointer to the PermMap (if created). */
if ( permmap ) permmap = astAnnul( permmap );
}
}
/* If an error occurred, clean up by annulling the result pointers and
returning appropriate null values. */
if ( !astOK ) {
*map = astAnnul( *map );
*result = astAnnul( *result );
match = 0;
}
/* Return the result. */
return match;
}
/* Functions which access class attributes. */
/* ---------------------------------------- */
/* Implement member functions to access the attributes associated with
the axes of a SpecFluxFrame using the private macros defined for this
purpose at the start of this file. */
/* Copy constructor. */
/* ----------------- */
/* Destructor. */
/* ----------- */
/* Dump function. */
/* -------------- */
static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
/*
* Name:
* Dump
* Purpose:
* Dump function for SpecFluxFrame 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 SpecFluxFrame class to an output Channel.
* Parameters:
* this
* Pointer to the SpecFluxFrame 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: */
AstSpecFluxFrame *this; /* Pointer to the SpecFluxFrame structure */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the SpecFluxFrame structure. */
this = (AstSpecFluxFrame *) this_object;
/* Write out values representing the instance variables for the
SpecFluxFrame class. Accompany these with appropriate comment strings,
possibly depending on the values being written.*/
/* In the case of attributes, we first use the appropriate (private)
Test... member function to see if they are set. If so, we then use
the (private) Get... function to obtain the value to be written
out.
For attributes which are not set, we use the astGet... method to
obtain the value instead. This will supply a default value
(possibly provided by a derived class which over-rides this method)
which is more useful to a human reader as it corresponds to the
actual default attribute value. Since "set" will be zero, these
values are for information only and will not be read back. */
}
/* Standard class functions. */
/* ========================= */
/* Implement the astIsASpecFluxFrame and astCheckSpecFluxFrame functions using
the macros defined for this purpose in the "object.h" header file. */
astMAKE_ISA(SpecFluxFrame,CmpFrame)
astMAKE_CHECK(SpecFluxFrame)
AstSpecFluxFrame *astSpecFluxFrame_( void *frame1_void, void *frame2_void,
const char *options, int *status, ...) {
/*
*++
* Name:
c astSpecFluxFrame
f AST_SPECFLUXFRAME
* Purpose:
* Create a SpecFluxFrame.
* Type:
* Public function.
* Synopsis:
c #include "specfluxframe.h"
c AstSpecFluxFrame *astSpecFluxFrame( AstSpecFrame *frame1, AstFluxFrame *frame2,
c const char *options, ... )
f RESULT = AST_SPECFLUXFRAME( FRAME1, FRAME2, OPTIONS, STATUS )
* Class Membership:
* SpecFluxFrame constructor.
* Description:
* This function creates a new SpecFluxFrame and optionally initialises
* its attributes.
*
* A SpecFluxFrame combines a SpecFrame and a FluxFrame into a single
* 2-dimensional compound Frame. Such a Frame can for instance be used
* to describe a Plot of a spectrum in which the first axis represents
* spectral position and the second axis represents flux.
* Parameters:
c frame1
f FRAME1 = INTEGER (Given)
* Pointer to the SpecFrame. This will form the first axis in the
* new SpecFluxFrame.
c frame2
f FRAME2 = INTEGER (Given)
* Pointer to the FluxFrame. This will form the second axis in the
* new SpecFluxFrame. The "SpecVal" attribute of this FluxFrame is
* not used by the SpecFluxFrame class and so may be set to AST__BAD
* when the FluxFrame is created.
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 SpecFluxFrame. 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 SpecFluxFrame. 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 astSpecFluxFrame()
f AST_SPECFLUXFRAME = INTEGER
* A pointer to the new SpecFluxFrame.
* Notes:
* - The supplied Frame pointers are stored directly, rather than
* being used to create deep copies of the supplied Frames. This means
* that any subsequent changes made to the Frames via the supplied
* pointers will result in equivalent changes being visible in the
* SpecFluxFrame.
* - A null Object pointer (AST__NULL) will be returned if this
c function is invoked with the AST error status set, or if it
f function is invoked with STATUS set to an error value, or if it
* should fail for any reason.
* Status Handling:
* The protected interface to this function includes an extra
* parameter at the end of the parameter list descirbed above. This
* parameter is a pointer to the integer inherited status
* variable: "int *status".
*--
* Implementation Notes:
* - This function implements the basic SpecFluxFrame constructor which
* is available via the protected interface to the SpecFluxFrame class.
* A public interface is provided by the astSpecFluxFrameId_ function.
* - Because this function has a variable argument list, it is
* invoked by a macro that evaluates to a function pointer (not a
* function invocation) and no checking or casting of arguments is
* performed before the function is invoked. Because of this, the
* "frame1" and "frame2" parameters are of type (void *) and are
* converted and validated within the function itself.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstSpecFluxFrame *new; /* Pointer to new SpecFluxFrame */
AstFluxFrame *frame2; /* Pointer to FluxFrame structure */
AstSpecFrame *frame1; /* Pointer to SpecFrame structure */
va_list args; /* Variable argument list */
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(NULL);
/* Check the global status. */
new = NULL;
if ( !astOK ) return new;
/* Obtain and validate pointers to the Frame structures provided. */
frame1 = astCheckSpecFrame( frame1_void );
frame2 = astCheckFluxFrame( frame2_void );
if ( astOK ) {
/* Initialise the SpecFluxFrame, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitSpecFluxFrame( NULL, sizeof( AstSpecFluxFrame ), !class_init,
&class_vtab, "SpecFluxFrame", frame1, frame2 );
/* 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
SpecFluxFrame'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 SpecFluxFrame. */
return new;
}
AstSpecFluxFrame *astSpecFluxFrameId_( void *frame1_void, void *frame2_void,
const char *options, ... ) {
/*
* Name:
* astSpecFluxFrameId_
* Purpose:
* Create a SpecFluxFrame.
* Type:
* Private function.
* Synopsis:
* #include "specfluxframe.h"
* AstSpecFluxFrame *astSpecFluxFrameId_( void *frame1_void, void *frame2_void,
* const char *options, ... )
* Class Membership:
* SpecFluxFrame constructor.
* Description:
* This function implements the external (public) interface to the
* astSpecFluxFrame constructor function. It returns an ID value
* (instead of a true C pointer) to external users, and must be
* provided because astSpecFluxFrame_ has a variable argument list which
* cannot be encapsulated in a macro (where this conversion would
* otherwise occur). For the same reason, the "frame1" and "frame2"
* parameters are of type (void *) and are converted and validated
* within the function itself.
*
* The variable argument list also prevents this function from
* invoking astSpecFluxFrame_ 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 astSpecFluxFrame_.
* Returned Value:
* The ID value associated with the new SpecFluxFrame.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstSpecFluxFrame *new; /* Pointer to new SpecFluxFrame */
AstSpecFrame *frame1; /* Pointer to first Frame structure */
AstFluxFrame *frame2; /* Pointer to second Frame structure */
va_list args; /* Variable argument list */
int *status; /* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(NULL);
/* Pointer to inherited status value */
/* Get a pointer to the inherited status value. */
status = astGetStatusPtr;
/* Check the global status. */
new = NULL;
if ( !astOK ) return new;
/* Obtain the Frame pointers from the ID's supplied and validate the
pointers to ensure they identify valid Frames. */
frame1 = astVerifySpecFrame( astMakePointer( frame1_void ) );
frame2 = astVerifyFluxFrame( astMakePointer( frame2_void ) );
if ( astOK ) {
/* Initialise the SpecFluxFrame, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitSpecFluxFrame( NULL, sizeof( AstSpecFluxFrame ), !class_init,
&class_vtab, "SpecFluxFrame", frame1, frame2 );
/* 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
SpecFluxFrame'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 SpecFluxFrame. */
return astMakeId( new );
}
AstSpecFluxFrame *astInitSpecFluxFrame_( void *mem, size_t size, int init,
AstSpecFluxFrameVtab *vtab, const char *name,
AstSpecFrame *frame1, AstFluxFrame *frame2, int *status ) {
/*
*+
* Name:
* astInitSpecFluxFrame
* Purpose:
* Initialise a SpecFluxFrame.
* Type:
* Protected function.
* Synopsis:
* #include "specfluxframe.h"
* AstSpecFluxFrame *astInitSpecFluxFrame( void *mem, size_t size, int init,
* AstSpecFluxFrameVtab *vtab, const char *name,
* AstSpecFrame *frame1, AstFluxFrame *frame2 )
* Class Membership:
* SpecFluxFrame initialiser.
* Description:
* This function is provided for use by class implementations to
* initialise a new SpecFluxFrame object. It allocates memory (if
* necessary) to accommodate the SpecFluxFrame plus any additional data
* associated with the derived class. It then initialises a
* SpecFluxFrame structure at the start of this memory. If the "init"
* flag is set, it also initialises the contents of a virtual
* function table for a SpecFluxFrame at the start of the memory passed
* via the "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory in which the SpecFluxFrame is to be
* created. This must be of sufficient size to accommodate the
* SpecFluxFrame data (sizeof(SpecFluxFrame)) 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 SpecFluxFrame (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 SpecFluxFrame structure, so a valid value must be
* supplied even if not required for allocating memory.
* init
* A logical flag indicating if the SpecFluxFrame'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 SpecFluxFrame.
* 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 Object astClass function).
* frame1
* Pointer to the SpecFrame
* frame2
* Pointer to the FluxFrame
* Returned Value:
* A pointer to the new SpecFluxFrame.
* 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: */
AstSpecFluxFrame *new; /* Pointer to new SpecFluxFrame */
/* Check the global status. */
if ( !astOK ) return NULL;
/* If necessary, initialise the virtual function table. */
if ( init ) astInitSpecFluxFrameVtab( vtab, name );
/* Initialise a Frame structure (the parent class) as the first
component within the SpecFluxFrame structure, allocating memory if
necessary. Set the number of Frame axes to zero, since all axis
information is stored within the component Frames. */
new = astInitCmpFrame( mem, size, 0, (AstCmpFrameVtab *) vtab, name,
frame1, frame2 );
if ( astOK ) {
/* 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;
}
AstSpecFluxFrame *astLoadSpecFluxFrame_( void *mem, size_t size,
AstSpecFluxFrameVtab *vtab, const char *name,
AstChannel *channel, int *status ) {
/*
*+
* Name:
* astLoadSpecFluxFrame
* Purpose:
* Load a SpecFluxFrame.
* Type:
* Protected function.
* Synopsis:
* #include "specfluxframe.h"
* AstSpecFluxFrame *astLoadSpecFluxFrame( void *mem, size_t size,
* AstSpecFluxFrameVtab *vtab, const char *name,
* AstChannel *channel )
* Class Membership:
* SpecFluxFrame loader.
* Description:
* This function is provided to load a new SpecFluxFrame 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
* SpecFluxFrame structure in this memory, using data read from the
* input Channel.
* Parameters:
* mem
* A pointer to the memory into which the SpecFluxFrame is to be
* loaded. This must be of sufficient size to accommodate the
* SpecFluxFrame data (sizeof(SpecFluxFrame)) 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 SpecFluxFrame (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 SpecFluxFrame 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(AstSpecFluxFrame) is used instead.
* vtab
* Pointer to the start of the virtual function table to be
* associated with the new SpecFluxFrame. If this is NULL, a pointer
* to the (static) virtual function table for the SpecFluxFrame 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 "SpecFluxFrame" is used instead.
* Returned Value:
* A pointer to the new SpecFluxFrame.
* 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 Constants: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
#define KEY_LEN 50 /* Maximum length of a keyword */
/* Local Variables: */
AstSpecFluxFrame *new; /* Pointer to the new SpecFluxFrame */
/* Initialise. */
new = NULL;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(channel);
/* Check the global error status. */
if ( !astOK ) return new;
/* If a NULL virtual function table has been supplied, then this is
the first loader to be invoked for this SpecFluxFrame. In this case the
SpecFluxFrame belongs to this class, so supply appropriate values to be
passed to the parent class loader (and its parent, etc.). */
if ( !vtab ) {
size = sizeof( AstSpecFluxFrame );
vtab = &class_vtab;
name = "SpecFluxFrame";
/* If required, initialise the virtual function table for this class. */
if ( !class_init ) {
astInitSpecFluxFrameVtab( 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 SpecFluxFrame. */
new = astLoadCmpFrame( mem, size, (AstCmpFrameVtab *) 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, "SpecFluxFrame" );
/* Now read each individual data item from this list and use it to
initialise the appropriate instance variable(s) for this class. */
/* In the case of attributes, we first read the "raw" input value,
supplying the "unset" value as the default. If a "set" value is
obtained, we then use the appropriate (private) Set... member
function to validate and set the value properly. */
/* (none) */
/* If an error occurred, clean up by deleting the new SpecFluxFrame. */
if ( !astOK ) new = astDelete( new );
}
/* Return the new SpecFluxFrame pointer. */
return new;
/* Undefine macros local to this function. */
#undef KEY_LEN
}
/* 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. */
ast-8.0.7/fprism.c 0000664 0001750 0001750 00000006145 12610415012 010702 0000000 0000000 /*
*+
* Name:
* fprism.c
* Purpose:
* Define a FORTRAN 77 interface to the AST Prism class.
* Type of Module:
* C source file.
* Description:
* This file defines FORTRAN 77-callable C functions which provide
* a public FORTRAN 77 interface to the Prism class.
* Routines Defined:
* AST_ISAPRISM
* AST_PRISM
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* 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
* .
* Authors:
* DSB: David S. Berry (Starlink)
* History:
* 10-JAN-2004 (DSB):
* Original version.
*/
/* Define the astFORTRAN77 macro which prevents error messages from
AST C functions from reporting the file and line number where the
error occurred (since these would refer to this file, they would
not be useful). */
#define astFORTRAN77
/* Header files. */
/* ============= */
#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */
#include "c2f77.h" /* F77 <-> C support functions/macros */
#include "error.h" /* Error reporting facilities */
#include "memory.h" /* Memory handling facilities */
#include "prism.h" /* C interface to the Prism class */
F77_LOGICAL_FUNCTION(ast_isaprism)( INTEGER(THIS), INTEGER(STATUS) ) {
GENPTR_INTEGER(THIS)
F77_LOGICAL_TYPE(RESULT);
astAt( "AST_ISAPRISM", NULL, 0 );
astWatchSTATUS(
RESULT = astIsAPrism( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE;
)
return RESULT;
}
F77_INTEGER_FUNCTION(ast_prism)( INTEGER(REG1),
INTEGER(REG2),
CHARACTER(OPTIONS),
INTEGER(STATUS)
TRAIL(OPTIONS) ) {
GENPTR_INTEGER(REG1)
GENPTR_INTEGER(REG2)
GENPTR_CHARACTER(OPTIONS)
F77_INTEGER_TYPE(RESULT);
char *options;
int i;
astAt( "AST_PRISM", NULL, 0 );
astWatchSTATUS(
options = astString( OPTIONS, OPTIONS_length );
/* Truncate the options string to exlucde any trailing spaces. */
astChrTrunc( options );
/* Change ',' to '\n' (see AST_SET in fobject.c for why). */
if ( astOK ) {
for ( i = 0; options[ i ]; i++ ) {
if ( options[ i ] == ',' ) options[ i ] = '\n';
}
}
RESULT = astP2I( astPrism( astI2P( *REG1 ), astI2P( *REG2 ),
"%s", options ) );
astFree( options );
)
return RESULT;
}
ast-8.0.7/tpn.c 0000664 0001750 0001750 00000027033 12610415012 010202 0000000 0000000 #include
#include
#include "wcsmath.h"
#include "wcstrig.h"
#include "proj.h"
#define icopysign(X, Y) ((Y) < 0.0 ? -abs(X) : abs(X))
#define TPN 999
/*============================================================================
* TAN: gnomonic projection, with correction terms.
*
* This projection is no longer part of the FITSWCS standard, but is
* retained here for use b the AST library as a means of implementing
* the IRAF TNX projection, and the DSS encoding.
*
* Given and/or returned:
* prj->p Array of latitude coefficients
* prj->p2 Array of longitude coefficients
* prj->flag TPN, or -TPN if prj->flag is given < 0.
* prj->r0 r0; reset to 180/pi if 0.
* prj->n If zero, only do poly part of transformation (i.e. omit
* the TAN projection).
*
* Returned:
* prj->code "TPN"
* prj->phi0 0.0
* prj->theta0 90.0
* prj->astPRJfwd Pointer to astTPNfwd().
* prj->astPRJrev Pointer to astTPNrev().
* prj->w[ 0 ] Set to 0.0 if a simple tan projection is required
* (with no polynomial correction). Otherwise, set to 1.0.
*===========================================================================*/
int astTPNset(prj)
struct AstPrjPrm *prj;
{
int m;
prj->flag = icopysign(TPN, prj->flag);
prj->phi0 = 0.0;
prj->theta0 = 90.0;
if (prj->r0 == 0.0) prj->r0 = R2D;
prj->astPRJfwd = astTPNfwd;
prj->astPRJrev = astTPNrev;
/* If all co-efficients have their "unit" values, we do not need to
use the polynomial correction. */
prj->w[ 0 ] = 0.0;
if( prj->p[ 0 ] != 0.0 || prj->p2[ 0 ] != 0.0 ) {
prj->w[ 0 ] = 1.0;
} else if( prj->p[ 1 ] != 1.0 || prj->p2[ 1 ] != 1.0 ) {
prj->w[ 0 ] = 1.0;
} else {
for( m = 2; m < WCSLIB_MXPAR; m++ ){
if( prj->p[ m ] != 0.0 || prj->p2[ m ] != 0.0 ){
prj->w[ 0 ] = 1.0;
break;
}
}
}
return 0;
}
/*--------------------------------------------------------------------------*/
int astTPNfwd(phi, theta, prj, xx, yy)
const double phi, theta;
double *xx, *yy;
struct AstPrjPrm *prj;
{
double r, xi, eta, x2, xy, y2, r2, x3, x2y, xy2, y3, r3, x4, x3y,
x2y2, xy3, y4, x5, x4y, x3y2, x2y3, xy4, y5, r5, x6, x5y, x4y2,
x3y3, x2y4, xy5, y6, x7, x6y, x5y2, x4y3, x3y4, x2y5, xy6, y7,
r7, tol, f, g, fx, fy, gx, gy, dx, dy, x, y, denom;
double *a, *b;
int i, ok;
if (abs(prj->flag) != TPN ) {
if (astTPNset(prj)) return 1;
}
if( prj->n ) {
double s = astSind(theta);
if (prj->flag > 0 && s < 0.0) {
return 2;
}
r = prj->r0*astCosd(theta)/s;
xi = r*astSind(phi);
eta = -r*astCosd(phi);
} else {
xi = phi;
eta = theta;
}
/* Simple tan */
if( prj->w[ 0 ] == 0.0 ){
*xx = xi;
*yy = eta;
/* Tan with polynomial corrections: Iterate using Newton's method to
get the (x,y) corresponding to the above (xi,eta). */
} else {
a = prj->p2;
b = prj->p;
/* Initial guess: linear solution assuming a3,... and b3,... are zero. */
denom = a[1]*b[1] - a[2]*b[2];
if( denom != 0.0 ) {
x = ( xi*b[1] - eta*a[2] - a[0]*b[1] + b[0]*a[2] )/denom;
y = -( xi*b[2] - eta*a[1] - a[0]*b[2] + b[0]*a[1] )/denom;
} else {
if( a[1] != 0.0 ){
x = ( xi - a[0] )/a[1];
} else {
x = a[0];
}
if( b[1] != 0.0 ){
y = ( eta - b[0] )/b[1];
} else {
y = b[0];
}
}
/* Iterate up to 50 times, until the required relative accuracy is
achieved. */
tol = 1.0E-5;
ok = 0;
for (i = 0; i < 50; i++) {
/* Get required products of the current x and y values */
x2 = x*x;
xy = x*y;
y2 = y*y;
r2 = x2 + y2;
r = sqrt( r2 );
x3 = x2*x;
x2y = x2*y;
xy2 = x*y2;
y3 = y*y2;
r3 = r*r2;
x4 = x3*x;
x3y = x3*y;
x2y2 = x2*y2;
xy3 = x*y3;
y4 = y*y3;
x5 = x4*x;
x4y = x4*y;
x3y2 = x3*y2;
x2y3 = x2*y3;
xy4 = x*y4;
y5 = y*y4;
r5 = r3*r2;
x6 = x5*x;
x5y = x5*y;
x4y2 = x4*y2;
x3y3 = x3*y3;
x2y4 = x2*y4;
xy5 = x*y5;
y6 = y*y5;
x7 = x6*x;
x6y = x6*y;
x5y2 = x5*y2;
x4y3 = x4*y3;
x3y4 = x3*y4;
x2y5 = x2*y5;
xy6 = x*y6;
y7 = y*y6;
r7 = r5*r2;
/* Get the xi and eta models corresponding to the current x and y values */
f = a[0] + a[1]*x + a[2]*y + a[3]*r + a[4]*x2
+ a[5]*xy + a[6]*y2 + a[7]*x3 + a[8]*x2y + a[9]*xy2
+ a[10]*y3 + a[11]*r3 + a[12]*x4 + a[13]*x3y + a[14]*x2y2
+ a[15]*xy3 + a[16]*y4 + a[17]*x5 + a[18]*x4y + a[19]*x3y2
+ a[20]*x2y3 + a[21]*xy4 + a[22]*y5 + a[23]*r5 + a[24]*x6
+ a[25]*x5y + a[26]*x4y2 + a[27]*x3y3 + a[28]*x2y4 + a[29]*xy5
+ a[30]*y6 + a[31]*x7 + a[32]*x6y + a[33]*x5y2 + a[34]*x4y3
+ a[35]*x3y4 + a[36]*x2y5 + a[37]*xy6 + a[38]*y7 + a[39]*r7;
g = b[0] + b[1]*y + b[2]*x + b[3]*r + b[4]*y2
+ b[5]*xy + b[6]*x2 + b[7]*y3 + b[8]*xy2 + b[9]*x2y
+ b[10]*x3 + b[11]*r3 + b[12]*y4 + b[13]*xy3 + b[14]*x2y2
+ b[15]*x3y + b[16]*x4 + b[17]*y5 + b[18]*xy4 + b[19]*x2y3
+ b[20]*x3y2 + b[21]*x4y + b[22]*x5 + b[23]*r5 + b[24]*y6
+ b[25]*xy5 + b[26]*x2y4 + b[27]*x3y3 + b[28]*x4y2 + b[29]*x5y
+ b[30]*x6 + b[31]*y7 + b[32]*xy6 + b[33]*x2y5 + b[34]*x3y4
+ b[35]*x4y3 + b[36]*x5y2 + b[37]*x6y + b[38]*x7 + b[39]*r7;
/* Partial derivative of xi wrt x... */
fx = a[1] + a[3]*( (r!=0.0)?(x/r):0.0 ) + 2*a[4]*x +
a[5]*y + 3*a[7]*x2 + 2*a[8]*xy + a[9]*y2 +
3*a[11]*r*x + 4*a[12]*x3 + 3*a[13]*x2y + 2*a[14]*xy2 +
a[15]*y3 + 5*a[17]*x4 + 4*a[18]*x3y + 3*a[19]*x2y2 +
2*a[20]*xy3 + a[21]*y4 + 5*a[23]*r3*x + 6*a[24]*x5 +
5*a[25]*x4y + 4*a[26]*x3y2 + 3*a[27]*x2y3 + 2*a[28]*xy4 +
a[29]*y5 + 7*a[31]*x6 + 6*a[32]*x5y + 5*a[33]*x4y2 +
4*a[34]*x3y3 + 3*a[35]*x2y4 + 2*a[36]*xy5 + a[37]*y6 +
7*a[39]*r5*x;
/* Partial derivative of xi wrt y... */
fy = a[2] + a[3]*( (r!=0.0)?(y/r):0.0 ) + a[5]*x +
2*a[6]*y + a[8]*x2 + 2*a[9]*xy + 3*a[10]*y2 +
3*a[11]*r*y + a[13]*x3 + 2*a[14]*x2y + 3*a[15]*xy2 +
4*a[16]*y3 + a[18]*x4 + 2*a[19]*x3y + 3*a[20]*x2y2 +
4*a[21]*xy3 + 5*a[22]*y4 + 5*a[23]*r3*y + a[25]*x5 +
2*a[26]*x4y + 3*a[27]*x3y2 + 4*a[28]*x2y3 + 5*a[29]*xy4 +
6*a[30]*y5 + a[32]*x6 + 2*a[33]*x5y + 3*a[34]*x4y2 +
4*a[35]*x3y3 + 5*a[36]*x2y4 + 6*a[37]*xy5 + 7*a[38]*y6 +
7*a[39]*r5*y;
/* Partial derivative of eta wrt x... */
gx = b[2] + b[3]*( (r!=0.0)?(x/r):0.0 ) + b[5]*y +
2*b[6]*x + b[8]*y2 + 2*b[9]*xy + 3*b[10]*x2 +
3*b[11]*r*x + b[13]*y3 + 2*b[14]*xy2 + 3*b[15]*x2y +
4*b[16]*x3 + b[18]*y4 + 2*b[19]*xy3 + 3*b[20]*x2y2 +
4*b[21]*x3y + 5*b[22]*x4 + 5*b[23]*r3*x + b[25]*y5 +
2*b[26]*xy4 + 3*b[27]*x2y3 + 4*b[28]*x3y2 + 5*b[29]*x4y +
6*b[30]*x5 + b[32]*y6 + 2*b[33]*xy5 + 3*b[34]*x2y4 +
4*b[35]*x3y3 + 5*b[36]*x4y2 + 6*b[37]*x5y + 7*b[38]*x6 +
7*b[39]*r5*x;
/* Partial derivative of eta wrt y... */
gy = b[1] + b[3]*( (r!=0.0)?(y/r):0.0 ) + 2*b[4]*y +
b[5]*x + 3*b[7]*y2 + 2*b[8]*xy + b[9]*x2 +
3*b[11]*r*y + 4*b[12]*y3 + 3*b[13]*xy2 + 2*b[14]*x2y +
b[15]*x3 + 5*b[17]*y4 + 4*b[18]*xy3 + 3*b[19]*x2y2 +
2*b[20]*x3y + b[21]*x4 + 5*b[23]*r3*y + 6*b[24]*y5 +
5*b[25]*xy4 + 4*b[26]*x2y3 + 3*b[27]*x3y2 + 2*b[28]*x4y +
b[29]*x5 + 7*b[31]*y6 + 6*b[32]*xy5 + 5*b[33]*x2y4 +
4*b[34]*x3y3 + 3*b[35]*x4y2 + 2*b[36]*x5y + b[37]*x6 +
7*b[39]*r5*y;
/* Calculate new x and y values. */
f = f - xi;
g = g - eta;
dx = ( (-f*gy) + (g*fy) ) / ( (fx*gy) - (fy*gx) );
dy = ( (-g*fx) + (f*gx) ) / ( (fx*gy) - (fy*gx) );
x += dx;
y += dy;
/* Check if convergence has been achieved. */
if( fabs(dx) <= tol*fabs(x) && fabs(dy) <= tol*fabs(y) ) {
ok = 1;
break;
}
}
*xx = x;
*yy = y;
if( !ok ) return 2;
}
return 0;
}
/*--------------------------------------------------------------------------*/
int astTPNrev(x, y, prj, phi, theta)
const double x, y;
double *phi, *theta;
struct AstPrjPrm *prj;
{
double r, xi, eta, x2, xy, y2, r2, x3, x2y, xy2, y3, r3, x4, x3y,
x2y2, xy3, y4, x5, x4y, x3y2, x2y3, xy4, y5, r5, x6, x5y, x4y2,
x3y3, x2y4, xy5, y6, x7, x6y, x5y2, x4y3, x3y4, x2y5, xy6, y7,
r7;
double *a, *b;
if (abs(prj->flag) != TPN ) {
if (astTPNset(prj)) return 1;
}
/* Simple tan */
if( prj->w[ 0 ] == 0.0 ){
xi = x;
eta = y;
/* Tan with polynomial corrections. */
} else {
x2 = x*x;
xy = x*y;
y2 = y*y;
r2 = x2 + y2;
r = sqrt( r2 );
x3 = x2*x;
x2y = x2*y;
xy2 = x*y2;
y3 = y*y2;
r3 = r*r2;
x4 = x3*x;
x3y = x3*y;
x2y2 = x2*y2;
xy3 = x*y3;
y4 = y*y3;
x5 = x4*x;
x4y = x4*y;
x3y2 = x3*y2;
x2y3 = x2*y3;
xy4 = x*y4;
y5 = y*y4;
r5 = r3*r2;
x6 = x5*x;
x5y = x5*y;
x4y2 = x4*y2;
x3y3 = x3*y3;
x2y4 = x2*y4;
xy5 = x*y5;
y6 = y*y5;
x7 = x6*x;
x6y = x6*y;
x5y2 = x5*y2;
x4y3 = x4*y3;
x3y4 = x3*y4;
x2y5 = x2*y5;
xy6 = x*y6;
y7 = y*y6;
r7 = r5*r2;
a = prj->p2;
xi = a[0] + a[1]*x + a[2]*y + a[3]*r + a[4]*x2
+ a[5]*xy + a[6]*y2 + a[7]*x3 + a[8]*x2y + a[9]*xy2
+ a[10]*y3 + a[11]*r3 + a[12]*x4 + a[13]*x3y + a[14]*x2y2
+ a[15]*xy3 + a[16]*y4 + a[17]*x5 + a[18]*x4y + a[19]*x3y2
+ a[20]*x2y3 + a[21]*xy4 + a[22]*y5 + a[23]*r5 + a[24]*x6
+ a[25]*x5y + a[26]*x4y2 + a[27]*x3y3 + a[28]*x2y4 + a[29]*xy5
+ a[30]*y6 + a[31]*x7 + a[32]*x6y + a[33]*x5y2 + a[34]*x4y3
+ a[35]*x3y4 + a[36]*x2y5 + a[37]*xy6 + a[38]*y7 + a[39]*r7;
b = prj->p;
eta = b[0] + b[1]*y + b[2]*x + b[3]*r + b[4]*y2
+ b[5]*xy + b[6]*x2 + b[7]*y3 + b[8]*xy2 + b[9]*x2y
+ b[10]*x3 + b[11]*r3 + b[12]*y4 + b[13]*xy3 + b[14]*x2y2
+ b[15]*x3y + b[16]*x4 + b[17]*y5 + b[18]*xy4 + b[19]*x2y3
+ b[20]*x3y2 + b[21]*x4y + b[22]*x5 + b[23]*r5 + b[24]*y6
+ b[25]*xy5 + b[26]*x2y4 + b[27]*x3y3 + b[28]*x4y2 + b[29]*x5y
+ b[30]*x6 + b[31]*y7 + b[32]*xy6 + b[33]*x2y5 + b[34]*x3y4
+ b[35]*x4y3 + b[36]*x5y2 + b[37]*x6y + b[38]*x7 + b[39]*r7;
}
/* Now do the tan projection */
if( prj->n ) {
r = sqrt(xi*xi + eta*eta);
if (r == 0.0) {
*phi = 0.0;
} else {
*phi = astATan2d(xi, -eta);
}
*theta = astATan2d(prj->r0, r);
} else {
*phi = xi;
*theta = eta;
}
return 0;
}
ast-8.0.7/loader.c 0000664 0001750 0001750 00000010447 12610415012 010650 0000000 0000000 #define astCLASS
#include "axis.h"
#include "box.h"
#include "channel.h"
#include "circle.h"
#include "cmpframe.h"
#include "cmpmap.h"
#include "cmpregion.h"
#include "dsbspecframe.h"
#include "dssmap.h"
#include "ellipse.h"
#include "fitschan.h"
#include "fluxframe.h"
#include "timeframe.h"
#include "timemap.h"
#include "frame.h"
#include "frameset.h"
#include "grismmap.h"
#include "interval.h"
#include "intramap.h"
#include "keymap.h"
#include "loader.h"
#include "lutmap.h"
#include "mapping.h"
#include "mathmap.h"
#include "matrixmap.h"
#include "nullregion.h"
#include "object.h"
#include "pcdmap.h"
#include "permmap.h"
#include "plot.h"
#include "plot3d.h"
#include "pointlist.h"
#include "pointset.h"
#include "polygon.h"
#include "polymap.h"
#include "prism.h"
#include "normmap.h"
#include "ratemap.h"
#include "region.h"
#include "shiftmap.h"
#include "skyaxis.h"
#include "skyframe.h"
#include "slamap.h"
#include "specfluxframe.h"
#include "specframe.h"
#include "specmap.h"
#include "sphmap.h"
#include "tranmap.h"
#include "selectormap.h"
#include "switchmap.h"
#include "unitmap.h"
#include "wcsmap.h"
#include "winmap.h"
#include "xmlchan.h"
#include "zoommap.h"
#include "stc.h"
#include "stcresourceprofile.h"
#include "stcsearchlocation.h"
#include "stccatalogentrylocation.h"
#include "stcobsdatalocation.h"
#include "stcschan.h"
#include "table.h"
#include "fitstable.h"
#include "error.h"
#include "ast_err.h"
#include
#include
/*
*+
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* 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
* .
* Authors:
* RFWS: R.F. Warren-Smith (Starlink)
* DSB: David S. Berry (Starlink)
* History:
* 18-NOV-1997 (RFWS):
* Original version.
* 18-MAR-1998 (RFWS):
* Added the IntraMap class.
* 3-JUN-1999 (RFWS):
* Added the PcdMap class.
* 17-AUG-1999 (RFWS):
* Added the MathMap class.
* 8-JAN-2003 (DSB):
* Added the SpecMap and SpecFrame classes.
* 15-JUL-2003 (DSB):
* Added the GrsimMap class.
* 6-FEB-2009 (DSB):
* Added the StcsChan class.
*-
*/
AstLoaderType *astGetLoader( const char *class, int *status ) {
if ( !astOK ) return NULL;
#define LOAD(name) \
if ( !strcmp( class, #name ) ) return (AstLoaderType *) astLoad##name##_
LOAD(Axis);
LOAD(Box);
LOAD(Channel);
LOAD(Circle);
LOAD(CmpFrame);
LOAD(CmpMap);
LOAD(CmpRegion);
LOAD(DSBSpecFrame);
LOAD(DssMap);
LOAD(Ellipse);
LOAD(FitsChan);
LOAD(FitsTable);
LOAD(FluxFrame);
LOAD(Frame);
LOAD(FrameSet);
LOAD(GrismMap);
LOAD(Interval);
LOAD(IntraMap);
LOAD(KeyMap);
LOAD(LutMap);
LOAD(Mapping);
LOAD(MathMap);
LOAD(MatrixMap);
LOAD(NullRegion);
LOAD(Object);
LOAD(PcdMap);
LOAD(PermMap);
LOAD(Plot);
LOAD(Plot3D);
LOAD(PointList);
LOAD(PointSet);
LOAD(PolyMap);
LOAD(Polygon);
LOAD(Prism);
LOAD(NormMap);
LOAD(RateMap);
LOAD(Region);
LOAD(ShiftMap);
LOAD(SkyAxis);
LOAD(SkyFrame);
LOAD(SlaMap);
LOAD(SpecFluxFrame);
LOAD(SpecFrame);
LOAD(SpecMap);
LOAD(SphMap);
LOAD(SelectorMap);
LOAD(SwitchMap);
LOAD(Table);
LOAD(TimeFrame);
LOAD(TimeMap);
LOAD(TranMap);
LOAD(UnitMap);
LOAD(WcsMap);
LOAD(WinMap);
LOAD(XmlChan);
LOAD(ZoomMap);
LOAD(StcsChan);
LOAD(Stc);
LOAD(StcResourceProfile);
LOAD(StcSearchLocation);
LOAD(StcCatalogEntryLocation);
LOAD(StcObsDataLocation);
astError( AST__OCLUK, "astGetLoader: Object of unknown class \"%s\" cannot "
"be loaded.", status, class );
return NULL;
#undef LOAD
}
ast-8.0.7/stcsearchlocation.c 0000664 0001750 0001750 00000073007 12610415012 013113 0000000 0000000 /*
*class++
* Name:
* StcSearchLocation
* Purpose:
* Correspond to the IVOA SearchLocation class.
* Constructor Function:
c astStcSearchLocation
f AST_STCSEARCHLOCATION
* Description:
* The StcSearchLocation class is a sub-class of Stc used to describe
* the coverage of a query.
*
* See http://hea-www.harvard.edu/~arots/nvometa/STC.html
* Inheritance:
* The StcSearchLocation class inherits from the Stc class.
* Attributes:
* The StcSearchLocation class does not define any new attributes beyond
* those which are applicable to all Stcs.
* Functions:
c The StcSearchLocation class does not define any new functions beyond those
f The StcSearchLocation class does not define any new routines beyond those
* which are applicable to all Stcs.
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* 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
* .
* Authors:
* DSB: David S. Berry (Starlink)
* History:
* 26-NOV-2004 (DSB):
* Original version.
*class--
*/
/* Module Macros. */
/* ============== */
/* Set the name of the class we are implementing. This indicates to
the header files that define class interfaces that they should make
"protected" symbols available. */
#define astCLASS StcSearchLocation
/* 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 "stc.h" /* Coordinate stcs (parent class) */
#include "channel.h" /* I/O channels */
#include "region.h" /* Regions within coordinate systems */
#include "stcsearchlocation.h" /* Interface definition for this class */
/* Error code definitions. */
/* ----------------------- */
#include "ast_err.h" /* AST error codes */
/* C header files. */
/* --------------- */
#include
#include
#include
#include
/* Module Variables. */
/* ================= */
/* Address of this static variable is used as a unique identifier for
member of this class. */
static int class_check;
#ifdef THREAD_SAFE
/* Define how to initialise thread-specific globals. */
#define GLOBAL_inits \
globals->Class_Init = 0;
/* Create the function that initialises global data for this module. */
astMAKE_INITGLOBALS(StcSearchLocation)
/* Define macros for accessing each item of thread specific global data. */
#define class_init astGLOBAL(StcSearchLocation,Class_Init)
#define class_vtab astGLOBAL(StcSearchLocation,Class_Vtab)
#include
#else
/* Define the class virtual function table and its initialisation flag
as static variables. */
static AstStcSearchLocationVtab 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. */
AstStcSearchLocation *astStcSearchLocationId_( void *, int, AstKeyMap **, const char *, ... );
/* Prototypes for Private Member Functions. */
/* ======================================== */
static void Dump( AstObject *, AstChannel *, int * );
/* Member functions. */
/* ================= */
void astInitStcSearchLocationVtab_( AstStcSearchLocationVtab *vtab, const char *name, int *status ) {
/*
*+
* Name:
* astInitStcSearchLocationVtab
* Purpose:
* Initialise a virtual function table for a StcSearchLocation.
* Type:
* Protected function.
* Synopsis:
* #include "stcsearchlocation.h"
* void astInitStcSearchLocationVtab( AstStcSearchLocationVtab *vtab, const char *name )
* Class Membership:
* StcSearchLocation vtab initialiser.
* Description:
* This function initialises the component of a virtual function
* table which is used by the StcSearchLocation 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 */
AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */
AstStcVtab *stc; /* Pointer to Stc 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. */
astInitStcVtab( (AstStcVtab *) vtab, name );
/* Store a unique "magic" value in the virtual function table. This
will be used (by astIsAStcSearchLocation) 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 = &(((AstStcVtab *) vtab)->id);
/* Initialise member function pointers. */
/* ------------------------------------ */
/* Store pointers to the member functions (implemented here) that provide
virtual methods for this class. */
/* Save the inherited pointers to methods that will be extended, and
replace them with pointers to the new member functions. */
mapping = (AstMappingVtab *) vtab;
stc = (AstStcVtab *) vtab;
/* Store replacement pointers for methods which will be over-ridden by
new member functions implemented here. */
/* Declare the copy constructor, destructor and class dump
functions. */
astSetDump( vtab, Dump, "StcSearchLocation", "Query coverage" );
/* 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) );
}
}
/* 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). */
/* Copy constructor. */
/* ----------------- */
/* None */
/* Destructor. */
/* ----------- */
/* None */
/* Dump function. */
/* -------------- */
static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
/*
* Name:
* Dump
* Purpose:
* Dump function for StcSearchLocation 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 StcSearchLocation class to an output Channel.
* Parameters:
* this
* Pointer to the StcSearchLocation 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: */
AstStcSearchLocation *this; /* Pointer to the StcSearchLocation structure */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the StcSearchLocation structure. */
this = (AstStcSearchLocation *) this_object;
/* Write out values representing the instance variables for the
StcSearchLocation class. Accompany these with appropriate comment strings,
possibly depending on the values being written.*/
/* In the case of attributes, we first use the appropriate (private)
Test... member function to see if they are set. If so, we then use
the (private) Get... function to obtain the value to be written
out.
For attributes which are not set, we use the astGet... method to
obtain the value instead. This will supply a default value
(possibly provided by a derived class which over-rides this method)
which is more useful to a human reader as it corresponds to the
actual default attribute value. Since "set" will be zero, these
values are for information only and will not be read back. */
/* There are no values to write, so return without further action. */
}
/* Standard class functions. */
/* ========================= */
/* Implement the astIsAStcSearchLocation and astCheckStcSearchLocation functions using the macros
defined for this purpose in the "object.h" header file. */
astMAKE_ISA(StcSearchLocation,Stc)
astMAKE_CHECK(StcSearchLocation)
AstStcSearchLocation *astStcSearchLocation_( void *region_void, int ncoords,
AstKeyMap **coords, const char *options, int *status, ...) {
/*
*++
* Name:
c astStcSearchLocation
f AST_STCSEARCHLOCATION
* Purpose:
* Create a StcSearchLocation.
* Type:
* Public function.
* Synopsis:
c #include "stcsearchlocation.h"
c AstStcResourceProfile *astStcSearchLocation( AstRegion *region,
c int ncoords, AstKeyMap *coords[], const char *options, ... )
f RESULT = AST_STCSEARCHLOCATION( REGION, NCOORDS, COORDS, OPTIONS, STATUS )
* Class Membership:
* StcSearchLocation constructor.
* Description:
* This function creates a new StcSearchLocation and optionally initialises its
* attributes.
*
* The StcSearchLocation class is a sub-class of Stc used to describe
* the coverage of a VO query.
*
* See http://hea-www.harvard.edu/~arots/nvometa/STC.html
* Parameters:
c region
f REGION = INTEGER (Given)
* Pointer to the encapsulated Region.
c ncoords
f NCOORDS = INTEGER (Given)
c The length of the "coords" array. Supply zero if "coords" is NULL.
f The length of the COORDS array. Supply zero if COORDS should be
f ignored.
c coords
f COORDS( NCOORDS ) = INTEGER (Given)
c Pointer to an array holding "ncoords" AstKeyMap pointers (if "ncoords"
f An array holding NCOORDS AstKeyMap pointers (if NCOORDS
* is zero, the supplied value is ignored). Each supplied KeyMap
* describes the contents of a single STC element, and
* should have elements with keys given by constants AST__STCNAME,
* AST__STCVALUE, AST__STCERROR, AST__STCRES, AST__STCSIZE,
* AST__STCPIXSZ. Any of these elements may be omitted, but no other
* elements should be included. If supplied, the AST__STCNAME element
* should be a vector of character string pointers holding the "Name"
* item for each axis in the coordinate system represented by
c "region".
f REGION.
* Any other supplied elements should be scalar elements, each holding
* a pointer to a Region describing the associated item of ancillary
* information (error, resolution, size, pixel size or value). These
* Regions should describe a volume within the coordinate system
c represented by "region".
f represented by REGION.
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 StcSearchLocation. 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 StcSearchLocation. 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 astStcSearchLocation()
f AST_STCSEARCHLOCATION = INTEGER
* A pointer to the new StcSearchLocation.
* Notes:
* - A deep copy is taken of the supplied Region. This means that
* any subsequent changes made to the encapsulated Region using the
* supplied pointer will have no effect on the Stc.
* - A null Object pointer (AST__NULL) will be returned if this
c function is invoked with the AST error status set, or if it
f function is invoked with STATUS set to an error value, or if it
* should fail for any reason.
* Status Handling:
* The protected interface to this function includes an extra
* parameter at the end of the parameter list descirbed above. This
* parameter is a pointer to the integer inherited status
* variable: "int *status".
*--
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstRegion *region; /* Pointer to Region structure */
AstStcSearchLocation *new; /* Pointer to new StcSearchLocation */
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;
/* Obtain and validate a pointer to the Region structure provided. */
region = astCheckRegion( region_void );
/* Initialise the StcSearchLocation, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitStcSearchLocation( NULL, sizeof( AstStcSearchLocation ), !class_init,
&class_vtab, "StcSearchLocation", region,
ncoords, coords );
/* 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 StcSearchLocation'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 StcSearchLocation. */
return new;
}
AstStcSearchLocation *astStcSearchLocationId_( void *region_void, int ncoords,
AstKeyMap **coords, const char *options, ... ) {
/*
* Name:
* astStcSearchLocationId_
* Purpose:
* Create a StcSearchLocation.
* Type:
* Private function.
* Synopsis:
* #include "stcsearchlocation.h"
* AstStcSearchLocation *astStcSearchLocationId_( AstRegion *region,
* int ncoords, AstKeyMap *coords[], const char *options, ... )
* Class Membership:
* StcSearchLocation constructor.
* Description:
* This function implements the external (public) interface to the
* astStcSearchLocation constructor function. It returns an ID value (instead
* of a true C pointer) to external users, and must be provided
* because astStcSearchLocation_ 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 astStcSearchLocation_ 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 astStcSearchLocation_.
* Returned Value:
* The ID value associated with the new StcSearchLocation.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstKeyMap **keymaps; /* Pointer to array of KeyMap pointers */
AstRegion *region; /* Pointer to Region structure */
AstStcSearchLocation *new; /* Pointer to new StcSearchLocation */
int icoord; /* Keymap index */
va_list args; /* Variable argument list */
int *status; /* Pointer to inherited status value */
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(NULL);
/* Get a pointer to the inherited status value. */
status = astGetStatusPtr;
/* Check the global status. */
if ( !astOK ) return NULL;
/* Obtain a Region pointer from the supplied ID and validate the
pointer to ensure it identifies a valid Region. */
region = astVerifyRegion( astMakePointer( region_void ) );
/* Obtain pointer from the supplied KeyMap ID's and validate the
pointers to ensure it identifies a valid KeyMap. */
keymaps = astMalloc( sizeof( AstKeyMap * )*(size_t) ncoords );
if( keymaps ) {
for( icoord = 0; icoord < ncoords; icoord++ ) {
keymaps[ icoord ] = astVerifyKeyMap( astMakePointer( coords[ icoord ] ) );
}
}
/* Initialise the StcSearchLocation, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitStcSearchLocation( NULL, sizeof( AstStcSearchLocation ), !class_init,
&class_vtab, "StcSearchLocation", region,
ncoords, keymaps );
/* Free resources. */
keymaps = astFree( keymaps );
/* 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 StcSearchLocation'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 StcSearchLocation. */
return astMakeId( new );
}
AstStcSearchLocation *astInitStcSearchLocation_( void *mem, size_t size,
int init, AstStcSearchLocationVtab *vtab,
const char *name, AstRegion *region,
int ncoords, AstKeyMap **coords, int *status ) {
/*
*+
* Name:
* astInitStcSearchLocation
* Purpose:
* Initialise a StcSearchLocation.
* Type:
* Protected function.
* Synopsis:
* #include "stcsearchlocation.h"
* AstStcSearchLocation *astInitStcSearchLocation_( void *mem, size_t size,
* int init, AstStcSearchLocationVtab *vtab,
* const char *name, AstRegion *region,
* int ncoords, AstKeyMap **coords )
* Class Membership:
* StcSearchLocation initialiser.
* Description:
* This function is provided for use by class implementations to initialise
* a new StcSearchLocation object. It allocates memory (if necessary) to accommodate
* the StcSearchLocation plus any additional data associated with the derived class.
* It then initialises a StcSearchLocation structure at the start of this memory. If
* the "init" flag is set, it also initialises the contents of a virtual
* function table for a StcSearchLocation at the start of the memory passed via the
* "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory in which the StcSearchLocation is to be initialised.
* This must be of sufficient size to accommodate the StcSearchLocation data
* (sizeof(StcSearchLocation)) 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 StcSearchLocation (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 StcSearchLocation
* structure, so a valid value must be supplied even if not required for
* allocating memory.
* init
* A logical flag indicating if the StcSearchLocation'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 StcSearchLocation.
* 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).
* region
* A pointer to the Region encapsulated by the StcSearchLocation.
* ncoords
* Number of KeyMap pointers supplied in "coords". Can be zero.
* Ignored if "coords" is NULL.
* coords
* Pointer to an array of "ncoords" KeyMap pointers, or NULL if
* "ncoords" is zero. Each KeyMap defines defines a single
* element, and should have elements with keys given by constants
* AST__STCNAME, AST__STCVALUE, AST__STCERROR, AST__STCRES, AST__STCSIZE,
* AST__STCPIXSZ. These elements hold values for the corresponding
* components of the STC AstroCoords element. Any of these elements may
* be omitted, but no other elements should be included. All supplied
* elements should be vector elements, with vector length less than or
* equal to the number of axes in the supplied Region. The data type of
* all elements should be "double", except for AST__STCNAME which should
* be "character string". If no value is available for a given axis, then
* AST__BAD (or NULL for the AST__STCNAME element) should be stored in
* the vector at the index corresponding to the axis (trailing axes
* can be omitted completely from the KeyMap).
* Returned Value:
* A pointer to the new StcSearchLocation.
* 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: */
AstStcSearchLocation *new; /* Pointer to new StcSearchLocation */
/* Check the global status. */
if ( !astOK ) return NULL;
/* If necessary, initialise the virtual function table. */
if ( init ) astInitStcSearchLocationVtab( vtab, name );
/* Initialise a Stc structure (the parent class) as the first component
within the StcSearchLocation structure, allocating memory if necessary. */
new = (AstStcSearchLocation *) astInitStc( mem, size, 0, (AstStcVtab *) vtab,
name, region, ncoords, coords );
/* If an error occurred, clean up by deleting the new StcSearchLocation. */
if ( !astOK ) new = astDelete( new );
/* Return a pointer to the new StcSearchLocation. */
return new;
}
AstStcSearchLocation *astLoadStcSearchLocation_( void *mem, size_t size, AstStcSearchLocationVtab *vtab,
const char *name, AstChannel *channel, int *status ) {
/*
*+
* Name:
* astLoadStcSearchLocation
* Purpose:
* Load a StcSearchLocation.
* Type:
* Protected function.
* Synopsis:
* #include "stcsearchlocation.h"
* AstStcSearchLocation *astLoadStcSearchLocation( void *mem, size_t size, AstStcSearchLocationVtab *vtab,
* const char *name, AstChannel *channel )
* Class Membership:
* StcSearchLocation loader.
* Description:
* This function is provided to load a new StcSearchLocation 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
* StcSearchLocation 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 a StcSearchLocation at the start of the memory
* passed via the "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory into which the StcSearchLocation is to be
* loaded. This must be of sufficient size to accommodate the
* StcSearchLocation data (sizeof(StcSearchLocation)) 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 StcSearchLocation (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 StcSearchLocation 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(AstStcSearchLocation) is used instead.
* vtab
* Pointer to the start of the virtual function table to be
* associated with the new StcSearchLocation. If this is NULL, a pointer
* to the (static) virtual function table for the StcSearchLocation 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 "StcSearchLocation" is used instead.
* Returned Value:
* A pointer to the new StcSearchLocation.
* 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 */
AstStcSearchLocation *new; /* Pointer to the new StcSearchLocation */
/* 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 StcSearchLocation. In this case the
StcSearchLocation belongs to this class, so supply appropriate values to be
passed to the parent class loader (and its parent, etc.). */
if ( !vtab ) {
size = sizeof( AstStcSearchLocation );
vtab = &class_vtab;
name = "StcSearchLocation";
/* If required, initialise the virtual function table for this class. */
if ( !class_init ) {
astInitStcSearchLocationVtab( 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 StcSearchLocation. */
new = astLoadStc( mem, size, (AstStcVtab *) 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, "StcSearchLocation" );
/* Now read each individual data item from this list and use it to
initialise the appropriate instance variable(s) for this class. */
/* In the case of attributes, we first read the "raw" input value,
supplying the "unset" value as the default. If a "set" value is
obtained, we then use the appropriate (private) Set... member
function to validate and set the value properly. */
/* There are no values to read. */
/* ---------------------------- */
/* If an error occurred, clean up by deleting the new StcSearchLocation. */
if ( !astOK ) new = astDelete( new );
}
/* Return the new StcSearchLocation 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. */
ast-8.0.7/fitschan.c 0000664 0001750 0001750 00006071155 12610415012 011212 0000000 0000000 /*
*class++
* Name:
* FitsChan
* Purpose:
* I/O Channel using FITS header cards to represent Objects.
* Constructor Function:
c astFitsChan
f AST_FITSCHAN
* Description:
* A FitsChan is a specialised form of Channel which supports I/O
* operations involving the use of FITS (Flexible Image Transport
* System) header cards. Writing an Object to a FitsChan (using
c astWrite) will, if the Object is suitable, generate a
f AST_WRITE) will, if the Object is suitable, generate a
* description of that Object composed of FITS header cards, and
* reading from a FitsChan will create a new Object from its FITS
* header card description.
*
* While a FitsChan is active, it represents a buffer which may
* contain zero or more 80-character "header cards" conforming to
* FITS conventions. Any sequence of FITS-conforming header cards
* may be stored, apart from the "END" card whose existence is
* merely implied. The cards may be accessed in any order by using
* the FitsChan's integer Card attribute, which identifies a "current"
* card, to which subsequent operations apply. Searches
c based on keyword may be performed (using astFindFits), new
c cards may be inserted (astPutFits, astPutCards, astSetFits) and
c existing ones may be deleted (astDelFits), extracted (astGetFits),
c or changed (astSetFits).
f based on keyword may be performed (using AST_FINDFITS), new
f cards may be inserted (AST_PUTFITS, AST_PUTCARDS, AST_SETFITS) and
f existing ones may be deleted (AST_DELFITS), extracted
f (AST_GETFITS) or changed (AST_SETFITS).
*
* When you create a FitsChan, you have the option of specifying
* "source" and "sink" functions which connect it to external data
* stores by reading and writing FITS header cards. If you provide
* a source function, it is used to fill the FitsChan with header cards
* when it is accessed for the first time. If you do not provide a
* source function, the FitsChan remains empty until you explicitly enter
c data into it (e.g. using astPutFits, astPutCards, astWrite
f data into it (e.g. using AST_PUTFITS, AST_PUTCARDS, AST_WRITE
* or by using the SourceFile attribute to specifying a text file from
* which headers should be read). When the FitsChan is deleted, any
* remaining header cards in the FitsChan can be saved in either of
* two ways: 1) by specifying a value for the SinkFile attribute (the
* name of a text file to which header cards should be written), or 2)
* by providing a sink function (used to to deliver header cards to an
* external data store). If you do not provide a sink function or a
* value for SinkFile, any header cards remaining when the FitsChan
* is deleted will be lost, so you should arrange to extract them
* first if necessary
c (e.g. using astFindFits or astRead).
f (e.g. using AST_FINDFITS or AST_READ).
*
* Coordinate system information may be described using FITS header
* cards using several different conventions, termed
* "encodings". When an AST Object is written to (or read from) a
* FitsChan, the value of the FitsChan's Encoding attribute
* determines how the Object is converted to (or from) a
* description involving FITS header cards. In general, different
* encodings will result in different sets of header cards to
* describe the same Object. Examples of encodings include the DSS
* encoding (based on conventions used by the STScI Digitised Sky
* Survey data), the FITS-WCS encoding (based on a proposed FITS
* standard) and the NATIVE encoding (a near loss-less way of
* storing AST Objects in FITS headers).
*
* The available encodings differ in the range of Objects they can
* represent, in the number of Object descriptions that can coexist
* in the same FitsChan, and in their accessibility to other
* (external) astronomy applications (see the Encoding attribute
* for details). Encodings are not necessarily mutually exclusive
* and it may sometimes be possible to describe the same Object in
* several ways within a particular set of FITS header cards by
* using several different encodings.
*
c The detailed behaviour of astRead and astWrite, when used with
f The detailed behaviour of AST_READ and AST_WRITE, when used with
* a FitsChan, depends on the encoding in use. In general, however,
c all successful use of astRead is destructive, so that FITS header cards
f all successful use of AST_READ is destructive, so that FITS header cards
* are consumed in the process of reading an Object, and are
* removed from the FitsChan (this deletion can be prevented for
* specific cards by calling the
c astRetainFits function).
f AST_RETAINFITS routine).
* An unsuccessful call of
c astRead
f AST_READ
* (for instance, caused by the FitsChan not containing the necessary
* FITS headers cards needed to create an Object) results in the
* contents of the FitsChan being left unchanged.
*
* If the encoding in use allows only a single Object description
* to be stored in a FitsChan (e.g. the DSS, FITS-WCS and FITS-IRAF
c encodings), then write operations using astWrite will
f encodings), then write operations using AST_WRITE will
* over-write any existing Object description using that
* encoding. Otherwise (e.g. the NATIVE encoding), multiple Object
* descriptions are written sequentially and may later be read
* back in the same sequence.
* Inheritance:
* The FitsChan class inherits from the Channel class.
* Attributes:
* In addition to those attributes common to all Channels, every
* FitsChan also has the following attributes:
*
* - AllWarnings: A list of the available conditions
* - Card: Index of current FITS card in a FitsChan
* - CardComm: The comment of the current FITS card in a FitsChan
* - CardName: The keyword name of the current FITS card in a FitsChan
* - CardType: The data type of the current FITS card in a FitsChan
* - CarLin: Ignore spherical rotations on CAR projections?
* - CDMatrix: Use a CD matrix instead of a PC matrix?
* - Clean: Remove cards used whilst reading even if an error occurs?
* - DefB1950: Use FK4 B1950 as default equatorial coordinates?
* - Encoding: System for encoding Objects as FITS headers
* - FitsAxisOrder: Sets the order of WCS axes within new FITS-WCS headers
* - FitsDigits: Digits of precision for floating-point FITS values
* - Iwc: Add a Frame describing Intermediate World Coords?
* - Ncard: Number of FITS header cards in a FitsChan
* - Nkey: Number of unique keywords in a FitsChan
* - TabOK: Should the FITS "-TAB" algorithm be recognised?
* - PolyTan: Use PVi_m keywords to define distorted TAN projection?
* - Warnings: Produces warnings about selected conditions
* Functions:
c In addition to those functions applicable to all Channels, the
c following functions may also be applied to all FitsChans:
f In addition to those routines applicable to all Channels, the
f following routines may also be applied to all FitsChans:
*
c - astDelFits: Delete the current FITS card in a FitsChan
c - astEmptyFits: Delete all cards in a FitsChan
c - astFindFits: Find a FITS card in a FitsChan by keyword
c - astGetFits: Get a keyword value from a FitsChan
c - astGetTables: Retrieve any FitsTables from a FitsChan
c - astPurgeWCS: Delete all WCS-related cards in a FitsChan
c - astPutCards: Stores a set of FITS header card in a FitsChan
c - astPutFits: Store a FITS header card in a FitsChan
c - astPutTable: Store a single FitsTable in a FitsChan
c - astPutTables: Store multiple FitsTables in a FitsChan
c - astReadFits: Read cards in through the source function
c - astRemoveTables: Remove one or more FitsTables from a FitsChan
c - astRetainFits: Ensure current card is retained in a FitsChan
c - astSetFits: Store a new keyword value in a FitsChan
c - astShowFits: Display the contents of a FitsChan on standard output
c - astTableSource: Register a source function for FITS table access
c - astTestFits: Test if a keyword has a defined value in a FitsChan
c - astWriteFits: Write all cards out to the sink function
f - AST_DELFITS: Delete the current FITS card in a FitsChan
f - AST_EMPTYFITS: Delete all cards in a FitsChan
f - AST_FINDFITS: Find a FITS card in a FitsChan by keyword
f - AST_GETFITS: Get a keyword value from a FitsChan
f - AST_GETTABLES: Retrieve any FitsTables from a FitsChan
f - AST_PURGEWCS: Delete all WCS-related cards in a FitsChan
f - AST_PUTCARDS: Stores a set of FITS header card in a FitsChan
f - AST_PUTFITS: Store a FITS header card in a FitsChan
f - AST_PUTTABLE: Store a single FitsTables in a FitsChan
f - AST_PUTTABLES: Store multiple FitsTables in a FitsChan
f - AST_READFITS: Read cards in through the source function
f - AST_REMOVETABLES: Remove one or more FitsTables from a FitsChan
f - AST_RETAINFITS: Ensure current card is retained in a FitsChan
f - AST_SETFITS: Store a new keyword value in a FitsChan
c - AST_SHOWFITS: Display the contents of a FitsChan on standard output
f - AST_TABLESOURCE: Register a source function for FITS table access
f - AST_TESTFITS: Test if a keyword has a defined value in a FitsChan
f - AST_WRITEFITS: Write all cards out to the sink function
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* Copyright (C) 2008-2011 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
* .
* Authors:
* DSB: David Berry (Starlink)
* RFWS: R.F. Warren-Smith (Starlink, RAL)
* TIMJ: Tim Jenness (JAC, Hawaii)
* History:
* 11-DEC-1996 (DSB):
* Original version.
* 20-MAR-1997 (DSB):
* Made keyword setting and getting functions protected instead of
* public. Renamed public methods. Added Ncard attribute.
* 20-MAY-1997 (RFWS):
* Tidied public prologues.
* 30-JUN-1997 (DSB):
* Added support for reading post-2000 DATE-OBS strings. Reading DSS
* or FITS-WCS objects now returns NULL unless the FitsChan is
* positioned at the start-of-file prior to the read. Bug fixed
* which caused Ncard to be returned too large by one. Removed
* dependancy on hard-wired header and footer text in Native
* FitsChans.
* 18-AUG-1997 (DSB):
* Bug fixed in WcsNative which caused incorrect CRVAL values
* to be used if the axes needed permuting. Values assigned to the
* Projection attribute fo the SkyFrames created by astRead.
* 2-SEP-1997 (DSB):
* Added the IRAF convention that EPOCH=0.0 really means EPOCH=1950.0
* (the EPOCH keyword is deprecated in the new FITS-WCS conventions
* and is taken always as a Besselian epoch).
* 19-SEP-1997 (DSB):
* Corrected interpretation of the FITS CD matrix.
* 25-SEP-1997 (DSB):
* o Fix bug in LinearMap which caused it always to detect a linear
* mapping. For instance, this allowed DssMaps to be erroneously
* written out using FITS-WCS encoding with a CAR projection.
* o Assign a full textual description to SkyFrame's Projection
* attribute instead of a 3 letter acronym.
* o If DATE-OBS >= 1999.0 then DATE-OBS is now written in new
* Y2000 format. For DATE-OBS < 1999.0, the old format is written.
* o Add new attribute CDMatrix to determine whether PC or CD
* matrices should be used when writing objects using FITS-WCS
* encoding.
* o Modified the way floating point values are formatted to omit
* unnecessary leading zeros from the exponent (i.e. E-5 instead of
* E-05).
* o New-line characters at the end of supplied header cards are now
* ignored.
* o Cater for EQUINOX specified as a string prefixed by B or J
* rather than as a floating point value (some HST data does this).
* o Corrected SetValue so that it always inserts comment cards
* rather than over-write existing comment cards. Previously,
* writing a FrameSet to a DSS encoded FitsChan resulted in all
* comments cards being stripped except for the last one.
* o Reading a FrameSet from a DSS-encoded FrameSet now only
* removes the keywords actually required to construct the FrameSet.
* Previously, all keywords were removed.
* o The EPOCH and EQUINOX keywords created when a FrameSet is
* written to a DSS-encoded FitsChan are now determined from the
* epoch and equinox of the current Frame, instead of from a copy
* of the original FitsChan stored within the DssMap.
* o The Encoding and CDMatrix attributes, and keyword types are
* now stored as strings externally instead of integers.
* 11-NOV-1997 (DSB):
* o Assume default of j2000 for DSS EQUINOX value.
* o Check for null object pointers in the interfaces for
* virtual functions which execute even if an error has previously
* occurred. Otherwise, a segmentation violation can occur when
* trying to find the member function pointer.
* o Trailing spaces ignored in Encoding attribute.
* o Bugs fixed in FindWcs and SetValue which resulted in WCS cards
* being written at the wrong place if the supplied FitsChan does not
* contain any WCS keywords.
* o Default for CDMatrix (if no axis rotation keywords can be found)
* changed to 2 (i.e. use "CDi_j" form keywords).
* o Write now leaves the current card unchanged if nothing is
* written to the FitsChan.
* 17-NOV-1997 (RFWS):
* Disabled use of CDmatrix. Fixed initialisation problems in
* astLoadFitsChan.
* 24-NOV-1997 (DSB):
* Replace references to error code AST__OPT with AST__RDERR.
* 28-NOV-1997 (DSB):
* o Function WcsValues modified to prevent it from changing the
* current card. Previously, this could cause new cards to be
* written to the wrong place in a FITS-WCS encoded FitsChan.
* o Description of argument "value" corrected in prologue of
* function SetFits.
* o Argument "lastkey" removed from function SetValue since it
* was never used (it was a relic from a previous method of
* determining where to store new cards). Corresponding changes
* have been made to all the functions which create "lastkey" values
* or pass them on to SetValue (i.e DescWcs, WcsPrimary, WcsSecondary,
* WriteWcs and WriteDss).
* 10-DEC-1997 (DSB):
* Bug fixed which caused the initial character designating the system
* within CTYPE value (eg E in ELON, G in GLON, etc) to be omitted.
* 1-JUN-1998 (DSB):
* CDELT values of zero are now replaced by a small non-zero value
* when creating the "pixel-to-relative physical" transformation
* matrix. Previously, zero CDELT values could cause the matrix to
* be non-invertable.
* 4-SEP-1998 (DSB):
* - Indicate that SphMaps created by this class when using FITS-WCS
* encoding all operate on the unit sphere. This aids simplification.
* - Fix a bug in StoreFits which caused CD matrices to be indexed
* incorrectly (sometimes causing floating exceptions) if they do not
* describe a celestial longitude/latitude system.
* - Changed astFindFits to ignore trailing spaces in the keyword
* template.
* - astSplit changed so that an error is not reported if a textual
* keyword value ends before column 20.
* 7-OCT-1998 (DSB):
* - Corrected test for linearity in LinearMap to include a factor
* of the test vector length. Also LinearMap now uses a simplified
* Mapping.
* 5-NOV-1998 (DSB):
* Added FITS-IRAF encoding.
* 9-NOV-1998 (DSB):
* - Corrected values of macros DSS_ENCODING and MAX_ENCODING.
* - Corrected erroneous success indication in IrafStore.
* - Included checks for bad values in function LinearMap.
* 17-NOV-1998 (DSB):
* The Domain name GRID is now given to the Base Frame in any FrameSets
* created by astRead when using FitsChans with DSS, FITS-WCS or
* FITS-IRAF encodings.
* 18-DEC-1998 (DSB):
* Check for "D" exponents in floating point keyword strings.
* 12-FEB-1998 (DSB):
* Modified EncodeFloat to avoid exceeding the 20 character FITS
* limit wherever possible if FitsDigits is positive.
* 10-MAY-1998 (DSB):
* Bug fixed in astSplit which caused comments associated with string
* keywords to be lost when storing the card in a FitsChan.
* 15-JUN-1999 (DSB):
* Report an error if an unrecognised projection name is supplied.
* 9-DEC-1999 (DSB):
* - Fixed bug in WcsNatPole which could result in longitude values
* being out by 180 degrees for cylindrical projections such as CAR.
* - Only report an "unrecognised projection" error for CTYPE values
* which look like celestial longitude or latitude axes (i.e. if the
* first 4 characters are "RA--", "DEC-", "xLON" or "xLAT", and the
* fifth character is "-").
* - Added function SpecTrans to translated keywords related to the
* IRAF ZPX projection into keyword for the standard ZPN projection.
* - Add ICRS as a valid value for the RADECSYS keyword. Since the
* SkyFrame class does not yet support ICRS, an FK5 SkyFrame is
* created if RADECSYS=ICRS.
* 16-DEC-1999 (DSB):
* - Modified SpecTrans so that all keywords used to created a
* standard WCS representation from a non-standard one are consumed
* by the astRead operation.
* - Changed the text of ASTWARN cards added to the FitsChan if an
* IRAF ZPX projection is found to require unsupported corrections.
* - Simplified the documentation describing the handling of the IRAF
* ZPX projection.
* - Fixed code which assumed that the 10 FITS-WCS projection
* parameters were PROJP1 -> PROJP10. In fact they are PROJP0 -
* PROJP9. This could cause projection parameter values to be
* incorrectly numbered when they are written out upon deletion of
* the FitsChan.
* 1-FEB-2000 (DSB):
* Check that FITS_IRAF encoding is not being used before using a
* PC matrix when reading WCS information from a header. This is
* important if the header contains both PC and CD matrices.
* 8-FEB-2000 (DSB):
* - Header cards are now only consumed by an astRead operation if the
* operation succeeds (i.e. returns a non-null Object).
* - The original FITS-WCS encoding has been renamed as FITS-PC (to
* indicate the use of a PCiiijjj matrix), and a new FITS-WCS
* encoding has been added.
* - The disabled CDMatrix attribute has been removed.
* - Bug in LinearMap corrected which prevented genuinely linear
* Mappings from being judged to be linear. This bug was previously
* fudged (so it now appears) by the introduction of the test vector
* length factor (see History entry for 7-OCT-1998). This test
* vector length scale factor has consequently now been removed.
* - Added FITS-AIPS encoding.
* - The critical keywords used to select default encoding have been
* changed.
* - Support for common flavours of IRAF TNX projections added.
* - The algorithm used to find a WcsMap in the supplied FrameSet
* has been improved so that compound Mappings which contain complex
* mixtures of parallel and serial Mappings can be translated into
* FITS-WCS encoding.
* - Trailing white space in string keyword values is now retained
* when using foreign encodings to enable correct concatenation where
* a string has been split over several keywords. E.g. if 2 string
* keywords contain a list of formatted numerical values (e.g. IRAF
* WAT... keywords), and the 1st one ends "0.123 " and the next one
* begins "1234.5 ", the trailing space at the end of the first keyword
* is needed to prevent the two numbers being merged into "0.1231234.5".
* Trailing spaces in native encodings is still protected by enclosing
* the whole string in double quotes.
* - The Channel methods WriteString and GetNextData can now save
* and restore strings of arbitary length. This is done by storing
* as much of the string as possible in the usual way, and then
* storing any remaining characters in subsequent CONTINUE cards,
* using the FITSIO conventions. This storage and retrieval of long
* strings is only available for native encodings.
* 19-MAY-2000 (DSB):
* Added attribute Warnings. Lowered DSS in the priority list
* of encodings implemented by GetEncoding.
* 6-OCT-2000 (DSB):
* Increased size of buffers used to store CTYPE values to take
* account of the possiblity of lots of trailing spaces.
* 5-DEC-2000 (DSB):
* Add support for the WCSNAME FITS keyword.
* 12-DEC-2000 (DSB):
* Add a title to each physical, non-celestial coord Frame based on
* its Domain name (if any).
* 3-APR-2001 (DSB):
* - Use an "unknown" celestial coordinate system, instead of a
* Cartesian coordinate system, if the CTYPE keywords specify an
* unknown celestial coordinate system.
* - Do not report an error if there are no CTYPE keywords in the
* header (assume a unit mapping, like in La Palma FITS files).
* - Add a NoCTYPE warning condition.
* - Added AllWarnings attribute.
* - Ensure multiple copies of identical warnings are not produced.
* - Use the Object Ident attribute to store the identifier letter
* associated with each Frame read from a secondary axis description,
* so that they can be given the same letter when they are written
* out to a new FITS file.
* 10-AUG-2001 (DSB):
* - Corrected function value returned by SkySys to be 1 unless an
* error occurs. This error resulted in CAR headers being produced
* by astWrite with CRVAL and CD values till in radians rather than
* degrees.
* - Introduced SplitMap2 in order to guard against producing
* celestial FITS headers for a Mapping which includes more than
* one WcsMap.
* 13-AUG-2001 (DSB):
* - Modified FixNew so that it retains the current card index if possible.
* This fixed a bug which could cause headers written out using Native
* encodings to be non-contiguous.
* - Corrected ComBlock to correctly remove AST comment blocks in
* native encoded fitschans.
* 14-AUG-2001 (DSB):
* - Modified FixUsed so that it it does not set the current card
* back to the start of file if the last card in the FitsChan is
* deleted.
* 16-AUG-2001 (DSB):
* Modified WcsNative to limit reference point latitude to range
* +/-90 degs (previously values outside this range were wrapped
* round onto the opposite meridian). Also added new warning
* condition "badlat".
* 23-AUG-2001 (DSB):
* - Re-write LinearMap to use a least squares fit.
* - Check that CDj_i is not AST__BAD within WcsWithWcs when
* forming the increments along each physical axis.
* 28-SEP-2001 (DSB):
* GoodWarns changed so that no error is reported if a blank list
* of conditions is supplied.
* 12-OCT-2001 (DSB):
* - Added DefB1950 attribute.
* - Corrected equations which calculate CROTA when writing
* FITS-AIPS encodings.
* - Corrected equations which turn a CROTA value into a CD matrix.
* 29-NOV-2001 (DSB):
* Corrected use of "_" and "-" characters when referring to FK4-NO-E
* system in function SkySys.
* 20-FEB-2002 (DSB)
* Added CarLin attribute.
* 8-MAY-2002 (DSB):
* Correct DSSToStore to ignore trailing blanks in the PLTDECSN
* keyword value.
* 9-MAY-2002 (DSB):
* Correct GetCard to avoid infinite loop if the current card has
* been marked as deleted.
* 25-SEP-2002 (DSB):
* AIPSFromStore: use larger of coscro and sincro when determining
* CDELT values. Previously a non-zero coscro was always used, even
* if it was a s small as 1.0E-17.
* 3-OCT-2002 (DSB):
* - SkySys: Corrected calculation of longitude axis index for unknown
* celestial systems.
* - SpecTrans: Corrected check for latcor terms for ZPX projections.
* - WcsFrame: Only store an explicit equinox value in a skyframe if
* it needs one (i.e. if the system is ecliptic or equatorial).
* - WcsWithWcs: For Zenithal projections, always use the default
* LONPOLE value, and absorb any excess rotation caused by this
* into the CD matrix.
* - WcsWithWcs: Improve the check that the native->celestial mapping
* is a pure rotation, allowing for rotations which change the
* handed-ness of the system (if possible).
* - WcsWithWcs: Avoid using LONPOLE keywords when creating headers
* for a zenithal projection. Instead, add the corresponding rotation
* into the CD matrix.
* 22-OCT-2002 (DSB):
* - Retain leading and trailing white space within COMMENT cards.
* - Only use CTYPE comments as axis labels if all non-celestial
* axes have a unique non-blank comment (otherwise use CTYPE
* values as labels).
* - Updated to use latest FITS-WCS projections. This means that the
* "TAN with projection terms" is no longer a standard FITS
* projection. It is now represented using the AST-specific TPN
* projection (until such time as FITS-WCS paper IV is finished).
* - Remove trailing "Z" from DATE-OBS values created by astWrite.
* 14-NOV-2002 (DSB):
* - WcsWithWcs: Corrected to ignore longitude axis returned by
* astPrimaryFrame since it does not take into account any axis
* permutation.
* 26-NOV-2002 (DSB):
* - SpecTrans: Corrected no. of characters copied from CTYPE to PRJ,
* (from 5 to 4), and terminate PRJ correctly.
* 8-JAN-2003 (DSB):
* Changed private InitVtab method to protected astInitFitsChanVtab
* method.
* 22-JAN-2003 (DSB):
* Restructured the functions used for reading FITS_WCS headers to
* make the distinction between the generic parts (pixel->intermediate
* world coordinates) and the specialised parts (e.g. celestial,
* spectral, etc) clearer.
* 31-JAN-2003 (DSB)
* - Added Clean attribute.
* - Corrected initialisation and defaulting of CarLin and DefB1950
* attributes.
* - Extensive changes to allow foreign encodings to be produced in
* cases where the Base Frame has fewer axes than the Current Frame.
* 12-FEB-2003 (DSB)
* - Modified SetFits so that the existing card comment is retained
* if the new data value equals the existing data value.
* 30-APR-2003 (DSB):
* - Revert to standard "TAN" code for distorted tan projections,
* rather than using the "TPN" code. Also recognise QVi_m (produced
* by AUTOASTROM) as an alternative to PVi_m when reading distorted
* TAN headers.
* 22-MAY-2003 (DSB):
* Modified GetEncoding so that the presence of RADECSYS and/or
* PROJPm is only considered significant if the modern equivalent
* keyword (REDESYS or PVi_m) is *NOT* present.
* 2-JUN-2003 (DSB):
* - Added support for PCi_j kewwords within FITS-WCS encoding
* - Added CDMatrix attribute
* - Changed internal FitsStore usage to use PC/CDELT instead of CD
* (as preparation for FITS-WCS paper IV).
* - Added warning "BadMat".
* 11-JUN-2003 (DSB):
* - Modified WcsNative to use the new SphMap PolarLong attribute
* in order to ensure correct propagation of the longitude CRVAL
* value in cases where the fiducial point is coincident with a pole.
* - Use PVi_3 and PVi_4 for longitude axis "i" (if present) in
* preference to LONPOLE and LATPOLE when reading a FITS-WCS header.
* Note, these projection values are never written out (LONPOLE and
* LATPOLE are written instead).
* - Associate "RADESYS=ICRS" with SkyFrame( "System=ICRS" ), rather
* than SkyFrame( "System=FK5" ).
* - If DefB1950 is zero, use ICRS instead of FK5 as the default RADESYS
* if no EQUINOX is present.
* 1-SEP-2003 (DSB):
* - Modify Dump so that it dumps all cards including those flagged as
* having been read.
* - Added "reset" parameter to FixUsed.
* - WcsMapFrm: store an Ident of ' ' for the primary coordinate
* description (previously Ident was left unset)
* - Default value for DefB1950 attribute now depends on the value
* of the Encoding attribute.
* 15-SEP-2003 (DSB):
* - Added Warnings "BadVal", "Distortion".
* - Ignore FITS-WCS paper IV CTYPE distortion codes (except for
* "-SIP" which is interpreted correctly on reading).
* 22-OCT-2003 (DSB):
* - GetEncoding: If the header contains CDi_j but does not contain
* any of the old IRAF keywords (RADECSYS, etc) then assume FITS-WCS
* encoding. This allows a FITS-WCS header to have both CDi_j *and*
* CROTA keywords.
* 5-JAN-2004 (DSB):
* - SpecTrans: Use 1.0 (instead of the CDELT value) as the
* diagonal PCi_j term for non-celestial axes with associated CROTA
* values.
* 12-JAN-2004 (DSB):
* - CelestialAxes: Initialise "tmap1" pointer to NULL in case of error
* (avoids a segvio happening in the case of an error).
* - AddVersion: Do not attempt to add a Frame into the FITS header
* if the mapping from grid to frame is not invertable.
* - WorldAxes: Initialise the returned "perm" values to safe values,
* and return these values if no basis vectors cen be created.
* 19-JAN-2004 (DSB):
* - When reading a FITS-WCS header, allow all keywords to be defaulted
* as decribed in paper I.
* 27-JAN-2004 (DSB):
* - Modify FitLine to use correlation between actual and estimated
* axis value as the test for linearity.
* - Modify RoundFString to avoid writing beyond the end of the
* supplied buffer if the supplied string contains a long list of 9's.
* 11-MAR-2004 (DSB):
* - Modified SpecTrans to check all axis descriptions for keywords
* to be translated.
* 19-MAR-2004 (DSB):
* - Added astPutCards to support new fits_hdr2str function in
* CFITSIO.
* 25-MAR-2004 (DSB):
* - Corrected bug in astSplit which causes legal cards to be
* rejected because characters beyond the 80 char limit are being
* considered significant.
* - Corrected bug in SpecTrans which caused QV keywords to be
* ignored.
* 15-APR-2004 (DSB):
* - SpecTrans modified to include translation of old "-WAV", "-FRQ"
* and "-VEL" spectral algorithm codes to modern "-X2P" form.
* - WcsFromStore modified to supress creation of WCSAXES keywords
* for un-used axis versions.
* - IsMapLinear modified to improve fit by doing a second least
* squares fit to the residualleft by the first least squares fit.
* 16-APR-2004 (DSB):
* - NonLinSpecWcs: Issue a warning if an illegal non-linear
* spectral code is encountered.
* - Add a BadCTYPE warning condition.
* - Corrected default value for Clean so that it is zero (as
* documented).
* 21-APR-2004 (DSB):
* - FindWcs: Corrected to use correct OBSGEO template. This bug
* caused OBSGEO keywords to be misplaced in written headers.
* 23-APR-2004 (DSB):
* - SplitMap: Modified so that a Mapping which has celestial axes
* with constant values (such as produced by a PermMap) are treated
* as a valid sky coordinate Mapping.
* - AddFrame modified so that WCS Frames with a different number
* of axes ot the pixel Frame can be added into the FrameSet.
* - IRAFFromStore and AIPSFromStore modified so that they do not
* create any output keywords if the number of WCS axes is different
* to the number of pixel axes.
* - Handling of OBSGEO-X/Y/Z corrected again.
* - WCSFromStore modified to avouid writing partial axis descriptions.
* 26-APR-2004 (DSB):
* - Corrected text of output SPECSYS keyword values.
* 17-MAY-2004 (DSB):
* - Added IWC attribute.
* 15-JUN-2004 (DSB):
* - Ensure out-of-bounds longitude CRPIX values for CAR
* projections are wrapped back into bounds.
* 21-JUN-2004 (DSB):
* - Ensure primary MJD-OBS value is used when reading foreign FITS
* headers.
* 7-JUL-2004 (DSB):
* - Issue errors if an un-invertable PC/CD matrix is supplied in a
* FITS-WCS Header.
* 11-JUL-2004 (DSB):
* - Re-factor code for checking spectral axis CTYPE values into
* new function IsSpectral.
* - Modify AIPSFromSTore to create spectral axis keywords if
* possible.
* - Modify SpecTrans to recognize AIPS spectral axis keywords, and
* to convert "HZ" to "Hz".
* - Added FITS-AIPS++ encoding.
* 12-AUG-2004 (DSB):
* - Convert GLS projection codes to equivalent SFL in SpecTrans.
* - Added FITS-CLASS encoding.
* 16-AUG-2004 (DSB):
* - Removed support for paper III keyword VSOURCE, and added
* support for SSYSSRC keyword.
* - Added initial support for CLASS encoding.
* - In FitOK: Changed tolerance for detecting constant values
* from 1.0E-10 to 1.0E-8.
* 17-AUG-2004 (DSB):
* Correct GetFiducialNSC so that the stored values for longitude
* parameters 1 and 2 are ignored unless the value of parameter 0 is
* not zero.
* 19-AUG-2004 (DSB):
* Modify SpecTrans to ignore any CDELT values if the header
* includes some CDi_j values.
* 26-AUG-2004 (DSB):
* Modify astSplit_ to allow floating point keyword values which
* include an exponent to be specified with no decimal point
* (e.g. "2E-4").
* 27-AUG-2004 (DSB):
* Completed initial attempt at a FITS-CLASS encoding.
* 9-SEP-2004 (DSB):
* Fixed usage of uninitialised values within ReadCrval.
* 13-SEP-2004 (DSB):
* Check the "text" pointer can be used safely before using it in
* DSSToStore.
* 27-SEP-2004 (DSB):
* In SpecTrans, before creating new PCi_j values, check that no
* PCi_j values have been created via an earlier translation.
* 28-SEP-2004 (DSB):
* In AIPSPPFromStore only get projection parameters values if there
* are some celestialaxes. Also allow CROTA to describe rotation of
* non-celestial axes (same for AIPSFromSTore).
* 4-OCT-2004 (DSB):
* Correct rounding of CRPIX in AddVersion to avoid integer overflow.
* 11-NOV-2004 (DSB):
* - WcsFcRead: Avoid issuing warnings about bad keywords which
* have already been translated into equivalent good forms.
* - SpecTrans: If both PROJP and PV keywords are present, use PV
* in favour of PROJP only if the PV values look correct.
* 17-NOV-2004 (DSB):
* - Make astSetFits public.
* 16-MAR-2005 (DSB):
* - Primary OBSGEO-X/Y/Z, MJD-AVG and MJDOBS keywords are associated
* with all axis descriptions and should not have a trailing single
* character indicating an alternate axis set.
* 9-AUG-2005 (DSB):
* In WcsMapFrm, check reffrm is used before annulling it.
* 8-SEP-2005 (DSB):
* - Change "if( a < b < c )" constructs to "if( a < b && b < c )"
* - DSBSetup: correct test on FrameSet pointer state
* - Ensure CLASS keywords written to a FitsChan do not come before
* the final fixed position keyword.
* 9-SEP-2005 (DSB):
* - Added "AZ--" and "EL--" as allowed axis types in FITS-WCS
* ctype values.
* 12-SEP-2005 (DSB):
* - Cast difference between two pointers to (int)
* - CLASSFromStore:Check source velocity is defined before
* storing it in the output header.
* 13-SEP-2005 (DSB):
* - Corrected B1940 to B1950 in AddEncodingFrame. This bug
* prevented some FrameSets being written out using FITS-CLASS.
* - Rationalise the use of the "mapping" pointer in AddVersion.
* - WcsCelestial: Modified so that the FITS reference point is
* stored as the SkyFrame SkyRef attribute value.
* 7-OCT-2005 (DSB):
* Make astGetFits public.
* 30-NOV-2005 (DSB):
* Add support for undefined FITS keyword values.
* 5-DEC-2005 (DSB):
* - Include an IMAGFREQ keyword in the output when writing a
* DSBSpecFrame out using FITS-WCS encoding.
* - Correct test for constant values in FitOK.
* 7-DEC-2005 (DSB):
* Free memory allocated by calls to astReadString.
* 30-JAN-2006 (DSB):
* Modify astSplit so that it does no read the supplied card beyond
* column 80.
* 14-FEB-2006 (DSB):
* Override astGetObjSize.
* 28-FEB-2006 (DSB):
* Correct documentation typo ("NCards" -> "Ncard").
* 5-APR-2006 (DSB):
* Modify SpecTrans to convert CTYPE="LAMBDA" to CTYPE="WAVE".
* 26-MAY-2006 (DSB):
* Guard against NULL comment pointer when converting RESTFREQ to
* RESTFRQ in SpecTrans.
* 29-JUN-2006 (DSB):
* - Added astRetainFits.
* - Consume VELOSYS FITS-WCS keywords when reading an object.
* - Write out VELOSYS FITS-WCS keywords when writing an object.
* 7-AUG-2006 (DSB):
* Remove trailing spaces from the string returned by astGetFitsS
* if the original string contains 8 or fewer characters.
* 16-AUG-2006 (DSB):
* Document non-destructive nature of unsuccessful astRead calls.
* 17-AUG-2006 (DSB):
* Fix bugs so that the value of the Clean attribute is honoured
* even if an error has occurred.
* 4-SEP-2006 (DSB):
* Modify GetClean so that it ignores the inherited status.
* 20-SEP-2006 (DSB):
* Fix memory leak in WcsSpectral.
* 6-OCT-2006 (DSB):
* Modify IsSpectral and IsAIPSSpectral to allow for CTYPE values that
* are shorter than eight characters.
* 13-OCT-2006 (DSB):
* - Ensure SpecFrames and SkyFrames created from a foreign FITS header
* are consistent in their choice of Epoch.
* - Convert MJD-OBS and MJD-AVG values from TIMESYS timescale to
* TDB before using as the Epoch value in an AstFrame. Use UTC if
* TIMESYS is absent.
* - Convert Epoch values from TDB to UTC before storing as the
* value of an MJD-OBS or MJD-AVG keyword (no TIMESYS keyword is
* written).
* 23-OCT-2006 (DSB):
* Prefer MJD-AVG over MJD-OBS.
* 30-OCT-2006 (DSB):
* In FitOK: Changed lower limit on acceptbale correlation from
* 0.999999 to 0.99999.
* 1-NOV-2006 (DSB):
* - When reading a foreign header that contains a DUT1 keyword,
* use it to set the Dut1 attribute in the SkyFrame. Note, JACH
* store DUT1 in units of days. This may clash with the FITS-WCS
* standard (when its produced). Also note that DUT1 is not written
* out as yet when writing a FrameSet to a foreign FITS header.
* - Correct bug that prevented ZSOURCE keyword being added to the
* output header if the source velocity was negative.
* 9-NOV-2006 (DSB):
* Add STATUS argument to docs for F77 AST_SETx.
* 20-DEC-2006 (DSB):
* Correct FK5 to ICRS in error message issued if no RADESYS or
* EQUINOX is found.
* 16-JAN-2007 (DSB):
* Cast ignored function return values to (void) to avoid compiler
* warnings.
* 31-JAN-2007 (DSB):
* Change SpecTrans to ignore blank unit strings (previously
* converted them to "Hz").
* 16-APR-2007 (DSB):
* In SplitMat, increase the allowed level of rounding erros from
* 1.0E-10 to 1.0E-7 (to avoid spurious low CDi_j values being
* created that should be zero).
* 30-APR-2007 (DSB):
* - Change DSBSetup so that the central DSBSpecFrame frequency is
* CRVAL and the IF is the difference between CRVAL and LO.
* - Change tolerance in FitOK from 0.99999 to 0.995 to handle data from Nicolas
* Peretto.
* 1-MAY-2007 (DSB):
* - In astSplit, if a keyword value looks like an int but is too long to
* fit in an int, then treat it as a float instead.
* 18-MAY-2007 (DSB):
* In CnvType, use input type rather than output type when checking
* for a COMMENT card. Also, return a null data value buffer for a
* COMMENT card.
* 4-JUN-2007 (DSB):
* In CLASSFromStore, create a DELTAV header even if it is equal to
* the spectral CDELT value. Also, convert spatial reference point
* to (az,el) and write out as headers AZIMUTH and ELEVATIO.
* 9-JUL-2007 (DSB):
* Fixed bug in DSBSetUp - previously, this function assumed that
* the supplied DSBSpecFrame represented frequency, and so gave
* incorrect values for IF and DSBCentre if the header described
* velocity.
* 9-AUG-2007 (DSB):
* Changed GetEncoding so that critcal keywords are ignored if
* there are no CTYPE, CRPIX or CRVAL keywords in the header.
* 10-AUG-2007 (DSB):
* - Changed GetEncoding so that FITS_PC is not returned if there are
* any CDi_j or PCi_j keywords in the header.
* - Added astPurgeWCS method.
* 13-AUG-2007 (DSB):
* - Include the DSS keywords AMDX%d and AMDY%d in FindWCS.
* 16-AUG-2007 (DSB):
* - Force all FITS-CLASS headers to contain frequency axes
* (velocity axes seem not to be recognised properly by CLASS).
* - Change the CLASS "VELO-LSR" header to be the velocity at the
* reference channel, not the source velocity.
* 22-AUG-2007 (DSB):
* - Remove debugging printf statements.
* 20-SEP-2007 (DSB):
* Changed FitOK to check that the RMS residual is not more than
* a fixed small fraction of the pixel size.
* 4-DEC-2007 (DSB):
* Changed CreateKeyword so that it uses a KeyMap to search for
* existing keywords. This is much faster than checking every
* FitsCard in the FitsChan explicitly.
* 18-DEC-2007 (DSB):
* Add keyword VLSR to the CLASS encoding. It holds the same value
* as VELO-LSR, but different versions of class use different names.
* Also write out the DELTAV keyword in the LSR rest frame rather
* than the source rest frame.
* 31-JAN-2008 (DSB):
* Correct calculation of redshift from radio velocity in ClassTrans.
* 25-FEB-2008 (DSB):
* Ensure a SkyFrame represents absolute (rather than offset)
* coords before writing it out in any non-native encoding.
* 28-FEB-2008 (DSB):
* Test for existing of SkyRefIs attribute before accessing it.
* 2-APR-2008 (DSB):
* In CLASSFromStore, adjust the spatial CRVAL and CRPIX values to be
* the centre of the first pixel if the spatial axes are degenerate.
* 17-APR-2008 (DSB):
* Ignore latitude axis PV terms supplied in a TAN header
* (previously, such PV terms were used as polynomial correction
* terms in a TPN projection).
* 30-APR-2008 (DSB):
* SetValue changed so that new keywords are inserted before the
* current card.
* 1-MAY-2008 (DSB):
* Added UndefRead warning.
* 7-MAY-2008 (DSB):
* Correct conversion of CDi_j to PCi_j/CDELT in SpecTrans.
* 8-MAY-2008 (DSB):
* When writing out a FITS-WCS header, allow linear grid->WCS
* mapping to be represented by a CAR projection.
* 9-MAY-2008 (DSB):
* Make class variables IgnoreUsed and MarkNew static.
* 30-JUN-2008 (DSB):
* Improve efficiency of FindWcs.
* 2-JUL-2008 (DSB):
* FitsSof now returns non-zero if the FitsChan is empty.
* 16-JUL-2008 (DSB):
* Plug memory leak caused by failure to free the Warnings
* attribute string when a FitsChan is deleted.
* 24-JUL-2008 (TIMJ):
* Fix buffer overrun in astGetFits when writing the keyword
* to the buffer (occurred if the input string was 80 characters).
* 1-OCT-2008 (DSB):
* When reading a FITS-WCS header, spurious PVi_j keywords no
* longer generate an error. Instead they generate warnings via the
* new "BadPV" warning type.
* 21-NOV-2008 (DSB):
* Do not remove keywords from read headers if they may be of
* relevance to things other than WCS (e.g. MJD-OBS, OBSGEO, etc).
* 2-DEC-2008 (DSB):
* - astGetFits now reports an error if the keyword value is undefined.
* - Add new functions astTestFits and astSetFitsU.
* - Remove use of AST__UNDEF constants.
* - Remove "undefread" warning.
* 16-JAN-2009 (DSB):
* Use astAddWarning to store each warning in the parent Channel
* structure.
* 4-MAR-2009 (DSB):
* DATE-OBS and MJD-OBS cannot have an axis description character.
* 13-MAR-2009 (DSB):
* The VELOSYS value read from the header is never used, so do not
* report an error if VELOSYS has an undefined value.
* 11-JUN-2009 (DSB):
* Delay reading cards from the source until they are actually
* needed. Previously, the source function was called in the
* FitsChan initialiser, but this means it is not possible for
* application code to call astPutChannelData before the source
* function is called. The ReadFromSource function is now called
* at the start of each (nearly) public or protected function to
* ensure the source function has been called (the source function
* pointer in the FitsChan is then nullified to ensure it is not
* called again).
* 18-JUN-2009 (DSB):
* Include the effect of observer height (in the ObsAlt attribute)
* when creating OBSGEO-X/Y/Z headers, and store a value for
* ObsAlt when reading a set of OBSGEO-X/Y/Z headers.
* 2-JUL-2009 (DSB):
* Check FitsChan is not empty at start of FindWcs.
* 7-JUL-2009 (DSB):
* Add new function astSetFitsCM.
* 30-JUL-2009 (DSB):
* Fix axis numbering in SkyPole.
* 12-FEB-2010 (DSB):
* Use "" to represent AST__BAD externally.
* 25-JUN-2010 (DSB):
* Fix problem rounding lots of 9's in RoundFString. The problem
* only affected negative values, and could lead to an extra zero
* being included in the integer part.
* 28-JUN-2010 (DSB):
* Another problem in RoundFString! If the value has a series of
* 9's followed by a series of zeros, with no decimal point (e.g.
* "260579999000"), then the trailing zeros were being lost.
* 16-JUL-2010 (DSB):
* In SpecTrans, avoid over-writing the spatial projection code
* with the spectral projection code.
* 20-JUL-2010 (DSB):
* Correct interpretation of NCP projection code.
* 14-OCT-2010 (DSB):
* - Correct loading of FitsChans that contain UNDEF keywords.
* - Correct translation of spectral units with non-standard
* capitalisation in SpecTrans.
* 10-JAN-2011 (DSB):
* Fix memory leak in MakeIntWorld.
* 13-JAN-2011 (DSB):
* Rename astEmpty ast astEmptyFits and make public.
* 20-JAN-2011 (DSB):
* - Extensive changes to support -TAB algorithm
* - Recovery from a major unrequested reformatting of whitespace by
* my editor!
* 7-FEB-2011 (DSB):
* Put a space between keyword value and slash that starts a comment
* when formatting a FITS header card.
* 11-FEB-2011 (DSB):
* Change meaning of TabOK attribute. It is no longer a simple
* boolean indicating if the -TAB algorithm is supported. Instead
* it gives the value to be used for the EXTVER header - i.e. the
* version number to store with any binary table created as a
* result of calling astWrite. If TabOK is zero or begative, then
* the -TAB algorithm is not supported. This is so that there is
* some way of having multiple binary table extensions with the same
* name (but different EXTVER values).
* 14-FEB-2011 (DSB):
* - Spectral reference point CRVAL records the obs. centre. So for -TAB
* (when CRVAL is set to zero) we need to record the obs centre some
* other way (use the AST-specific AXREF keywords, as for spatial axes).
* - Whether to scale spatial axes from degs to rads depends on
* whether the spatial axes are descirbed by -TAB or not.
* - Relax the linearity requirement in IsMapLinear by a factor of
* 10 to prevent a change in rest frame resulting in a non-linear
* mapping.
* 17-FEB-2011 (DSB):
* Fix bug in axis linearity check (IsMapLinear).
* 22-FEB-2011 (DSB):
* The translations of AIPS non-standard CTYPE values were always
* stored as primary axis description keywords, even if the original
* non-standard CTYPE values were read from an alternative axis
* descriptions.
* 5-APR-2011 (DSB):
* In SpecTrans, correct the MSX CAR projection translation. The
* first pixel starts at GRID=0.5, not GRID=0.0. So the CRPIX value
* needs to be reduced by 0.5 prior to normalisation, and then
* increased by 0.5 after normalisation.
* 23-MAY-2011 (DSB):
* Add support for TNX projections that use Chebyshev polynomials.
* 24-MAY-2011 (DSB):
* - Add support for ZPX projections that include IRAF polynomial
* corrections.
* - Add PolyTan attribute.
* - Fix interpretation of -SIP headers that have no inverse.
* 1-JUN-2011 (DSB):
* In astInitFitsChanVtab, only create the two TimeFrames if they
* have not already been created (fixes scuba2 trac ticket #666).
* 9-JUN-2011 (DSB):
* In WCSFcRead, ignore trailing spaces when reading string values
* for WCS keywords.
* 23-JUN-2011 (DSB):
* - Override the parent astSetSourceFile method so that it reads
* headers from the SourceFile and appends them to the end of the
* FitsChan.
* - On deletion, write out the FitsChan contents to the file
* specified by the SinkFile attribute. If no file is specified,
* use the sink function specified when the FitsChan was created.
* 30-AUG-2011 (DSB):
* - Added astWriteFits and astReadFits.
* - Move the deletion of tables and warnings from Delete to
* EmptyFits.
* 21-SEP-2011 (DSB):
* - In RoundFString, remember to update the pointer to the exponent.
* This bug caused parts of the exponent to dissappear when
* formatting a value that included some trailing zeros and a
* series of adjacent 9's.
* - Added Nkey attribute.
* 22-SEP-2011 (DSB):
* - Added CardType attribute
* - Allow GetFits to be used to get/set the value of the current
* card.
* 4-OCT-2011 (DSB):
* When reading a FITS-WCFS header, if the projection is TPV (as produced
* by SCAMP), change to TPN (the internal AST code for a distorted
* TAN projection).
* 22-NOV-2011 (DSB):
* Allow the "-SIP" code to be used with non-celestial axes.
* 1-FEB-2012 (DSB):
* Write out MJD-OBS in the timescale specified by any TIMESYS
* keyword in the FitsChan, and ensure the TIMESYS value is included
* in the output header.
* 23-FEB-2012 (DSB):
* Use iauGd2gc in place of palGeoc where is saves some calculations.
* 24-FEB-2012 (DSB):
* Move invocation of AddEncodingFrame from Write to end of
* MakeFitsFrameSet. This is so that AddEncodingFrame can take
* advantage of any standardisations (such as adding celestial axes)
* performed by MakeFItsFrameSet. Without this, a FRameSet contain
* a 1D SpecFrame (no celestial axes) would fail to be exported using
* FITS-CLASS encoding.
* 29-FEB-2012 (DSB):
* Fix bug in CLASSFromStore that caused spatial axes added by
* MakeFitsFrameSet to be ignored.
* 2-MAR-2012 (DSB):
* - In CLASSFromSTore, ensure NAXIS2/3 values are stored in teh FitsChan,
* and cater for FrameSets that have only a apectral axis and no celestial
* axes (this prevented the VELO_LSR keyword being created)..
* 7-MAR-2012 (DSB):
* Use iauGc2gd in place of Geod.
* 22-JUN-2012 (DSB):
* - Check for distorted TAN projections that have zero for all PVi_m
* coefficients. Issue a warning and ignore the distortion in such
* cases.
* - Remove all set but unused variables.
* - Convert SAO distorted TAN projections (which use COi_j keywords
* for polynomial coeffs) to TPN.
* 26-JUN-2012 (DSB):
* Correct call to astKeyFields in SAOTrans (thanks to Bill Joye
* for pointing out this error).
* 8-AUG-2012 (DSB):
* Correct assignment to lonpole within CLASSFromStore.
* 10-AUG-2012 (DSB):
* Default DSS keywords CNPIX1 and CNPIX2 to zero if they are
* absent, rather than reporting an error.
* 7-DEC-2012 (DSB):
* - When writing out a FrameSet that uses an SkyFrame to describe a
* generalised spherical coordinate system ("system=unknown"), ensure
* that the generated FITS CTYPE values use FITS-compliant codes
* for the axis type ( "xxLN/xxLT" or "xLON/xLAT" ).
* - Add support for reading and writing offset SkyFrames to
* FITS-WCS.
* 30-JAN-2013 (DSB):
* When reading a FITS-CLASS header, use "VLSR" keyword if
* "VELO-..." is not available.
* 15-APR-2013 (DSB):
* Correct initialisation of missing coefficients When reading a
* SAO plate solution header.
* 16-APR-2013 (DSB):
* When determining default Encoding value, use "VLSR" keyword if
* "VELO-..." is not available.
* 30-MAY-2013 (DSB):
* Prevent seg fault caused by overrunning the coeffs array in
* WATCoeffs in cases where the TNX/ZPX projection is found to be
* of a form that cannot be implemented as a TPN projection.
* 11-JUN-2013 (DSB):
* Fix support for reading GLS projections, and add support for
* rotated GLS projections.
* 28-AUG-2013 (DSB):
* In WcsCelestial, if celestial axes are found with no projection
* code in CTYPE, assume an old-fashioned CAR projection (i.e. no
* rotation from native to WCS coords). Before this change,
* CTYPE = "RA" | "DEC" axes got treated as radians, not degrees.
* 16-SEP-2013 (DSB):
* When exporting alternate offset SkyFrames to FITS-WCS headers,
* correctly test the alternate Frame in the supplied FrameSet, rather
* than the current Frame.
* 24-SEP-2013 (DSB):
* Fix bug in choosing default value for PolyTan attribute.
* 19-OCT-2013 (DSB):
* - In SIPMapping, always ignore any inverse polynomial supplied in
* a SIP header as they seem often to be inaccurate. A new inverse is
* created to replace it.
* - In SIPMapping, only use a fit to the inverted SIP transformation
* if an accuracy of 0.01 pixel can be achieved over an area three
* times the dimensions of the image. Otherwise use an iterative
* inverse for each point. People were seeing bad round-trip errors
* when transforming points outside the image because the fit was
* being used when it was not very accurate.
* 12-NOV-2013 (DSB):
* Added CardName and CardComm attributes.
* 13-NOV-2013 (DSB):
* Use a zero-length string for the CardComm attribute if the card
* has no comment.
* 15-NOV-2013 (DSB):
* - Added method astShowFits.
* - Ensure PurgeWcs removes WCS cards even if an error occurs when
* reading FrameSets from the FitsChan.
* - Change IsMapTab1D to improve chances of a -TAB mapping being found.
* 6-JAN-2014 (DSB):
* - Allow default options for newly created FitsChans to be
* specified by the FITSCHAN_OPTIONS environment variable.
* - Ensure the used CarLin value is not changed by a trailing frequency axis.
* 9-JUL-2014 (DSB):
* Added attribute FitsAxisOrder, which allows an order to be
* specified for WCS axis within FITS headers generated using astWrite.
* 9-SEP-2014 (DSB):
* Modify Split so that any non-printing characters such as
* newlines at the end of the string are ignored.
* 30-SEP-2014 (DSB):
* Modify CnvType to indicate that comment cards cannot be
* converted to any other data type. For instance, this causes
* a warning to be issued if an equals sign is misplaced in a
* WCS-related card (causing it to be treated as a comment card).
* 24-MAR-2015 (DSB):
* Modify SpecTrans to avoid modifying the CRPIC value for CAR
* projections when they do not need to be modified. The princiuple
* is that the bulk of the array should be witin the native longitude
* range [-180,+180]. Prompted by bug report from Bill Joye "yet
* another CAR issue" on 24-MAR-2015 (file CHIPASS_Equ.head in
* ast_tester).
* 27-APR-2015 (DSB):
* Modify MakeFitsFrameSet so that isolated SkyAxes (e.g.
* individual axes that have been oicked from a SkyFrame) are
* re-mapped into degrees before being used.
* 20-APR-2015 (DSB):
* In MakeIntWorld, relax tolerance for checking that each FITS-WCS IWC
* axis is linear, from 0.01 of a pixel to 0.1 of a pixel.
* 6-JUL-2015 (DSB):
* When checking a sub-string, ensure the whole string is at least as
* long as the offset to the start of the sub-string. Without this, you
* can get erroneous sub-string matches by chance, depending on what
* characters happen to be present in memory after the end of the string.
* 11-AUG-2015 (DSB):
* - Fix bug in CheckFitsName that prevented an error from being reported
* if the FITS keyword name contained any illegal printable characters.
* - Add new Warning "badkeyname", and issue such a warning instead
* of an error if illegal characters are found in a keyword name.
* 31-AUG-2015 (DSB):
* In FitLine, use the whole axis rather than 0.1 of the axis (if "dim"
* is supplied). This is because non-linearity can become greater at
* further distances along the axis. In practice, it meant that SIP
* distortion were being treated as linear because the test did not
* explore a large enough region of pixel space.
* 12-OCT-2015 (DSB):
* Only add sky axes to a SpecFrame if the WCS Frame contains no
* other axes other than the SpecFrame (MakeFitsFrameSet).
* 17-OCT-2015 (DSB):
* - Add new Warning "badkeyvalue", and issue such a warning instead
* of an error if the Split function cannot determine the keyword value.
* - Move the check for PLTRAH (i.e. DSS encoding) higher up in GetEncoding.
* This is because some DSS file slaos have CD and/or PC keywords.
*class--
*/
/* Module Macros. */
/* ============== */
/* Set the name of the class we are implementing. This indicates to
the header files that define class interfaces that they should make
"protected" symbols available. */
#define astCLASS FitsChan
/* A macro which tests a character to see if it can be used within a FITS
keyword. We include lower case letters here, but they are considered
as equivalent to upper case letter. */
#define isFits(a) ( islower(a) || isupper(a) || isdigit(a) || (a)=='-' || (a)=='_' )
/* A amacro to test if a Frame is a SkyFrame, and is used to describe the
sky (skyframes could be used for other purposes - eg a POLANAL Frame). */
#define IsASkyFrame(frame) (astIsASkyFrame(frame)&&!strcmp("SKY",astGetDomain(frame)))
/* Macros which return the maximum and minimum of two values. */
#define MAX(aa,bb) ((aa)>(bb)?(aa):(bb))
#define MIN(aa,bb) ((aa)<(bb)?(aa):(bb))
/* Macro which takes a pointer to a FitsCard and returns non-zero if the
card has been used and so should be ignored. */
#define CARDUSED(card) ( \
( ignore_used == 2 && \
( (FitsCard *) (card) )->flags & PROVISIONALLY_USED ) || \
( ignore_used >= 1 && \
( (FitsCard *) (card) )->flags & USED ) )
/* Set of characters used to encode a "sequence number" at the end of
FITS keywords in an attempt to make them unique.. */
#define SEQ_CHARS "_ABCDEFGHIJKLMNOPQRSTUVWXYZ"
/* A general tolerance for equality between floating point values. */
#define TOL1 10.0*DBL_EPSILON
/* A tolerance for equality between angular values in radians. */
#define TOL2 1.0E-10
/* Macro to check for equality of floating point values. We cannot
compare bad values directory because of the danger of floating point
exceptions, so bad values are dealt with explicitly. */
#define EQUAL(aa,bb) (((aa)==AST__BAD)?(((bb)==AST__BAD)?1:0):(((bb)==AST__BAD)?0:(fabs((aa)-(bb))<=1.0E5*MAX((fabs(aa)+fabs(bb))*DBL_EPSILON,DBL_MIN))))
/* Macro to check for equality of floating point angular values. We cannot
compare bad values directory because of the danger of floating point
exceptions, so bad values are dealt with explicitly. The smallest
significant angle is assumed to be 1E-9 radians (0.0002 arc-seconds).*/
#define EQUALANG(aa,bb) (((aa)==AST__BAD)?(((bb)==AST__BAD)?1:0):(((bb)==AST__BAD)?0:(fabs((aa)-(bb))<=MAX(1.0E5*(fabs(aa)+fabs(bb))*DBL_EPSILON,1.0E-9))))
/* Macro to compare an angle in radians with zero, allowing some tolerance. */
#define ZEROANG(aa) (fabs(aa)<1.0E-9)
/* Constants: */
#define UNKNOWN_ENCODING -1
#define NATIVE_ENCODING 0
#define FITSPC_ENCODING 1
#define DSS_ENCODING 2
#define FITSWCS_ENCODING 3
#define FITSIRAF_ENCODING 4
#define FITSAIPS_ENCODING 5
#define FITSAIPSPP_ENCODING 6
#define FITSCLASS_ENCODING 7
#define MAX_ENCODING 7
#define UNKNOWN_STRING "UNKNOWN"
#define NATIVE_STRING "NATIVE"
#define FITSPC_STRING "FITS-PC"
#define FITSPC_STRING2 "FITS_PC"
#define DSS_STRING "DSS"
#define FITSWCS_STRING "FITS-WCS"
#define FITSWCS_STRING2 "FITS_WCS"
#define FITSIRAF_STRING "FITS-IRAF"
#define FITSIRAF_STRING2 "FITS_IRAF"
#define FITSAIPS_STRING "FITS-AIPS"
#define FITSAIPS_STRING2 "FITS_AIPS"
#define FITSAIPSPP_STRING "FITS-AIPS++"
#define FITSAIPSPP_STRING2 "FITS_AIPS++"
#define FITSCLASS_STRING "FITS-CLASS"
#define FITSCLASS_STRING2 "FITS_CLASS"
#define INDENT_INC 3
#define PREVIOUS 0
#define NEXT 1
#define HEADER_TEXT "Beginning of AST data for "
#define FOOTER_TEXT "End of AST data for "
#define FITSNAMLEN 8
#define FITSSTCOL 20
#define FITSRLCOL 30
#define FITSIMCOL 50
#define FITSCOMCOL 32
#define NORADEC 0
#define FK4 1
#define FK4NOE 2
#define FK5 3
#define GAPPT 4
#define ICRS 5
#define NOCEL 0
#define RADEC 1
#define ECLIP 2
#define GALAC 3
#define SUPER 4
#define HECLIP 5
#define AZEL 6
#define LONAX -1
#define NONAX 0
#define LATAX 1
#define NDESC 9
#define MXCTYPELEN 81
#define ALLWARNINGS " distortion noequinox noradesys nomjd-obs nolonpole nolatpole tnx zpx badcel noctype badlat badmat badval badctype badpv badkeyname badkeyvalue "
#define NPFIT 10
#define SPD 86400.0
#define FL 1.0/298.257 /* Reference spheroid flattening factor */
#define A0 6378140.0 /* Earth equatorial radius (metres) */
/* String used to represent AST__BAD externally. */
#define BAD_STRING ""
/* Each card in the fitschan has a set of flags associated with it,
stored in different bits of the "flags" item within each FitsCard
structure (note, in AST V1.4 these flags were stored in the "del"
item... Dump and LoadFitsChan will need to be changed to use a
correspondingly changed name for the external representation of this
item). The following flags are currently defined: */
/* "USED" - This flag indicates that the the card has been used in the
construction of an AST Object returned by astRead. Such cards should
usually be treated as if they do not exist, i.e. they should not be
used again by subsequent calls to astRead, they should not be recognised
by public FitsChan methods which search the FitsChan for specified
cards, and they should not be written out when the FitsChan is deleted.
This flag was the only flag available in AST V1.4, and was called
"Del" (for "deleted"). Used cards are retained in order to give an
indication of where abouts within the header new cards should be placed
when astWrite is called (i.e. new cards should usually be placed at
the same point within the header as the cards which they replace). */
#define USED 1
/* "PROVISIONALLY_USED" - This flag indicates that the the card is being
considered as a candidate for inclusion in the construction of an AST
Object. If the Object is constructed succesfully, cards flagged as
"provisionally used" will be changed to be flagged as "definitely used"
(using the USED flag). If the Object fails to be constructed
succesfully (if some required cards are missing from the FitsChan
for instance), then "provisionally used" cards will be returned to the
former state which they had prior to the attempt to construct the
object. */
#define PROVISIONALLY_USED 2
/* "NEW" - This flag indicates that the the card has just been added to
the FitsChan and may yet proove to be unrequired. For instance if the
supplied Object is not of an appropriate flavour to be stored using
the requested encoding, all "new" cards which were added before the
inappropriateness was discovered will be removed from the FitsChan.
Two different levels of "newness" are available. */
#define NEW1 4
#define NEW2 8
/* "PROTECTED" - This flag indicates that the the card should not be
removed form the FitsChan when an Object is read using astRead. If
this flag is not set, then the card will dehave as if it has been
deleted if it was used in the construction of the returned AST Object. */
#define PROTECTED 16
/* Include files. */
/* ============== */
/* Interface definitions. */
/* ---------------------- */
#include "channel.h"
#include "cmpframe.h"
#include "cmpmap.h"
#include "dssmap.h"
#include "error.h"
#include "fitschan.h"
#include "frame.h"
#include "frameset.h"
#include "grismmap.h"
#include "lutmap.h"
#include "mathmap.h"
#include "matrixmap.h"
#include "memory.h"
#include "object.h"
#include "permmap.h"
#include "pointset.h"
#include "shiftmap.h"
#include "skyframe.h"
#include "timeframe.h"
#include "keymap.h"
#include "pal.h"
#include "erfa.h"
#include "slamap.h"
#include "specframe.h"
#include "dsbspecframe.h"
#include "specmap.h"
#include "sphmap.h"
#include "unit.h"
#include "unitmap.h"
#include "polymap.h"
#include "wcsmap.h"
#include "winmap.h"
#include "zoommap.h"
#include "globals.h"
#include "fitstable.h"
/* Error code definitions. */
/* ----------------------- */
#include "ast_err.h" /* AST error codes */
/* C header files. */
/* --------------- */
#include
#include
#include
#include
#include
#include
#include
#include
/* Type Definitions */
/* ================ */
/* This structure contains information describing a single FITS header card
in a circular list of such structures. */
typedef struct FitsCard {
char name[ FITSNAMLEN + 1 ];/* Keyword name (plus terminating null). */
int type; /* Data type. */
void *data; /* Pointer to the keyword's data value. */
char *comment; /* Pointer to a comment for the keyword. */
int flags; /* Flags for each card */
size_t size; /* Size of data value */
struct FitsCard *next; /* Pointer to next structure in list. */
struct FitsCard *prev; /* Pointer to previous structure in list. */
} FitsCard;
/* Structure used to store information derived from the FITS WCS keyword
values in a form more convenient to further processing. Conventions
for units, etc, for values in a FitsStore follow FITS-WCS (e.g. angular
values are stored in degrees, equinox is B or J depending on RADECSYS,
etc). */
typedef struct FitsStore {
char ****cname;
char ****ctype;
char ****ctype_com;
char ****cunit;
char ****radesys;
char ****wcsname;
char ****specsys;
char ****ssyssrc;
char ****ps;
char ****timesys;
double ***pc;
double ***cdelt;
double ***crpix;
double ***crval;
double ***equinox;
double ***latpole;
double ***lonpole;
double ***mjdobs;
double ***dut1;
double ***mjdavg;
double ***pv;
double ***wcsaxes;
double ***obsgeox;
double ***obsgeoy;
double ***obsgeoz;
double ***restfrq;
double ***restwav;
double ***zsource;
double ***velosys;
double ***asip;
double ***bsip;
double ***apsip;
double ***bpsip;
double ***imagfreq;
double ***axref;
int naxis;
AstKeyMap *tables;
double ***skyref;
double ***skyrefp;
char ****skyrefis;
} FitsStore;
/* 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 void (* parent_setsourcefile)( AstChannel *, const char *, int * );
static int (* parent_getobjsize)( AstObject *, int * );
static const char *(* parent_getattrib)( AstObject *, const char *, int * );
static int (* parent_getfull)( AstChannel *, int * );
static int (* parent_getskip)( AstChannel *, 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_write)( AstChannel *, AstObject *, int * );
static AstObject *(* parent_read)( AstChannel *, int * );
#if defined(THREAD_SAFE)
static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * );
#endif
/* Strings to describe each data type. These should be in the order implied
by the corresponding macros (eg AST__FLOAT, etc). */
static const char *type_names[9] = {"comment", "integer", "floating point",
"string", "complex floating point",
"complex integer", "logical",
"continuation string", "undef" };
/* Text values used to represent Encoding values externally. */
static const char *xencod[8] = { NATIVE_STRING, FITSPC_STRING,
DSS_STRING, FITSWCS_STRING,
FITSIRAF_STRING, FITSAIPS_STRING,
FITSAIPSPP_STRING, FITSCLASS_STRING };
/* Define two variables to hold TimeFrames which will be used for converting
MJD values between time scales. */
static AstTimeFrame *tdbframe = NULL;
static AstTimeFrame *timeframe = NULL;
/* Max number of characters in a formatted int */
static int int_dig;
/* 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->GetAttrib_Buff[ 0 ] = 0; \
globals->Items_Written = 0; \
globals->Write_Nest = -1; \
globals->Current_Indent = 0; \
globals->Ignore_Used = 1; \
globals->Mark_New = 0; \
globals->CnvType_Text[ 0 ] = 0; \
globals->CnvType_Text0[ 0 ] = 0; \
globals->CnvType_Text1[ 0 ] = 0; \
globals->CreateKeyword_Seq_Nchars = -1; \
globals->FormatKey_Buff[ 0 ] = 0; \
globals->FitsGetCom_Sval[ 0 ] = 0; \
globals->IsSpectral_Ret = NULL; \
globals->Match_Fmt[ 0 ] = 0; \
globals->Match_Template = NULL; \
globals->Match_PA = 0; \
globals->Match_PB = 0; \
globals->Match_NA = 0; \
globals->Match_NB = 0; \
globals->Match_Nentry = 0; \
globals->WcsCelestial_Type[ 0 ] = 0; \
globals->Ignore_Used = 1; \
globals->Mark_New = 0;
/* Create the function that initialises global data for this module. */
astMAKE_INITGLOBALS(FitsChan)
/* Define macros for accessing each item of thread specific global data. */
#define class_init astGLOBAL(FitsChan,Class_Init)
#define class_vtab astGLOBAL(FitsChan,Class_Vtab)
#define getattrib_buff astGLOBAL(FitsChan,GetAttrib_Buff)
#define items_written astGLOBAL(FitsChan,Items_Written)
#define write_nest astGLOBAL(FitsChan,Write_Nest)
#define current_indent astGLOBAL(FitsChan,Current_Indent)
#define ignore_used astGLOBAL(FitsChan,Ignore_Used)
#define mark_new astGLOBAL(FitsChan,Mark_New)
#define cnvtype_text astGLOBAL(FitsChan,CnvType_Text)
#define cnvtype_text0 astGLOBAL(FitsChan,CnvType_Text0)
#define cnvtype_text1 astGLOBAL(FitsChan,CnvType_Text1)
#define createkeyword_seq_nchars astGLOBAL(FitsChan,CreateKeyword_Seq_Nchars)
#define formatkey_buff astGLOBAL(FitsChan,FormatKey_Buff)
#define fitsgetcom_sval astGLOBAL(FitsChan,FitsGetCom_Sval)
#define isspectral_ret astGLOBAL(FitsChan,IsSpectral_Ret)
#define match_fmt astGLOBAL(FitsChan,Match_Fmt)
#define match_template astGLOBAL(FitsChan,Match_Template)
#define match_pa astGLOBAL(FitsChan,Match_PA)
#define match_pb astGLOBAL(FitsChan,Match_PB)
#define match_na astGLOBAL(FitsChan,Match_NA)
#define match_nb astGLOBAL(FitsChan,Match_NB)
#define match_nentry astGLOBAL(FitsChan,Match_Nentry)
#define wcscelestial_type astGLOBAL(FitsChan,WcsCelestial_Type)
static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 );
#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 );
static pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER;
#define LOCK_MUTEX3 pthread_mutex_lock( &mutex3 );
#define UNLOCK_MUTEX3 pthread_mutex_unlock( &mutex3 );
static pthread_mutex_t mutex4 = PTHREAD_MUTEX_INITIALIZER;
#define LOCK_MUTEX4 pthread_mutex_lock( &mutex4 );
#define UNLOCK_MUTEX4 pthread_mutex_unlock( &mutex4 );
/* If thread safety is not needed, declare and initialise globals at static
variables. */
#else
/* Buffer returned by GetAttrib. */
static char getattrib_buff[ AST__FITSCHAN_GETATTRIB_BUFF_LEN + 1 ];
/* Buffer for returned text string in CnvType */
static char cnvtype_text[ AST__FITSCHAN_FITSCARDLEN + 1 ];
/* Buffer for real value in CnvType */
static char cnvtype_text0[ AST__FITSCHAN_FITSCARDLEN + 1 ];
/* Buffer for imaginary value in CnvType */
static char cnvtype_text1[ AST__FITSCHAN_FITSCARDLEN + 1 ];
/* Number of output items written since the last "Begin" or "IsA"
output item, and level of Object nesting during recursive
invocation of the astWrite method. */
static int items_written = 0;
static int write_nest = -1;
/* Indentation level for indented comments when writing Objects to a
FitsChan. */
static int current_indent = 0;
/* Ignore_Used: If 2, then cards which have been marked as either "definitely
used" or "provisionally used" (see the USED flag above) will be ignored
when searching the FitsChan, etc (i.e. they will be treated as if they
have been removed from the FitsChan). If 1, then cards which have been
"definitely used" will be skipped over. If zero then no cards will be
skipped over. */
static int ignore_used = 1;
/* Mark_New: If non-zero, then all cards added to the FitsChan will be
marked with both the NEW1 and NEW2 flags (see above). If zero then
new cards will not be marked with either NEW1 or NEW2. */
static int mark_new = 0;
/* Number of characters used for encoding */
static int createkeyword_seq_nchars = -1;
/* Buffer for value returned by FormatKey */
static char formatkey_buff[ 10 ];
/* Buffer for value returned by FitsGetCom */
static char fitsgetcom_sval[ AST__FITSCHAN_FITSCARDLEN + 1 ];
/* Pointer returned by IsSpectral */
static const char *isspectral_ret = NULL;
/* Format specifier for reading an integer field in Match */
static char match_fmt[ 10 ];
/* Pointer to start of template in Match */
static const char *match_template = NULL;
/* Pointer to first returned field value in Match */
static int *match_pa = 0;
/* Pointer to last returned field value in Match */
static int *match_pb = 0;
/* No. of characters read from the test string in Match */
static int match_na = 0;
/* No. of characters read from the template string in Match */
static int match_nb = 0;
/* Number of recursive entries into Match */
static int match_nentry = 0;
/* Buffer for celestial system in WcsCelestial */
static char wcscelestial_type[ 4 ];
/* Define the class virtual function table and its initialisation flag
as static variables. */
static AstFitsChanVtab class_vtab; /* Virtual function table */
static int class_init = 0; /* Virtual function table initialised? */
#define LOCK_MUTEX2
#define UNLOCK_MUTEX2
#define LOCK_MUTEX3
#define UNLOCK_MUTEX3
#define LOCK_MUTEX4
#define UNLOCK_MUTEX4
#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. */
AstFitsChan *astFitsChanForId_( const char *(*)( void ),
char *(*)( const char *(*)( void ), int * ),
void (*)( const char * ),
void (*)( void (*)( const char * ), const char *, int * ),
const char *, ... );
AstFitsChan *astFitsChanId_( const char *(* source)( void ),
void (* sink)( const char * ),
const char *options, ... );
/* Prototypes for Private Member Functions. */
/* ======================================== */
static int GetObjSize( AstObject *, int * );
static void ClearCard( AstFitsChan *, int * );
static int GetCard( AstFitsChan *, int * );
static int TestCard( AstFitsChan *, int * );
static void SetCard( AstFitsChan *, int, int * );
static void ClearEncoding( AstFitsChan *, int * );
static int GetEncoding( AstFitsChan *, int * );
static int TestEncoding( AstFitsChan *, int * );
static void SetEncoding( AstFitsChan *, int, int * );
static void ClearCDMatrix( AstFitsChan *, int * );
static int GetCDMatrix( AstFitsChan *, int * );
static int TestCDMatrix( AstFitsChan *, int * );
static void SetCDMatrix( AstFitsChan *, int, int * );
static void ClearFitsDigits( AstFitsChan *, int * );
static int GetFitsDigits( AstFitsChan *, int * );
static int TestFitsDigits( AstFitsChan *, int * );
static void SetFitsDigits( AstFitsChan *, int, int * );
static void ClearFitsAxisOrder( AstFitsChan *, int * );
static const char *GetFitsAxisOrder( AstFitsChan *, int * );
static int TestFitsAxisOrder( AstFitsChan *, int * );
static void SetFitsAxisOrder( AstFitsChan *, const char *, int * );
static void ClearDefB1950( AstFitsChan *, int * );
static int GetDefB1950( AstFitsChan *, int * );
static int TestDefB1950( AstFitsChan *, int * );
static void SetDefB1950( AstFitsChan *, int, int * );
static void ClearTabOK( AstFitsChan *, int * );
static int GetTabOK( AstFitsChan *, int * );
static int TestTabOK( AstFitsChan *, int * );
static void SetTabOK( AstFitsChan *, int, int * );
static void ClearCarLin( AstFitsChan *, int * );
static int GetCarLin( AstFitsChan *, int * );
static int TestCarLin( AstFitsChan *, int * );
static void SetCarLin( AstFitsChan *, int, int * );
static void ClearPolyTan( AstFitsChan *, int * );
static int GetPolyTan( AstFitsChan *, int * );
static int TestPolyTan( AstFitsChan *, int * );
static void SetPolyTan( AstFitsChan *, int, int * );
static void ClearIwc( AstFitsChan *, int * );
static int GetIwc( AstFitsChan *, int * );
static int TestIwc( AstFitsChan *, int * );
static void SetIwc( AstFitsChan *, int, int * );
static void ClearClean( AstFitsChan *, int * );
static int GetClean( AstFitsChan *, int * );
static int TestClean( AstFitsChan *, int * );
static void SetClean( AstFitsChan *, int, int * );
static void ClearWarnings( AstFitsChan *, int * );
static const char *GetWarnings( AstFitsChan *, int * );
static int TestWarnings( AstFitsChan *, int * );
static void SetWarnings( AstFitsChan *, const char *, int * );
static AstFitsChan *SpecTrans( AstFitsChan *, int, const char *, const char *, int * );
static AstFitsTable *GetNamedTable( AstFitsChan *, const char *, int, int, int, const char *, int * );
static AstFrameSet *MakeFitsFrameSet( AstFitsChan *, AstFrameSet *, int, int, int, const char *, const char *, int * );
static AstGrismMap *ExtractGrismMap( AstMapping *, int, AstMapping **, int * );
static AstKeyMap *GetTables( AstFitsChan *, int * );
static AstMapping *AddUnitMaps( AstMapping *, int, int, int * );
static AstMapping *CelestialAxes( AstFitsChan *this, AstFrameSet *, double *, int *, char, FitsStore *, int *, int, const char *, const char *, int * );
static AstMapping *GrismSpecWcs( char *, FitsStore *, int, char, AstSpecFrame *, const char *, const char *, int * );
static AstMapping *IsMapTab1D( AstMapping *, double, const char *, AstFrame *, double *, int, int, AstFitsTable **, int *, int *, int *, int * );
static AstMapping *IsMapTab2D( AstMapping *, double, const char *, AstFrame *, double *, int, int, int, int, AstFitsTable **, int *, int *, int *, int *, int *, int *, int *, int *, int * );
static AstMapping *LinearWcs( FitsStore *, int, char, const char *, const char *, int * );
static AstMapping *LogAxis( AstMapping *, int, int, double *, double *, double, int * );
static AstMapping *LogWcs( FitsStore *, int, char, const char *, const char *, int * );
static AstMapping *MakeColumnMap( AstFitsTable *, const char *, int, int, const char *, const char *, int * );
static AstMapping *NonLinSpecWcs( AstFitsChan *, char *, FitsStore *, int, char, AstSpecFrame *, const char *, const char *, int * );
static AstMapping *OtherAxes( AstFitsChan *, AstFrameSet *, double *, int *, char, FitsStore *, double *, int *, const char *, const char *, int * );
static AstMapping *SIPMapping( double *, FitsStore *, char, int, const char *, const char *, int * );
static AstMapping *SpectralAxes( AstFitsChan *, AstFrameSet *, double *, int *, char, FitsStore *, double *, int *, const char *, const char *, int * );
static AstMapping *TabMapping( AstFitsChan *, FitsStore *, char, int **, const char *, const char *, int *);
static AstMapping *WcsCelestial( AstFitsChan *, FitsStore *, char, AstFrame **, AstFrame *, double *, double *, AstSkyFrame **, AstMapping **, int *, const char *, const char *, int * );
static AstMapping *WcsIntWorld( AstFitsChan *, FitsStore *, char, int, const char *, const char *, int * );
static AstMapping *WcsMapFrm( AstFitsChan *, FitsStore *, char, AstFrame **, const char *, const char *, int * );
static AstMapping *WcsNative( AstFitsChan *, FitsStore *, char, AstWcsMap *, int, int, const char *, const char *, int * );
static AstMapping *WcsOthers( AstFitsChan *, FitsStore *, char, AstFrame **, AstFrame *, const char *, const char *, int * );
static AstMapping *WcsSpectral( AstFitsChan *, FitsStore *, char, AstFrame **, AstFrame *, double, double, AstSkyFrame *, const char *, const char *, int * );
static AstMapping *ZPXMapping( AstFitsChan *, FitsStore *, char, int, int[2], const char *, const char *, int * );
static AstMatrixMap *WcsCDeltMatrix( FitsStore *, char, int, const char *, const char *, int * );
static AstMatrixMap *WcsPCMatrix( FitsStore *, char, int, const char *, const char *, int * );
static AstObject *FsetFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * );
static AstObject *Read( AstChannel *, int * );
static AstSkyFrame *WcsSkyFrame( AstFitsChan *, FitsStore *, char, int, char *, int, int, const char *, const char *, int * );
static AstTimeScaleType TimeSysToAst( AstFitsChan *, const char *, const char *, const char *, int * );
static AstWinMap *WcsShift( FitsStore *, char, int, const char *, const char *, int * );
static FitsCard *GetLink( FitsCard *, int, const char *, const char *, int * );
static FitsStore *FitsToStore( AstFitsChan *, int, const char *, const char *, int * );
static FitsStore *FreeStore( FitsStore *, int * );
static FitsStore *FsetToStore( AstFitsChan *, AstFrameSet *, int, double *, int, const char *, const char *, int * );
static char *CardComm( AstFitsChan *, int * );
static char *CardName( AstFitsChan *, int * );
static char *ConcatWAT( AstFitsChan *, int, const char *, const char *, int * );
static char *FormatKey( const char *, int, int, char, int * );
static char *GetItemC( char *****, int, int, char, char *, const char *method, const char *class, int * );
static char *SourceWrap( const char *(*)( void ), int * );
static char *UnPreQuote( const char *, int * );
static char GetMaxS( double ****item, int * );
static const char *GetAllWarnings( AstFitsChan *, int * );
static const char *GetAttrib( AstObject *, const char *, int * );
static const char *GetCardComm( AstFitsChan *, int * );
static const char *GetCardName( AstFitsChan *, int * );
static const char *GetFitsSor( const char *, int * );
static const char *IsSpectral( const char *, char[5], char[5], int * );
static double **OrthVectorSet( int, int, double **, int * );
static double *Cheb2Poly( double *, int, int, double, double, double, double, int * );
static double *FitLine( AstMapping *, double *, double *, double *, double, double *, int * );
static double *OrthVector( int, int, double **, int * );
static double *ReadCrval( AstFitsChan *, AstFrame *, char, const char *, const char *, int * );
static double ChooseEpoch( AstFitsChan *, FitsStore *, char, const char *, const char *, int * );
static double DateObs( const char *, int * );
static double GetItem( double ****, int, int, char, char *, const char *method, const char *class, int * );
static double NearestPix( AstMapping *, double, int, int * );
static double TDBConv( double, int, int, const char *, const char *, int * );
static int *CardFlags( AstFitsChan *, int * );
static int AIPSFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * );
static int AIPSPPFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * );
static int AddEncodingFrame( AstFitsChan *, AstFrameSet *, int, const char *, const char *, int * );
static int AddVersion( AstFitsChan *, AstFrameSet *, int, int, FitsStore *, double *, char, int, int, const char *, const char *, int * );
static int CLASSFromStore( AstFitsChan *, FitsStore *, AstFrameSet *, double *, const char *, const char *, int * );
static int CardType( AstFitsChan *, int * );
static int CheckFitsName( AstFitsChan *, const char *, const char *, const char *, int * );
static int ChrLen( const char *, int * );
static int CnvType( int, void *, size_t, int, int, void *, const char *, const char *, const char *, int * );
static int CnvValue( AstFitsChan *, int , int, void *, const char *, int * );
static int ComBlock( AstFitsChan *, int, const char *, const char *, int * );
static int CountFields( const char *, char, const char *, const char *, int * );
static int DSSFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * );
static int EncodeFloat( char *, int, int, int, double, int * );
static int EncodeValue( AstFitsChan *, char *, int, int, const char *, int * );
static int FindBasisVectors( AstMapping *, int, int, double *, AstPointSet *, AstPointSet *, int * );
static int FindFits( AstFitsChan *, const char *, char[ AST__FITSCHAN_FITSCARDLEN + 1 ], int, int * );
static int FindKeyCard( AstFitsChan *, const char *, const char *, const char *, int * );
static int FindLonLatSpecAxes( FitsStore *, char, int *, int *, int *, const char *, const char *, int * );
static int FindString( int, const char *[], const char *, const char *, const char *, const char *, int * );
static int FitOK( int, double *, double *, double, int * );
static int FitsAxisOrder( AstFitsChan *this, int nwcs, AstFrame *wcsfrm, int *perm, int *status );
static int FitsEof( AstFitsChan *, int * );
static int FitsFromStore( AstFitsChan *, FitsStore *, int, double *, AstFrameSet *, const char *, const char *, int * );
static int FitsGetCom( AstFitsChan *, const char *, char **, int * );
static int FitsSof( AstFitsChan *, int * );
static int FullForm( const char *, const char *, int, int * );
static int GetCardType( AstFitsChan *, int * );
static int GetFiducialWCS( AstWcsMap *, AstMapping *, int, int, double *, double *, int * );
static int GetFitsCF( AstFitsChan *, const char *, double *, int * );
static int GetFitsCI( AstFitsChan *, const char *, int *, int * );
static int GetFitsCN( AstFitsChan *, const char *, char **, int * );
static int GetFitsF( AstFitsChan *, const char *, double *, int * );
static int GetFitsI( AstFitsChan *, const char *, int *, int * );
static int GetFitsL( AstFitsChan *, const char *, int *, int * );
static int GetFitsS( AstFitsChan *, const char *, char **, int * );
static int GetFull( AstChannel *, int * );
static int GetMaxI( double ****item, char, int * );
static int GetMaxJM( double ****item, char, int * );
static int GetMaxJMC( char *****item, char, int * );
static int GetNcard( AstFitsChan *, int * );
static int GetNkey( AstFitsChan *, int * );
static int GetSkip( AstChannel *, int * );
static int GetUsedPolyTan( AstFitsChan *, AstFitsChan *, int, int, char, const char *, const char *, int * );
static int GetValue( AstFitsChan *, const char *, int, void *, int, int, const char *, const char *, int * );
static int GetValue2( AstFitsChan *, AstFitsChan *, const char *, int, void *, int, const char *, const char *, int * );
static int GoodWarns( const char *, int * );
static int HasAIPSSpecAxis( AstFitsChan *, const char *, const char *, int * );
static int HasCard( AstFitsChan *, const char *, const char *, const char *, int * );
static int IRAFFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * );
static int IsAIPSSpectral( const char *, char **, char **, int * );
static int IsMapLinear( AstMapping *, const double [], const double [], int, int * );
static int IsSkyOff( AstFrameSet *, int, int * );
static int KeyFields( AstFitsChan *, const char *, int, int *, int *, int * );
static int LooksLikeClass( AstFitsChan *, const char *, const char *, int * );
static int MakeBasisVectors( AstMapping *, int, int, double *, AstPointSet *, AstPointSet *, int * );
static int MakeIntWorld( AstMapping *, AstFrame *, int *, char, FitsStore *, double *, const char *, const char *, int * );
static int Match( const char *, const char *, int, int *, int *, const char *, const char *, int * );
static int MatchChar( char, char, const char *, const char *, const char *, int * );
static int MatchFront( const char *, const char *, char *, int *, int *, int *, const char *, const char *, const char *, int * );
static int MoveCard( AstFitsChan *, int, const char *, const char *, int * );
static int PCFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * );
static int SAOTrans( AstFitsChan *, AstFitsChan *, const char *, const char *, int * );
static int SearchCard( AstFitsChan *, const char *, const char *, const char *, int * );
static int SetFits( AstFitsChan *, const char *, void *, int, const char *, int, int * );
static int Similar( const char *, const char *, int * );
static int SkySys( AstFitsChan *, AstSkyFrame *, int, int, FitsStore *, int, int, char c, int, const char *, const char *, int * );
static int Split( AstFitsChan *, const char *, char **, char **, char **, const char *, const char *, int * );
static int SplitMap( AstMapping *, int, int, int, AstMapping **, AstWcsMap **, AstMapping **, int * );
static int SplitMap2( AstMapping *, int, AstMapping **, AstWcsMap **, AstMapping **, int * );
static int SplitMat( int , double *, double *, int * );
static int TestAttrib( AstObject *, const char *, int * );
static int TestFits( AstFitsChan *, const char *, int *, int * );
static int Use( AstFitsChan *, int, int, int * );
static int Ustrcmp( const char *, const char *, int * );
static int Ustrncmp( const char *, const char *, size_t, int * );
static int WATCoeffs( const char *, int, double **, int **, int *, int * );
static int WcsFromStore( AstFitsChan *, FitsStore *, const char *, const char *, int * );
static int WcsNatPole( AstFitsChan *, AstWcsMap *, double, double, double, double *, double *, double *, int * );
static int WorldAxes( AstFitsChan *this, AstMapping *, double *, int *, int * );
static int Write( AstChannel *, AstObject *, int * );
static void *CardData( AstFitsChan *, size_t *, int * );
static void AdaptLut( AstMapping *, int, double, double, double, double, double, double **, double **, int *, int * );
static void AddFrame( AstFitsChan *, AstFrameSet *, int, int, FitsStore *, char, const char *, const char *, int * );
static void ChangePermSplit( AstMapping *, int * );
static void CheckZero( char *, double, int, int * );
static void Chpc1( double *, double *, int, int *, int *, int * );
static void ClassTrans( AstFitsChan *, AstFitsChan *, int, int, const char *, const char *, int * );
static void ClearAttrib( AstObject *, const char *, int * );
static void Copy( const AstObject *, AstObject *, int * );
static void CreateKeyword( AstFitsChan *, const char *, char [ FITSNAMLEN + 1 ], int * );
static void DSBSetUp( AstFitsChan *, FitsStore *, AstDSBSpecFrame *, char, double, const char *, const char *, int * );
static void DSSToStore( AstFitsChan *, FitsStore *, const char *, const char *, int * );
static void DelFits( AstFitsChan *, int * );
static void Delete( AstObject *, int * );
static void DeleteCard( AstFitsChan *, const char *, const char *, int * );
static void DistortMaps( AstFitsChan *, FitsStore *, char, int , AstMapping **, AstMapping **, AstMapping **, AstMapping **, const char *, const char *, int * );
static void Dump( AstObject *, AstChannel *, int * );
static void EmptyFits( AstFitsChan *, int * );
static void FindWcs( AstFitsChan *, int, int, int, const char *, const char *, int * );
static void FixNew( AstFitsChan *, int, int, const char *, const char *, int * );
static void FixUsed( AstFitsChan *, int, int, int, const char *, const char *, int * );
static void FormatCard( AstFitsChan *, char *, const char *, int * );
static void FreeItem( double ****, int * );
static void FreeItemC( char *****, int * );
static void GetFiducialNSC( AstWcsMap *, double *, double *, int * );
static void GetFiducialPPC( AstWcsMap *, double *, double *, int * );
static void GetNextData( AstChannel *, int, char **, char **, int * );
static void InsCard( AstFitsChan *, int, const char *, int, void *, const char *, const char *, const char *, int * );
static void MakeBanner( const char *, const char *, const char *, char [ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ], int * );
static void MakeIndentedComment( int, char, const char *, const char *, char [ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1], int * );
static void MakeIntoComment( AstFitsChan *, const char *, const char *, int * );
static void MakeInvertable( double **, int, double *, int * );
static void MarkCard( AstFitsChan *, int * );
static void NewCard( AstFitsChan *, const char *, int, const void *, const char *, int, int * );
static void PreQuote( const char *, char [ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ], int * );
static void PurgeWCS( AstFitsChan *, int * );
static void PutCards( AstFitsChan *, const char *, int * );
static void PutFits( AstFitsChan *, const char [ AST__FITSCHAN_FITSCARDLEN + 1 ], int, int * );
static void PutTable( AstFitsChan *, AstFitsTable *, const char *, int * );
static void PutTables( AstFitsChan *, AstKeyMap *, int * );
static void ReadFits( AstFitsChan *, int * );
static void ReadFromSource( AstFitsChan *, int * );
static void RemoveTables( AstFitsChan *, const char *, int * );
static void RetainFits( AstFitsChan *, int * );
static void RoundFString( char *, int, int * );
static void SetAlgCode( char *, const char *, int * );
static void SetAttrib( AstObject *, const char *, int * );
static void SetFitsCF( AstFitsChan *, const char *, double *, const char *, int, int * );
static void SetFitsCI( AstFitsChan *, const char *, int *, const char *, int, int * );
static void SetFitsCM( AstFitsChan *, const char *, int, int * );
static void SetFitsCN( AstFitsChan *, const char *, const char *, const char *, int, int * );
static void SetFitsCom( AstFitsChan *, const char *, const char *, int, int * );
static void SetFitsF( AstFitsChan *, const char *, double, const char *, int, int * );
static void SetFitsI( AstFitsChan *, const char *, int, const char *, int, int * );
static void SetFitsL( AstFitsChan *, const char *, int, const char *, int, int * );
static void SetFitsS( AstFitsChan *, const char *, const char *, const char *, int, int * );
static void SetFitsU( AstFitsChan *, const char *, const char *, int, int * );
static void SetItem( double ****, int, int, char, double, int * );
static void SetItemC( char *****, int, int, char, const char *, int * );
static void SetSourceFile( AstChannel *, const char *, int * );
static void SetValue( AstFitsChan *, const char *, void *, int, const char *, int * );
static void ShowFits( AstFitsChan *, int * );
static void Shpc1( double, double, int, double *, double *, int * );
static void SinkWrap( void (*)( const char * ), const char *, int * );
static void SkyPole( AstWcsMap *, AstMapping *, int, int, int *, char, FitsStore *, const char *, const char *, int * );
static void TableSource( AstFitsChan *, void (*)( AstFitsChan *, const char *, int, int, int * ), int * );
static void TidyOffsets( AstFrameSet *, int * );
static void Warn( AstFitsChan *, const char *, const char *, const char *, const char *, int * );
static void WcsFcRead( AstFitsChan *, AstFitsChan *, FitsStore *, const char *, const char *, int * );
static void WcsToStore( AstFitsChan *, AstFitsChan *, FitsStore *, 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 WriteFits( AstFitsChan *, 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 void WriteToSink( AstFitsChan *, int * );
static void SetTableSource( AstFitsChan *,
void (*)( void ),
void (*)( void (*)( void ),
AstFitsChan *, const char *, int, int, int * ), int * );
static void TabSourceWrap( void (*)( void ),
AstFitsChan *, const char *, int, int, int * );
#if defined(THREAD_SAFE)
static int ManageLock( AstObject *, int, int, AstObject **, int * );
#endif
/* Member functions. */
/* ================= */
static void AdaptLut( AstMapping *map, int npos, double eps, double x0,
double x1, double v0, double v1, double **xtab,
double **vtab, int *nsamp, int *status ){
/*
* Name:
* AdaptLut
* Purpose:
* Create a table of optimally sampled values for a Mapping.
* Type:
* Private function.
* Synopsis:
* void AdaptLut( AstMapping *map, int npos, double eps, double x0,
* double x1, double v0, double v1, double **xtab,
* double **vtab, int *nsamp, int *status )
* Class Membership:
* FitsChan
* Description:
* This function returns a look-up table holding samples of the supplied
* 1D mapping. The input values at which the samples are taken are
* returned in the "xtab" array, and the Mapping output values at
* these input values are returned in the "vtab" array. The sample
* spacing is smaller at positions where the output gradient is
* changing more rapidly (i.e. where the output is more non-linear).
* Parameters:
* map
* Pointer to the Mapping. Should have 1 input and 1 output.
* npos
* The minimum number of samples to place within the interval to be
* sampled, excluding the two end points (which are always sampeld
* anyway). These samples are placed evenly through the [x0,x1]
interval. The interval between adjacent samples will be further
* subdivided if necessary by calling this function recursively.
* eps
* The maximum error in X (i.e. the Mapping input) allowed before
* the supplied interval is subdivided further by a recursive call
* to this function.
* x0
* The Mapping input value at the start of the interval to be sampled.
* It is assumed that this value is already stored in (*xtab)[0] on
* entry.
* x1
* The Mapping input value at the end of the interval to be sampled.
* v0
* The Mapping output value at the start of the interval to be sampled.
* It is assumed that this value is already stored in (*vtab)[0] on
* entry.
* v1
* The Mapping output value at the end of the interval to be sampled.
* xtab
* Address of a pointer to the array in which to store the Mapping
* input values at which samples were taken. The supplied pointer
* may be changed on exit to point to a larger array. New values
* are added to the end of this array. The initial size of the array
* is given by the supplied value for "*nsamp"
* vtab
* Address of a pointer to the array in which to store the Mapping
* output value at each sample. The supplied pointer may be changed
* on exit to point to a larger array. New values are added to the
* end of this array. The initial size of the array is given by the
* supplied value for "*nsamp".
* nsamp
* Address of an int holding the number of values in the "*xtab"
* and "*ytab" arrays. Updated on exit to include the new values
* added to the arrays by this function.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The size of the returned xtab and vtab arrays.
*/
/* Local Variables: */
double *vv; /* Pointer to Mapping output values */
double *xx; /* Pointer to Mapping input values */
double dx; /* Step between sample positions */
double rg; /* Reciprocal of gradient of (x0,v0)->(x1,v1) line */
double xx0; /* X at first new sample position */
int ipos; /* Interior sample index */
int isamp; /* Index into extended xtab and vtab arrays. */
int subdivide; /* Subdivide each subinterval? */
/* Check the inherited status. */
if( !astOK ) return;
/* Allocate work space. */
xx = astMalloc( sizeof( double )*npos );
vv = astMalloc( sizeof( double )*npos );
if( astOK ) {
/* Set up the evenly spaced interior sample positions. */
dx = ( x1 - x0 )/( npos + 1 );
xx0 = x0 + dx;
for( ipos = 0; ipos < npos; ipos++ ) {
xx[ ipos ] = xx0 + ipos*dx;
}
/* Find the Mapping output values at these input values. */
astTran1( map, npos, xx, 1, vv );
/* See if any of these samples deviate significantly from the straight line
defined by (x0,v0) and (x1,v1). If any such sample is found, we call
this function recursively to sample the subdivided intervals. First
handle cases where the straight line has zero gradient. */
subdivide = 0;
if( v0 == v1 ) {
/* Subdivide if any of the interior sample values are different to the
end values. */
for( ipos = 0; ipos < npos; ipos++ ) {
if( vv[ ipos ] != v0 ) {
subdivide = 1;
break;
}
}
/* Now handle cases where the line has non-zero gradient. Subdivide if any
of the interior sample input positions are further than "eps" from the
input position that would give the same output value if the mapping was
linear. */
} else {
rg = ( x1 - x0 )/( v1 - v0 );
for( ipos = 0; ipos < npos; ipos++ ) {
if( vv[ ipos ] == AST__BAD ||
fabs( rg*( vv[ ipos ] - v0 ) - ( xx[ ipos ] - x0 ) ) > eps ) {
subdivide = 1;
break;
}
}
}
/* If required, call this function recursively to subdivide each section
of the supplied input interval, and append samples to the returned
arrays. */
if( subdivide ) {
/* Do each sub-interval, except the last one. The number of subintervals
is one more than the number of interior samples. */
for( ipos = 0; ipos < npos; ipos++ ) {
/* Append samples covering the current subinterval to the ends of the
arrays. */
AdaptLut( map, npos, eps, x0, xx[ ipos ], v0, vv[ ipos ],
xtab, vtab, nsamp, status );
/* Store the starting position for the next sub-interval. */
x0 = xx[ ipos ];
v0 = vv[ ipos ];
}
/* Now do the final sub-interval. */
AdaptLut( map, npos, eps, x0, x1, v0, v1, xtab, vtab, nsamp, status );
/* If we do not need to subdivide, store the samples in the returned
array, together with the supplied final point. */
} else {
/* Extend the arrays. */
isamp = *nsamp;
*nsamp += npos + 1;
*xtab = astGrow( *xtab, *nsamp, sizeof( double ) );
*vtab = astGrow( *vtab, *nsamp, sizeof( double ) );
if( astOK ) {
/* Store the sample positions and values at the end of the extended
arrays. */
for( ipos = 0; ipos < npos; ipos++, isamp++ ) {
(*xtab)[ isamp ] = xx[ ipos ];
(*vtab)[ isamp ] = vv[ ipos ];
}
(*xtab)[ isamp ] = x1;
(*vtab)[ isamp ] = v1;
}
}
}
/* Free resources. */
xx = astFree( xx );
vv= astFree( vv );
}
static int AddEncodingFrame( AstFitsChan *this, AstFrameSet *fs, int encoding,
const char *method, const char *class, int *status ){
/*
* Name:
* AddEncodingFrame
* Purpose:
* Add a Frame which conforms to the requirements of the specified encoding.
* Type:
* Private function.
* Synopsis:
* int AddEncodingFrame( AstFitsChan *this, AstFrameSet *fs, int encoding,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* This function attempts to create a Frame based on the current Frame
* of the supplied FrameSet, which conforms to the requirements of the
* specified Encoding. If created, this Frame is added into the
* FrameSet as the new current Frame, and the index of the original current
* Frame is returned.
* Parameters:
* this
* Pointer to the FitsChan.
* fs
* Pointer to the FrameSet.
* encoding
* The encoding in use.
* 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 original current Frame in the FrameSet. A value of
* AST__NOFRAME is returned if no new Frame is added to the FrameSet,
* or if an error occurs.
*/
/* Local Variables: */
AstCmpFrame *cmpfrm; /* Pointer to spectral cube frame */
AstFrame *cfrm; /* Pointer to original current Frame */
AstFrame *newfrm; /* Frame describing coord system to be used */
AstFrame *pfrm; /* Pointer to primary Frame containing axis */
AstFrameSet *fsconv; /* FrameSet converting what we have to what we want */
AstMapping *map; /* Mapping from what we have to what we want */
AstSkyFrame *skyfrm; /* Pointer to SkyFrame */
AstSpecFrame *specfrm; /* Pointer to SpecFrame */
AstSystemType sys; /* Frame coordinate system */
int i; /* Axis index */
int naxc; /* No. of axes in original current Frame */
int paxis; /* Axis index in primary frame */
int result; /* Returned value */
/* Initialise */
result = AST__NOFRAME;
/* Check the inherited status. */
if( !astOK ) return result;
/* Get a pointer to the current Frame and note how many axes it has. */
cfrm = astGetFrame( fs, AST__CURRENT );
naxc = astGetNaxes( cfrm );
/* FITS-CLASS */
/* ========== */
if( encoding == FITSCLASS_ENCODING ) {
/* Try to locate a SpecFrame and a SkyFrame in the current Frame. */
specfrm = NULL;
skyfrm = NULL;
for( i = 0; i < naxc; i++ ) {
astPrimaryFrame( cfrm, i, &pfrm, &paxis );
if( astIsASpecFrame( pfrm ) ) {
if( !specfrm ) specfrm = astCopy( pfrm );
} else if( IsASkyFrame( pfrm ) ) {
if( !skyfrm ) skyfrm = astCopy( pfrm );
}
pfrm = astAnnul( pfrm );
}
/* Cannot do anything if either is missing. */
if( specfrm && skyfrm ) {
/* If the spectral axis is not frequency, set it to frequency. Also set
spectral units of "Hz". */
sys = astGetSystem( specfrm );
if( sys != AST__FREQ ) {
astSetSystem( specfrm, AST__FREQ );
sys = AST__FREQ;
}
/* Ensure the standard of rest is Source and units are "Hz". */
astSetUnit( specfrm, 0, "Hz" );
astSetStdOfRest( specfrm, AST__SCSOR );
/* The celestial axes must be either FK4, FK5 or galactic. */
sys = astGetSystem( skyfrm );
if( sys != AST__FK4 && sys != AST__FK5 && sys != AST__GALACTIC ) {
astSetSystem( skyfrm, AST__FK5 );
sys = AST__FK5;
}
/* FK5 systems must be J2000, and FK4 must be B1950. */
if( sys == AST__FK5 ) {
astSetC( skyfrm, "Equinox", "J2000.0" );
} else if( sys == AST__FK4 ) {
astSetC( skyfrm, "Equinox", "B1950.0" );
}
/* Combine the spectral and celestial Frames into a single CmpFrame with
the spectral axis being the first axis. */
cmpfrm = astCmpFrame( specfrm, skyfrm, "", status );
/* Attempt to obtain the current Frame of the supplied FrameSet to this
new Frame. */
fsconv = astConvert( cfrm, cmpfrm, "" );
if( fsconv ) {
/* Get the Mapping and current Frame from the rconversion FrameSet. */
newfrm = astGetFrame( fsconv, AST__CURRENT );
map = astGetMapping( fsconv, AST__BASE, AST__CURRENT );
/* Save the original current Frame index. */
result = astGetCurrent( fs );
/* Add the new Frame into the supplied FrameSet using the above Mapping
to connect it to the original current Frame. The new Frame becomes the
current Frame. */
astAddFrame( fs, AST__CURRENT, map, newfrm );
/* Free resources */
map = astAnnul( map );
newfrm = astAnnul( newfrm );
fsconv = astAnnul( fsconv );
}
/* Free resources */
cmpfrm = astAnnul( cmpfrm );
}
/* Release resources. */
if( specfrm ) specfrm = astAnnul( specfrm );
if( skyfrm ) skyfrm = astAnnul( skyfrm );
}
/* Free reources. */
cfrm = astAnnul( cfrm );
/* Return the result */
return result;
}
static void AddFrame( AstFitsChan *this, AstFrameSet *fset, int pixel,
int npix, FitsStore *store, char s, const char *method,
const char *class, int *status ){
/*
* Name:
* AddFrame
* Purpose:
* Create a Frame describing a set of axes with a given co-ordinate
* version, and add it to the supplied FrameSet.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void AddFrame( AstFitsChan *this, AstFrameSet *fset, int pixel,
* int npix, FitsStore *store, char s, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* A Frame is created describing axis with a specific co-ordinate
* version character, reading information from the supplied FitsStore.
* A suitable Mapping is created to connect the new Frame to the pixel
* (GRID) Frame in the supplied FrameSet, and the Frame is added into
* the FrameSet using this Mapping.
* Parameters:
* this
* The FitsChan from which the keywords were read. Warning messages
* are added to this FitsChan if the celestial co-ordinate system is
* not recognized.
* fset
* Pointer to the FrameSet to be extended.
* pixel
* The index of the pixel (GRID) Frame within fset.
* npix
* The number of pixel axes.
* store
* The FitsStore containing the required information extracted from
* the FitsChan.
* s
* The co-ordinate version character. A space means the primary
* axis descriptions. Otherwise the supplied character should be
* an upper case alphabetical character ('A' to 'Z').
* 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.
*/
/* Local Variables: */
AstFrame *frame; /* Requested Frame */
AstMapping *mapping; /* Mapping from pixel to requested Frame */
AstMapping *tmap; /* Temporary Mapping pointer */
AstPermMap *pmap; /* PermMap pointer to add or remove axes */
double con; /* Value to be assigned to missing axes */
int *inperm; /* Pointer to input axis permutation array */
int *outperm; /* Pointer to output axis permutation array */
int i; /* Axis index */
int nwcs; /* Number of wcs axes */
/* Check the inherited status. */
if( !astOK ) return;
/* Get a Mapping between pixel coordinates and physical coordinates, using
the requested axis descriptions. Also returns a Frame describing the
physical coordinate system. */
mapping = WcsMapFrm( this, store, s, &frame, method, class, status );
/* Add the Frame into the FrameSet, and annul the mapping and frame. If
the new Frame has more axes than the pixel Frame, use a PermMap which
assigns constant value 1.0 to the extra axes. If the new Frame has less
axes than the pixel Frame, use a PermMap which throws away the extra
axes. */
if( mapping != NULL ) {
nwcs = astGetNin( mapping );
if( nwcs != npix ) {
inperm = astMalloc( sizeof(int)*(size_t)npix );
outperm = astMalloc( sizeof(int)*(size_t)nwcs );
if( astOK ) {
for( i = 0; i < npix; i++ ) {
inperm[ i ] = ( i < nwcs ) ? i : -1;
}
for( i = 0; i < nwcs; i++ ) {
outperm[ i ] = ( i < npix ) ? i : -1;
}
con = 1.0;
pmap = astPermMap( npix, inperm, nwcs, outperm, &con, "", status );
tmap = (AstMapping *) astCmpMap( pmap, mapping, 1, "", status );
pmap = astAnnul( pmap );
(void) astAnnul( mapping );
mapping = tmap;
}
inperm = astFree( inperm );
outperm = astFree( outperm );
}
astAddFrame( fset, pixel, mapping, frame );
/* Annul temporary resources. */
mapping = astAnnul( mapping );
}
frame = astAnnul( frame );
}
static int AddVersion( AstFitsChan *this, AstFrameSet *fs, int ipix, int iwcs,
FitsStore *store, double *dim, char s, int encoding,
int isoff, const char *method, const char *class,
int *status ){
/*
* Name:
* AddVersion
* Purpose:
* Add values to a FitsStore describing a specified Frame in a FrameSet.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int AddVersion( AstFitsChan *this, AstFrameSet *fs, int ipix, int iwcs,
* FitsStore *store, double *dim, char s, int encoding,
* int isoff, const char *method, const char *class,
* int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Values are added to the supplied FitsStore describing the specified
* WCS Frame, and its relationship to the specified pixel Frame. These
* values are based on the standard FITS-WCS conventions.
* Parameters:
* this
* Pointer to the FitsChan.
* fs
* Pointer to the FrameSet.
* ipix
* The index of the pixel (GRID) Frame within fset.
* iwcs
* The index of the Frame within fset to use as the WCS co-ordinate
* Frame.
* store
* The FitsStore in which to store the information extracted from
* the FrameSet.
* dim
* Pointer to an array of pixel axis dimensions. Individual elements
* will be AST__BAD if dimensions are not known. The number of
* elements should equal the number of axes in the base Frame of the
* supplied FrameSet.
* s
* The co-ordinate version character. A space means the primary
* axis descriptions. Otherwise the supplied character should be
* an upper case alphabetical character ('A' to 'Z').
* encoding
* The encoding being used.
* isoff
* If greater than zero, the Frame is an offset SkyFrame and the
* description added to the FitsStore should describe offset coordinates.
* If less than than zero, the Frame is an offset SkyFrame and the
* description added to the FitsStore should describe absolute coordinates.
* If zero, the Frame is an absolute SkyFrame and the description added
* to the FitsSTore should (by necessity) describe absolute coordinates.
* 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.
* Retuned Value:
* A value of 1 is returned if the WCS Frame was succesfully added to
* the FitsStore. A value of zero is returned otherwise.
*/
/* Local Variables: */
AstFrame *wcsfrm; /* WCS Frame */
AstFrameSet *fset; /* Temporary FrameSet */
AstMapping *iwcmap; /* Mapping from WCS to IWC Frame */
AstMapping *mapping; /* Mapping from pixel to WCS Frame */
AstMapping *pixiwcmap; /* Mapping from pixel to IWC Frame */
AstMapping *tmap2; /* Temporary Mapping */
AstMapping *tmap; /* Temporary Mapping */
const char *old_skyrefis;/* Old value of SkyRefIs attribute */
double *crvals; /* Pointer to array holding default CRVAL values */
double cdelt2; /* Sum of squared PC values */
double cdelt; /* CDELT value for axis */
double crpix; /* CRPIX value for axis */
double crval; /* CRVAL value for axis */
double pc; /* Element of the PC array */
int *axis_done; /* Flags indicating which axes have been done */
int *wperm; /* FITS axis for each Mapping output (Frame axis) */
int fits_i; /* FITS WCS axis index */
int fits_j; /* FITS pixel axis index */
int iax; /* Frame axis index */
int icurr; /* Index of current Frame */
int nwcs; /* No. of axes in WCS frame */
int ret; /* Returned value */
/* Initialise */
ret = 0;
/* Check the inherited status. */
if( !astOK ) return ret;
/* If the frame is a SkyFrame describing offset coordinates, but the
description added to the FitsStore should be for absolute coordinates,
temporarily clear the SkyFrame SkyRefIs attribute. We need to make it
the current Frame first so that we can use the FrameSet to clear the
attribte, so that the SkyFrame will be re-mapped within the FrameSet
to take account of the clearing. For negative isoff values, set the
specific negative value to indicate the original SkyRefIs value. */
if( isoff < 0 ) {
icurr = astGetCurrent( fs );
astSetCurrent( fs, iwcs );
old_skyrefis = astGetC( fs, "SkyRefIs" );
if( astOK ) {
if( !Ustrcmp( old_skyrefis, "POLE", status ) ) {
isoff = -1;
} else if( !Ustrcmp( old_skyrefis, "ORIGIN", status ) ) {
isoff = -2;
} else {
isoff = -3;
}
}
astClear( fs, "SkyRefIs" );
astSetCurrent( fs, icurr );
} else {
old_skyrefis = AST__BAD_REF;
}
/* Construct a new FrameSet holding the pixel and WCS Frames from the
supplied FrameSet, but in which the current Frame is a copy of the
supplied WCS Frame, but optionally extended to include any extra axes
needed to conform to the FITS model. For instance, if the WCS Frame
consists of a single 1D SpecFrame with a defined celestial reference
position (SpecFrame attributes RefRA and RefDec), then FITS-WCS paper
III requires there to be a pair of celestial axes in the WCS Frame in
which the celestial reference point for the spectral axis is defined. */
fset = MakeFitsFrameSet( this, fs, ipix, iwcs, encoding, method, class, status );
/* If required, re-instate the original value of the SkyRefIs attribute
in the supplied FrameSet. */
if( old_skyrefis != AST__BAD_REF ) {
astSetCurrent( fs, iwcs );
astSetC( fs, "SkyRefIs", old_skyrefis );
astSetCurrent( fs, icurr );
}
/* Abort if the FrameSet could not be produced. */
if( !fset ) return ret;
/* Get the Mapping from base to current Frame and check its inverse is
defined. Return if not. Note, we can handle non-invertable Mappings if
we are allowed to use the -TAB algorithm. */
mapping = astGetMapping( fset, AST__BASE, AST__CURRENT );
wcsfrm = astGetFrame( fset, AST__CURRENT );
if( !astGetTranInverse( mapping ) && astGetTabOK( this ) <= 0 ) {
mapping = astAnnul( mapping );
wcsfrm = astAnnul( wcsfrm );
fset = astAnnul( fset );
return ret;
}
/* We now need to choose the "FITS WCS axis" (i.e. the number that is included
in FITS keywords such as CRVAL2) for each axis of the output Frame.
Allocate memory to store these indices. */
nwcs= astGetNout( mapping );
wperm = astMalloc( sizeof(int)*(size_t) nwcs );
/* Attempt to use the FitsAxisOrder attribute to determine the order. If
this is set to "", then for each WCS axis, we use the index of
the pixel axis which is most closely aligned with it. */
if( !FitsAxisOrder( this, nwcs, wcsfrm, wperm, status ) &&
!WorldAxes( this, mapping, dim, wperm, status ) ) {
wperm = astFree( wperm );
mapping = astAnnul( mapping );
wcsfrm = astAnnul( wcsfrm );
fset = astAnnul( fset );
return ret;
}
/* Allocate an array of flags, one for each axis, which indicate if a
description of the corresponding axis has yet been stored in the
FitsStore. Initialise them to indicate that no axes have yet been
described. */
axis_done = astMalloc( sizeof(int)*(size_t) nwcs );
if( astOK ) for( iax = 0; iax < nwcs; iax++ ) axis_done[ iax ] = 0;
/* Get the original reference point from the FitsChan and convert it into
the require WCS Frame. This is used as the default reference point (some
algorithms may choose to ignore this default reference point ). */
crvals = ReadCrval( this, wcsfrm, s, method, class, status );
/* For each class of FITS conventions (celestial, spectral, others),
identify any corresponding axes within the WCS Frame and add
descriptions of them to the FitsStore. These descriptions are in terms
of the FITS keywords defined in the corresponding FITS-WCS paper. Note,
the keywords which descirbed the pixel->IWC mapping (CRPIX, CD, PC,
CDELT) are not stored by these functions, instead each function
returns a Mapping from WCS to IWC coords (these Mappings
pass on axes of the wrong class without change). These Mappings are
combined in series to get the final WCS->IWC Mapping. First do
celestial axes. */
iwcmap = CelestialAxes( this, fset, dim, wperm, s, store, axis_done,
isoff, method, class, status );
/* Now look for spectral axes, and update the iwcmap. */
tmap = SpectralAxes( this, fset, dim, wperm, s, store, crvals, axis_done,
method, class, status );
tmap2 = (AstMapping *) astCmpMap( iwcmap, tmap, 1, "", status );
tmap = astAnnul( tmap );
(void) astAnnul( iwcmap );
iwcmap = tmap2;
/* Finally add descriptions of any axes not yet described (they are
assumed to be linear), and update the iwcmap. */
tmap = OtherAxes( this, fset, dim, wperm, s, store, crvals, axis_done,
method, class, status );
tmap2 = (AstMapping *) astCmpMap( iwcmap, tmap, 1, "", status );
tmap = astAnnul( tmap );
(void) astAnnul( iwcmap );
iwcmap = tmap2;
/* The "iwcmap" Mapping found above converts from the WCS Frame to the IWC
Frame. Combine the pixel->WCS Mapping with this WCS->IWC Mapping to
get the pixel->IWC Mapping. */
pixiwcmap = (AstMapping *) astCmpMap( mapping, iwcmap, 1, "", status );
mapping = astAnnul( mapping );
iwcmap = astAnnul( iwcmap );
/* Now attempt to store values for the keywords describing the pixel->IWC
Mapping (CRPIX, CD, PC, CDELT). This tests that the iwcmap is linear.
Zero is returned if the test fails. */
ret = MakeIntWorld( pixiwcmap, wcsfrm, wperm, s, store, dim, method, class,
status );
/* If succesfull... */
if( ret ) {
/* Store the Domain name as the WCSNAME keyword (if set). */
if( astTestDomain( wcsfrm ) ) {
SetItemC( &(store->wcsname), 0, 0, s, (char *) astGetDomain( wcsfrm ),
status );
}
/* Store the UT1-UTC correction, if set, converting from seconds to days
(as used by JACH). */
if( astTestDut1( wcsfrm ) && s == ' ' ) {
SetItem( &(store->dut1), 0, 0, ' ', astGetDut1( wcsfrm )/SPD, status );
}
/* Set CRVAL values which are very small compared to the pixel size to
zero. */
for( iax = 0; iax < nwcs; iax++ ) {
fits_i = wperm[ iax ];
crval = GetItem( &(store->crval), fits_i, 0, s, NULL, method, class,
status );
if( crval != AST__BAD ) {
cdelt2 = 0.0;
for( fits_j = 0; fits_j < nwcs; fits_j++ ){
pc = GetItem( &(store->pc), fits_i, fits_j, s, NULL, method, class, status );
if( pc == AST__BAD ) pc = ( fits_i == fits_j ) ? 1.0 : 0.0;
cdelt2 += pc*pc;
}
cdelt = GetItem( &(store->cdelt), fits_i, 0, s, NULL, method, class, status );
if( cdelt == AST__BAD ) cdelt = 1.0;
cdelt2 *= ( cdelt*cdelt );
if( fabs( crval ) < sqrt( DBL_EPSILON*cdelt2 ) ) {
SetItem( &(store->crval), fits_i, 0, s, 0.0, status );
}
}
}
/* Round CRPIX values to the nearest millionth of a pixel. */
for( iax = 0; iax < nwcs; iax++ ) {
crpix = GetItem( &(store->crpix), 0, iax, s, NULL, method, class, status );
if( crpix != AST__BAD ) {
SetItem( &(store->crpix), 0, iax, s,
floor( crpix*1.0E6 + 0.5 )*1.0E-6, status );
}
}
}
/* Free remaining resources. */
if( crvals ) crvals = astFree( crvals );
wcsfrm = astAnnul( wcsfrm );
pixiwcmap = astAnnul( pixiwcmap );
axis_done = astFree( axis_done );
wperm = astFree( wperm );
fset = astAnnul( fset );
/* If an error has occurred, return zero */
return astOK ? ret : 0;
}
static AstMapping *AddUnitMaps( AstMapping *map, int iax, int nax, int *status ) {
/*
* Name:
* AddUnitMaps
* Purpose:
* Embed a Mapping within a pair of parallel UnitMaps.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstMapping *AddUnitMaps( AstMapping *map, int iax, int nax, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function returns a Mapping which consists of the supplied Mapping
* in parallel with a pair of UnitMaps so that the first axis of the
* supplied Mapping is at a specified axis number in the returned Mapping.
* Parameters:
* map
* Pointer to the Mapping. The Mapping must have equal numbers of
* input and output coordinates.
* iax
* The index for the first input of "map" within the returned
* Mapping.
* nax
* The number of axes for the returned Mapping.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A Mapping which has "nax" axes, and in which the "iax" axis
* corresponds to the first axis of "map". Axes lower than "iax" are
* transformed using a UnitMap, and axes higher than the last axis of
* "map" are transformed using a UnitMap.
*/
/* Local Variables: */
AstMapping *ret; /* Returned Mapping */
AstMapping *tmap0; /* Temporary Mapping */
AstMapping *tmap1; /* Temporary Mapping */
AstMapping *tmap2; /* Temporary Mapping */
int nmap; /* Number of supplied Mapping inputs */
/* Initialise */
ret = NULL;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Initialise the returned Mapping to be a clone of the supplied Mapping. */
ret = astClone( map );
/* Note the number of inputs of the supplied Mapping (assumed to be equal
to the number of outputs). */
nmap = astGetNin( map );
/* If necessary produce a parallel CmpMap which combines the Mapping with a
UnitMap representing the axes lower than "iax". */
if( iax > 0 ) {
tmap0 = (AstMapping *) astUnitMap( iax, "", status );
tmap1 = (AstMapping *) astCmpMap( tmap0, ret, 0, "", status );
ret = astAnnul( ret );
tmap0 = astAnnul( tmap0 );
ret = tmap1;
}
/* If necessary produce a parallel CmpMap which combines the Mapping with a
UnitMap representing the axes higher than "iax+nmap". */
if( iax + nmap < nax ) {
tmap1 = (AstMapping *) astUnitMap( nax - iax - nmap, "", status );
tmap2 = (AstMapping *) astCmpMap( ret, tmap1, 0, "", status );
ret = astAnnul( ret );
tmap1 = astAnnul( tmap1 );
ret = tmap2;
}
/* Return the result. */
return ret;
}
static int AIPSFromStore( AstFitsChan *this, FitsStore *store,
const char *method, const char *class, int *status ){
/*
* Name:
* AIPSFromStore
* Purpose:
* Store WCS keywords in a FitsChan using FITS-AIPS encoding.
* Type:
* Private function.
* Synopsis:
* int AIPSFromStore( AstFitsChan *this, FitsStore *store,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* A FitsStore is a structure containing a generalised represention of
* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
* from a set of FITS header cards (using a specified encoding), or
* an AST FrameSet. In other words, a FitsStore is an encoding-
* independant intermediary staging post between a FITS header and
* an AST FrameSet.
*
* This function copies the WCS information stored in the supplied
* FitsStore into the supplied FitsChan, using FITS-AIPS encoding.
*
* AIPS encoding is like FITS-WCS encoding but with the following
* restrictions:
*
* 1) The celestial projection must not have any projection parameters
* which are not set to their default values. The one exception to this
* is that SIN projections are acceptable if the associated projection
* parameter PV_1 is zero and PV_2 = cot( reference point
* latitude). This is encoded using the string "-NCP". The SFL projection
* is encoded using the string "-GLS". Note, the original AIPS WCS
* system only recognised a small subset of the currently available
* projections, but some more recent AIPS-like software recognizes some
* of the new projections included in the FITS-WCS encoding. The AIT,
* GLS and MER can only be written if the CRVAL keywords are zero for
* both longitude and latitude axes.
*
* 2) The celestial axes must be RA/DEC, galactic or ecliptic.
*
* 3) LONPOLE and LATPOLE must take their default values.
*
* 4) Only primary axis descriptions are written out.
*
* 5) EPOCH is written instead of EQUINOX & RADECSYS, and uses the
* IAU 1984 rule ( EPOCH < 1984.0 is treated as a Besselian epoch
* and implies RADECSYS=FK4, EPOCH >= 1984.0 is treated as a
* Julian epoch and implies RADECSYS=FK5). The RADECSYS & EQUINOX
* values in the FitsStore must be consistent with this rule.
*
* 6) Any rotation produced by the PC matrix must be restricted to
* the celestial plane, and must involve no shear. A CROTA keyword
* with associated CDELT values are produced instead of the PC
* matrix.
*
* 7) ICRS is not supported.
*
* 8) Spectral axes can be created only for FITS-WCS CTYPE values of "FREQ"
* "VRAD" and "VOPT-F2W" and with standards of rest of LSRK, LSRD,
* BARYCENT and GEOCENTR.
* Parameters:
* this
* Pointer to the FitsChan.
* store
* Pointer to the FitsStore.
* 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:
* A value of 1 is returned if succesfull, and zero is returned
* otherwise.
*/
/* Local Variables: */
char *comm; /* Pointer to comment string */
const char *cval; /* Pointer to string keyword value */
const char *specunit;/* Pointer to corrected spectral units string */
char combuf[80]; /* Buffer for FITS card comment */
char lattype[MXCTYPELEN];/* Latitude axis CTYPE */
char lontype[MXCTYPELEN];/* Longitude axis CTYPE */
char s; /* Co-ordinate version character */
char sign[2]; /* Fraction's sign character */
char spectype[MXCTYPELEN];/* Spectral axis CTYPE */
double *cdelt; /* Pointer to CDELT array */
double cdl; /* CDELT term */
double cdlat_lon; /* Off-diagonal CD element */
double cdlon_lat; /* Off-diagonal CD element */
double coscro; /* Cos( CROTA ) */
double crota; /* CROTA value to use */
double epoch; /* Epoch of reference equinox */
double fd; /* Fraction of a day */
double latval; /* CRVAL for latitude axis */
double lonval; /* CRVAL for longitude axis */
double mjd99; /* MJD at start of 1999 */
double p1, p2; /* Projection parameters */
double rho_a; /* First estimate of CROTA */
double rho_b; /* Second estimate of CROTA */
double sincro; /* Sin( CROTA ) */
double specfactor; /* Factor for converting internal spectral units */
double val; /* General purpose value */
int axlat; /* Index of latitude FITS WCS axis */
int axlon; /* Index of longitude FITS WCS axis */
int axrot1; /* Index of first CROTA rotation axis */
int axrot2; /* Index of second CROTA rotation axis */
int axspec; /* Index of spectral FITS WCS axis */
int i; /* Axis index */
int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */
int iymdf[ 4 ]; /* Year, month, date, fractional day */
int j; /* Axis index */
int jj; /* SlaLib status */
int naxis; /* No. of axes */
int ok; /* Is FitsSTore OK for IRAF encoding? */
int prj; /* Projection type */
/* Check the inherited status. */
if( !astOK ) return 0;
/* Initialise */
specunit = "";
specfactor = 1.0;
/* First check that the values in the FitsStore conform to the
requirements of the AIPS encoding. Assume they do to begin with. */
ok = 1;
/* Just do primary axes. */
s = ' ';
/* Look for the primary celestial axes. */
FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status );
/* If both longitude and latitude axes are present ...*/
if( axlon >= 0 && axlat >= 0 ) {
/* Get the CRVAL values for both axes. */
latval = GetItem( &( store->crval ), axlat, 0, s, NULL, method, class, status );
if( latval == AST__BAD ) ok = 0;
lonval = GetItem( &( store->crval ), axlon, 0, s, NULL, method, class, status );
if( lonval == AST__BAD ) ok = 0;
/* Get the CTYPE values for both axes. Extract the projection type as
specified by the last 4 characters in the latitude CTYPE keyword value. */
cval = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status );
if( !cval ) {
ok = 0;
} else {
strcpy( lontype, cval );
}
cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status );
if( !cval ) {
ok = 0;
prj = AST__WCSBAD;
} else {
strcpy( lattype, cval );
prj = astWcsPrjType( cval + 4 );
}
/* Check the projection type is OK. */
if( prj == AST__WCSBAD ){
ok = 0;
} else if( prj != AST__SIN ){
/* There must be no projection parameters. */
if( GetMaxJM( &(store->pv), ' ', status ) >= 0 ) {
ok = 0;
/* FITS-AIPS cannot handle the AST-specific TPN projection. */
} else if( prj == AST__TPN ) {
ok = 0;
/* For AIT, MER and GLS, check that the reference point is the origin of
the celestial co-ordinate system. */
} else if( prj == AST__MER ||
prj == AST__AIT ||
prj == AST__SFL ) {
if( latval != 0.0 || lonval != 0.0 ){
ok = 0;
/* Change the new SFL projection code to to the older equivalent GLS */
} else if( prj == AST__SFL ){
(void) strcpy( lontype + 4, "-GLS" );
(void) strcpy( lattype + 4, "-GLS" );
}
}
/* SIN projections are only acceptable if the associated projection
parameters are both zero, or if the first is zero and the second
= cot( reference point latitude ) (the latter case is equivalent to
the old NCP projection). */
} else {
p1 = GetItem( &( store->pv ), axlat, 1, s, NULL, method, class, status );
p2 = GetItem( &( store->pv ), axlat, 2, s, NULL, method, class, status );
if( p1 == AST__BAD ) p1 = 0.0;
if( p2 == AST__BAD ) p2 = 0.0;
ok = 0;
if( p1 == 0.0 ) {
if( p2 == 0.0 ) {
ok = 1;
} else if( fabs( p2 ) >= 1.0E14 && latval == 0.0 ){
ok = 1;
(void) strcpy( lontype + 4, "-NCP" );
(void) strcpy( lattype + 4, "-NCP" );
} else if( fabs( p2*tan( AST__DD2R*latval ) - 1.0 )
< 0.01 ){
ok = 1;
(void) strcpy( lontype + 4, "-NCP" );
(void) strcpy( lattype + 4, "-NCP" );
}
}
}
/* Identify the celestial coordinate system from the first 4 characters of the
longitude CTYPE value. Only RA, galactic longitude, and ecliptic
longitude can be stored using FITS-AIPS. */
if( ok && strncmp( lontype, "RA--", 4 ) &&
strncmp( lontype, "GLON", 4 ) &&
strncmp( lontype, "ELON", 4 ) ) ok = 0;
/* If the physical Frame requires a LONPOLE or LATPOLE keyword, it cannot
be encoded using FITS-IRAF. */
if( GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status )
!= AST__BAD ||
GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status )
!= AST__BAD ) ok = 0;
}
/* If a spectral axis is present ...*/
if( ok && axspec >= 0 ) {
/* Get the CTYPE values for the axis, and find the AIPS equivalent, if
possible. */
cval = GetItemC( &(store->ctype), axspec, 0, s, NULL, method, class, status );
if( !cval ) {
ok = 0;
} else {
if( !strncmp( cval, "FREQ", astChrLen( cval ) ) ) {
strcpy( spectype, "FREQ" );
} else if( !strncmp( cval, "VRAD", astChrLen( cval ) ) ) {
strcpy( spectype, "VELO" );
} else if( !strncmp( cval, "VOPT-F2W", astChrLen( cval ) ) ) {
strcpy( spectype, "FELO" );
} else {
ok = 0;
}
}
/* If OK, check the SPECSYS value and add the AIPS equivalent onto the
end of the CTYPE value.*/
cval = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status );
if( !cval ) {
ok = 0;
} else if( ok ) {
if( !strncmp( cval, "LSRK", astChrLen( cval ) ) ) {
strcpy( spectype+4, "-LSR" );
} else if( !strncmp( cval, "LSRD", astChrLen( cval ) ) ) {
strcpy( spectype+4, "-LSD" );
} else if( !strncmp( cval, "BARYCENT", astChrLen( cval ) ) ) {
strcpy( spectype+4, "-HEL" );
} else if( !strncmp( cval, "GEOCENTR", astChrLen( cval ) ) ) {
strcpy( spectype+4, "-GEO" );
} else {
ok = 0;
}
}
/* If still OK, ensure the spectral axis units are Hz or m/s. */
cval = GetItemC( &(store->cunit), axspec, 0, s, NULL, method, class, status );
if( !cval ) {
ok = 0;
} else if( ok ) {
if( !strcmp( cval, "Hz" ) ) {
specunit = "HZ";
specfactor = 1.0;
} else if( !strcmp( cval, "kHz" ) ) {
specunit = "HZ";
specfactor = 1.0E3;
} else if( !strcmp( cval, "MHz" ) ) {
specunit = "HZ";
specfactor = 1.0E6;
} else if( !strcmp( cval, "GHz" ) ) {
specunit = "HZ";
specfactor = 1.0E9;
} else if( !strcmp( cval, "m/s" ) ) {
specunit = "m/s";
specfactor = 1.0;
} else if( !strcmp( cval, "km/s" ) ) {
specunit = "m/s";
specfactor = 1.0E3;
} else {
ok = 0;
}
}
}
/* Save the number of axes */
naxis = GetMaxJM( &(store->crpix), ' ', status ) + 1;
/* If this is different to the value of NAXIS abort since this encoding
does not support WCSAXES keyword. */
if( naxis != store->naxis ) ok = 0;
/* Allocate memory to store the CDELT values */
if( ok ) {
cdelt = (double *) astMalloc( sizeof(double)*naxis );
if( !cdelt ) ok = 0;
} else {
cdelt = NULL;
}
/* Check that rotation is restricted to the celestial plane, and extract
the CDELT (diagonal) terms, etc. If there are no celestial
axes, restrict rotation to the first two non-spectral axes. */
if( axlat < 0 && axlon < 0 ) {
if( axspec >= 0 && naxis > 2 ) {
axrot2 = ( axspec == 0 ) ? 1 : 0;
axrot1 = axrot2 + 1;
if( axrot1 == axspec ) axrot1++;
} else if( naxis > 1 ){
axrot2 = 0;
axrot1 = 1;
} else {
axrot2 = -1;
axrot1 = -1;
}
} else {
axrot1 = axlon;
axrot2 = axlat;
}
cdlat_lon = 0.0;
cdlon_lat = 0.0;
for( i = 0; i < naxis && ok; i++ ){
cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
if( cdl == AST__BAD ) cdl = 1.0;
for( j = 0; j < naxis && ok; j++ ){
val = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0;
val *= cdl;
if( i == j ){
cdelt[ i ] = val;
} else if( i == axrot2 && j == axrot1 ){
cdlat_lon = val;
} else if( i == axrot1 && j == axrot2 ){
cdlon_lat = val;
} else if( val != 0.0 ){
ok = 0;
}
}
}
/* Find the CROTA and CDELT values for the celestial axes. */
if( ok && axrot1 >= 0 && axrot2 >= 0 ) {
if( cdlat_lon > 0.0 ) {
rho_a = atan2( cdlat_lon, cdelt[ axrot1 ] );
} else if( cdlat_lon == 0.0 ) {
rho_a = 0.0;
} else {
rho_a = atan2( -cdlat_lon, -cdelt[ axrot1 ] );
}
if( cdlon_lat > 0.0 ) {
rho_b = atan2( cdlon_lat, -cdelt[ axrot2 ] );
} else if( cdlon_lat == 0.0 ) {
rho_b = 0.0;
} else {
rho_b = atan2( -cdlon_lat, cdelt[ axrot2 ] );
}
if( fabs( palDrange( rho_a - rho_b ) ) < 1.0E-2 ){
crota = 0.5*( palDranrm( rho_a ) + palDranrm( rho_b ) );
coscro = cos( crota );
sincro = sin( crota );
if( fabs( coscro ) > fabs( sincro ) ){
cdelt[ axrot2 ] /= coscro;
cdelt[ axrot1 ] /= coscro;
} else {
cdelt[ axrot2 ] = -cdlon_lat/sincro;
cdelt[ axrot1 ] = cdlat_lon/sincro;
}
crota *= AST__DR2D;
} else {
ok = 0;
}
} else {
crota = 0.0;
}
/* Get RADECSYS and the reference equinox (called EPOCH in FITS-AIPS). */
cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status );
epoch = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status );
/* If RADECSYS was available... */
if( cval ){
/* ICRS is not supported in this encoding. */
if( !strcmp( "ICRS", cval ) ) ok = 0;
/* If epoch was not available, set a default epoch. */
if( epoch == AST__BAD ){
if( !strcmp( "FK4", cval ) ){
epoch = 1950.0;
} else if( !strcmp( "FK5", cval ) ){
epoch = 2000.0;
} else {
ok = 0;
}
/* If an epoch was supplied, check it is consistent with the IAU 1984
rule. */
} else {
if( !strcmp( "FK4", cval ) ){
if( epoch >= 1984.0 ) ok = 0;
} else if( !strcmp( "FK5", cval ) ){
if( epoch < 1984.0 ) ok = 0;
} else {
ok = 0;
}
}
}
/* Only create the keywords if the FitsStore conforms to the requirements
of the FITS-AIPS encoding. */
if( ok ) {
/* Get and save CRPIX for all pixel axes. These are required, so break
if they are not available. */
for( j = 0; j < naxis && ok; j++ ){
val = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status );
if( val == AST__BAD ) {
ok = 0;
} else {
sprintf( combuf, "Reference pixel on axis %d", j + 1 );
SetValue( this, FormatKey( "CRPIX", j + 1, -1, s, status ), &val,
AST__FLOAT, combuf, status );
}
}
/* Get and save CRVAL for all intermediate axes. These are required, so
break if they are not available. */
for( i = 0; i < naxis && ok; i++ ){
val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
if( val == AST__BAD ) {
ok = 0;
} else {
if( i == axspec ) val *= specfactor;
sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 );
SetValue( this, FormatKey( "CRVAL", i + 1, -1, s, status ), &val,
AST__FLOAT, combuf, status );
}
}
/* Get and save CTYPE for all intermediate axes. These are required, so
break if they are not available. Use the potentially modified versions
saved above for the celestial axes. */
for( i = 0; i < naxis && ok; i++ ){
if( i == axlat ) {
cval = lattype;
} else if( i == axlon ) {
cval = lontype;
} else if( i == axspec ) {
cval = spectype;
} else {
cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
}
if( cval && ( strlen(cval) < 5 || strcmp( cval + 4, "-TAB" ) ) ) {
comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status );
if( !comm ) {
sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 );
comm = combuf;
}
SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval,
AST__STRING, comm, status );
} else {
ok = 0;
}
}
/* CDELT values */
if( axspec != -1 ) cdelt[ axspec ] *= specfactor;
for( i = 0; i < naxis; i++ ){
SetValue( this, FormatKey( "CDELT", i + 1, -1, s, status ), cdelt + i,
AST__FLOAT, "Pixel size", status );
}
/* CUNIT values. */
for( i = 0; i < naxis; i++ ) {
cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status );
if( cval ) {
if( i == axspec ) cval = specunit;
sprintf( combuf, "Units for axis %d", i + 1 );
SetValue( this, FormatKey( "CUNIT", i + 1, -1, s, status ), &cval, AST__STRING,
combuf, status );
}
}
/* CROTA */
if( axrot2 != -1 ){
SetValue( this, FormatKey( "CROTA", axrot2 + 1, -1, s, status ), &crota,
AST__FLOAT, "Axis rotation", status );
} else if( ( axspec == -1 && naxis > 1 ) ||
( axspec != -1 && naxis > 2 ) ) {
SetValue( this, "CROTA1", &crota, AST__FLOAT, "Axis rotation", status );
}
/* Reference equinox */
if( epoch != AST__BAD ) SetValue( this, "EPOCH", &epoch, AST__FLOAT,
"Epoch of reference equinox", status );
/* Date of observation. */
val = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, method, class, status );
if( val != AST__BAD ) {
/* The format used for the DATE-OBS keyword depends on the value of the
keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format.
Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */
palCaldj( 99, 1, 1, &mjd99, &jj );
if( val < mjd99 ) {
palDjcal( 0, val, iymdf, &jj );
sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ],
iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) );
} else {
palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj );
palDd2tf( 3, fd, sign, ihmsf );
sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d",
iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1],
ihmsf[2], ihmsf[3] );
}
/* Now store the formatted string in the FitsChan. */
cval = combuf;
SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING,
"Date of observation", status );
}
/* Spectral stuff.. */
if( axspec >= 0 ) {
/* Rest frequency */
val = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, FormatKey( "RESTFREQ", -1, -1, s, status ),
&val, AST__FLOAT, "[Hz] Rest frequency", status );
}
}
/* Release CDELT workspace */
if( cdelt ) cdelt = (double *) astFree( (void *) cdelt );
/* Return zero or ret depending on whether an error has occurred. */
return astOK ? ok : 0;
}
static int AIPSPPFromStore( AstFitsChan *this, FitsStore *store,
const char *method, const char *class, int *status ){
/*
* Name:
* AIPSPPFromStore
* Purpose:
* Store WCS keywords in a FitsChan using FITS-AIPS++ encoding.
* Type:
* Private function.
* Synopsis:
* int AIPSPPFromStore( AstFitsChan *this, FitsStore *store,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* A FitsStore is a structure containing a generalised represention of
* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
* from a set of FITS header cards (using a specified encoding), or
* an AST FrameSet. In other words, a FitsStore is an encoding-
* independant intermediary staging post between a FITS header and
* an AST FrameSet.
*
* This function copies the WCS information stored in the supplied
* FitsStore into the supplied FitsChan, using FITS-AIPS++ encoding.
*
* AIPS++ encoding is like FITS-WCS encoding but with the following
* restrictions:
*
* 1) The celestial axes must be RA/DEC, galactic or ecliptic.
*
* 2) Only primary axis descriptions are written out.
*
* 3) RADESYS is not written and so the RADECSYS & EQUINOX values in the
* FitsStore must be consistent with the "1984" rule.
*
* 4) Any rotation produced by the PC matrix must be restricted to
* the celestial plane, and must involve no shear. A CROTA keyword
* with associated CDELT values are produced instead of the PC
* matrix.
*
* 5) ICRS is not supported.
*
* 6) Spectral axes can be created only for FITS-WCS CTYPE values of "FREQ"
* "VRAD" and "VOPT-F2W" and with standards of rest of LSRK, LSRD,
* BARYCENT and GEOCENTR.
* Parameters:
* this
* Pointer to the FitsChan.
* store
* Pointer to the FitsStore.
* 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:
* A value of 1 is returned if succesfull, and zero is returned
* otherwise.
*/
/* Local Variables: */
char *comm; /* Pointer to comment string */
const char *cval; /* Pointer to string keyword value */
const char *specunit;/* Pointer to corrected spectral units string */
char combuf[80]; /* Buffer for FITS card comment */
char lattype[MXCTYPELEN];/* Latitude axis CTYPE */
char lontype[MXCTYPELEN];/* Longitude axis CTYPE */
char s; /* Co-ordinate version character */
char sign[2]; /* Fraction's sign character */
char spectype[MXCTYPELEN];/* Spectral axis CTYPE */
double *cdelt; /* Pointer to CDELT array */
double cdl; /* CDELT term */
double cdlat_lon; /* Off-diagonal CD element */
double cdlon_lat; /* Off-diagonal CD element */
double coscro; /* Cos( CROTA ) */
double crota; /* CROTA value to use */
double epoch; /* Epoch of reference equinox */
double fd; /* Fraction of a day */
double mjd99; /* MJD at start of 1999 */
double rho_a; /* First estimate of CROTA */
double rho_b; /* Second estimate of CROTA */
double sincro; /* Sin( CROTA ) */
double specfactor; /* Factor for converting internal spectral units */
double val; /* General purpose value */
int axlat; /* Index of latitude FITS WCS axis */
int axlon; /* Index of longitude FITS WCS axis */
int axrot1; /* Index of first CROTA rotation axis */
int axrot2; /* Index of second CROTA rotation axis */
int axspec; /* Index of spectral FITS WCS axis */
int i; /* Axis index */
int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */
int iymdf[ 4 ]; /* Year, month, date, fractional day */
int j; /* Axis index */
int jj; /* SlaLib status */
int m; /* Projection parameter index */
int maxm; /* Max projection parameter index */
int naxis; /* No. of axes */
int ok; /* Is FitsSTore OK for IRAF encoding? */
int prj; /* Projection type */
/* Check the inherited status. */
if( !astOK ) return 0;
/* Initialise */
specunit = "";
specfactor = 1.0;
maxm = 0;
/* First check that the values in the FitsStore conform to the
requirements of the AIPS++ encoding. Assume they do to begin with. */
ok = 1;
/* Just do primary axes. */
s = ' ';
/* Save the number of axes */
naxis = GetMaxJM( &(store->crpix), ' ', status ) + 1;
/* Look for the primary celestial and spectral axes. */
FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status );
/* If both longitude and latitude axes are present ...*/
if( axlon >= 0 && axlat >= 0 ) {
/* Get the CTYPE values for both axes. Extract the projection type as
specified by the last 4 characters in the latitude CTYPE keyword value. */
cval = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status );
if( !cval ) {
ok = 0;
} else {
strcpy( lontype, cval );
}
cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status );
if( !cval ) {
ok = 0;
prj = AST__WCSBAD;
} else {
strcpy( lattype, cval );
prj = astWcsPrjType( cval + 4 );
}
/* FITS-AIPS++ cannot handle the AST-specific TPN projection. */
if( prj == AST__TPN || prj == AST__WCSBAD ) ok = 0;
/* Projection parameters. FITS-AIPS++ encoding ignores projection parameters
associated with the longitude axis. The number of parameters is limited to
10. */
maxm = GetMaxJM( &(store->pv), ' ', status );
for( i = 0; i < naxis && ok; i++ ){
if( i != axlon ) {
for( m = 0; m <= maxm; m++ ){
val = GetItem( &(store->pv), i, m, s, NULL, method, class, status );
if( val != AST__BAD ) {
if( i != axlat || m >= 10 ){
ok = 0;
break;
}
}
}
}
}
/* Identify the celestial coordinate system from the first 4 characters of the
longitude CTYPE value. Only RA, galactic longitude, and ecliptic
longitude can be stored using FITS-AIPS++. */
if( ok && strncmp( lontype, "RA--", 4 ) &&
strncmp( lontype, "GLON", 4 ) &&
strncmp( lontype, "ELON", 4 ) ) ok = 0;
}
/* If a spectral axis is present ...*/
if( axspec >= 0 ) {
/* Get the CTYPE values for the axis, and find the AIPS equivalent, if
possible. */
cval = GetItemC( &(store->ctype), axspec, 0, s, NULL, method, class, status );
if( !cval ) {
ok = 0;
} else {
if( !strncmp( cval, "FREQ", astChrLen( cval ) ) ) {
strcpy( spectype, "FREQ" );
} else if( !strncmp( cval, "VRAD", astChrLen( cval ) ) ) {
strcpy( spectype, "VELO" );
} else if( !strncmp( cval, "VOPT-F2W", astChrLen( cval ) ) ) {
strcpy( spectype, "FELO" );
} else {
ok = 0;
}
}
/* If OK, check the SPECSYS value and add the AIPS equivalent onto the
end of the CTYPE value.*/
cval = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status );
if( !cval ) {
ok = 0;
} else {
if( !strncmp( cval, "LSRK", astChrLen( cval ) ) ) {
strcpy( spectype+4, "-LSR" );
} else if( !strncmp( cval, "LSRD", astChrLen( cval ) ) ) {
strcpy( spectype+4, "-LSD" );
} else if( !strncmp( cval, "BARYCENT", astChrLen( cval ) ) ) {
strcpy( spectype+4, "-HEL" );
} else if( !strncmp( cval, "GEOCENTR", astChrLen( cval ) ) ) {
strcpy( spectype+4, "-GEO" );
} else {
ok = 0;
}
}
/* If still OK, ensure the spectral axis units are Hz or m/s. */
cval = GetItemC( &(store->cunit), axspec, 0, s, NULL, method, class, status );
if( !cval ) {
ok = 0;
} else if( ok ) {
if( !strcmp( cval, "Hz" ) ) {
specunit = "HZ";
specfactor = 1.0;
} else if( !strcmp( cval, "kHz" ) ) {
specunit = "HZ";
specfactor = 1.0E3;
} else if( !strcmp( cval, "MHz" ) ) {
specunit = "HZ";
specfactor = 1.0E6;
} else if( !strcmp( cval, "GHz" ) ) {
specunit = "HZ";
specfactor = 1.0E9;
} else if( !strcmp( cval, "m/s" ) ) {
specunit = "m/s";
specfactor = 1.0;
} else if( !strcmp( cval, "km/s" ) ) {
specunit = "m/s";
specfactor = 1.0E3;
} else {
ok = 0;
}
}
}
/* If this is different to the value of NAXIS abort since this encoding
does not support WCSAXES keyword. */
if( naxis != store->naxis ) ok = 0;
/* Allocate memory to store the CDELT values */
if( ok ) {
cdelt = (double *) astMalloc( sizeof(double)*naxis );
if( !cdelt ) ok = 0;
} else {
cdelt = NULL;
}
/* Check that rotation is restricted to the celestial plane, and extract
the CDELT (diagonal) terms, etc. If there are no celestial
axes, restrict rotation to the first two non-spectral axes. */
if( axlat < 0 && axlon < 0 ) {
if( axspec >= 0 && naxis > 2 ) {
axrot2 = ( axspec == 0 ) ? 1 : 0;
axrot1 = axrot2 + 1;
if( axrot1 == axspec ) axrot1++;
} else if( naxis > 1 ){
axrot2 = 0;
axrot1 = 1;
} else {
axrot2 = -1;
axrot1 = -1;
}
} else {
axrot1 = axlon;
axrot2 = axlat;
}
cdlat_lon = 0.0;
cdlon_lat = 0.0;
for( i = 0; i < naxis && ok; i++ ){
cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
if( cdl == AST__BAD ) cdl = 1.0;
for( j = 0; j < naxis && ok; j++ ){
val = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0;
val *= cdl;
if( i == j ){
cdelt[ i ] = val;
} else if( i == axrot2 && j == axrot1 ){
cdlat_lon = val;
} else if( i == axrot1 && j == axrot2 ){
cdlon_lat = val;
} else if( val != 0.0 ){
ok = 0;
}
}
}
/* Find the CROTA and CDELT values for the celestial axes. */
if( ok && axrot1 >= 0 && axrot2 >= 0 ) {
if( cdlat_lon > 0.0 ) {
rho_a = atan2( cdlat_lon, cdelt[ axrot1 ] );
} else if( cdlat_lon == 0.0 ) {
rho_a = 0.0;
} else {
rho_a = atan2( -cdlat_lon, -cdelt[ axrot1 ] );
}
if( cdlon_lat > 0.0 ) {
rho_b = atan2( cdlon_lat, -cdelt[ axrot2 ] );
} else if( cdlon_lat == 0.0 ) {
rho_b = 0.0;
} else {
rho_b = atan2( -cdlon_lat, cdelt[ axrot2 ] );
}
if( fabs( palDrange( rho_a - rho_b ) ) < 1.0E-2 ){
crota = 0.5*( palDranrm( rho_a ) + palDranrm( rho_b ) );
coscro = cos( crota );
sincro = sin( crota );
if( fabs( coscro ) > fabs( sincro ) ){
cdelt[ axrot2 ] /= coscro;
cdelt[ axrot1 ] /= coscro;
} else {
cdelt[ axrot2 ] = -cdlon_lat/sincro;
cdelt[ axrot1 ] = cdlat_lon/sincro;
}
crota *= AST__DR2D;
/* Use AST__BAD to indicate that CDi_j values should be produced
instead of CROTA/CDELT. (I am told AIPS++ can understand CD matrices) */
} else {
crota = AST__BAD;
}
} else {
crota = 0.0;
}
/* Get RADECSYS and the reference equinox. */
cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status );
epoch = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status );
/* If RADECSYS was available... */
if( cval ){
/* ICRS is not supported in this encoding. */
if( !strcmp( "ICRS", cval ) ) ok = 0;
/* If epoch was not available, set a default epoch. */
if( epoch == AST__BAD ){
if( !strcmp( "FK4", cval ) ){
epoch = 1950.0;
} else if( !strcmp( "FK5", cval ) ){
epoch = 2000.0;
} else {
ok = 0;
}
/* If an equinox was supplied, check it is consistent with the IAU 1984
rule. */
} else {
if( !strcmp( "FK4", cval ) ){
if( epoch >= 1984.0 ) ok = 0;
} else if( !strcmp( "FK5", cval ) ){
if( epoch < 1984.0 ) ok = 0;
} else {
ok = 0;
}
}
}
/* Only create the keywords if the FitsStore conforms to the requirements
of the FITS-AIPS++ encoding. */
if( ok ) {
/* Get and save CRPIX for all pixel axes. These are required, so break
if they are not available. */
for( j = 0; j < naxis && ok; j++ ){
val = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status );
if( val == AST__BAD ) {
ok = 0;
} else {
sprintf( combuf, "Reference pixel on axis %d", j + 1 );
SetValue( this, FormatKey( "CRPIX", j + 1, -1, s, status ), &val,
AST__FLOAT, combuf, status );
}
}
/* Get and save CRVAL for all intermediate axes. These are required, so
break if they are not available. */
for( i = 0; i < naxis && ok; i++ ){
val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
if( val == AST__BAD ) {
ok = 0;
} else {
if( i == axspec ) val *= specfactor;
sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 );
SetValue( this, FormatKey( "CRVAL", i + 1, -1, s, status ), &val,
AST__FLOAT, combuf, status );
}
}
/* Get and save CTYPE for all intermediate axes. These are required, so
break if they are not available. Use the potentially modified versions
saved above for the celestial axes. */
for( i = 0; i < naxis && ok; i++ ){
if( i == axlat ) {
cval = lattype;
} else if( i == axlon ) {
cval = lontype;
} else if( i == axspec ) {
cval = spectype;
} else {
cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
}
if( cval && ( strlen(cval) < 5 || strcmp( cval + 4, "-TAB" ) ) ) {
comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status );
if( !comm ) {
sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 );
comm = combuf;
}
SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval,
AST__STRING, comm, status );
} else {
ok = 0;
}
}
/* CDELT values */
if( axspec != -1 ) cdelt[ axspec ] *= specfactor;
for( i = 0; i < naxis; i++ ){
SetValue( this, FormatKey( "CDELT", i + 1, -1, s, status ), cdelt + i,
AST__FLOAT, "Pixel size", status );
}
/* CUNIT values. [Spectral axis units should be upper-case] */
for( i = 0; i < naxis; i++ ) {
cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status );
if( cval ) {
if( i == axspec ) cval = specunit;
sprintf( combuf, "Units for axis %d", i + 1 );
SetValue( this, FormatKey( "CUNIT", i + 1, -1, s, status ), &cval, AST__STRING,
combuf, status );
}
}
/* CD matrix. Multiply the row of the PC matrix by the CDELT value. */
if( crota == AST__BAD ) {
for( i = 0; i < naxis; i++ ) {
cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
if( cdl == AST__BAD ) cdl = 1.0;
for( j = 0; j < naxis; j++ ){
val = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0;
val *= cdl;
if( val != 0.0 ) {
SetValue( this, FormatKey( "CD", i + 1, j + 1, s, status ), &val,
AST__FLOAT, "Transformation matrix element", status );
}
}
}
/* CROTA */
} else if( crota != 0.0 ) {
if( axrot2 != -1 ){
SetValue( this, FormatKey( "CROTA", axrot2 + 1, -1, s, status ), &crota,
AST__FLOAT, "Axis rotation", status );
} else if( ( axspec == -1 && naxis > 1 ) ||
( axspec != -1 && naxis > 2 ) ) {
SetValue( this, "CROTA1", &crota, AST__FLOAT, "Axis rotation", status );
}
}
/* Reference equinox */
if( epoch != AST__BAD ) SetValue( this, "EPOCH", &epoch, AST__FLOAT,
"Epoch of reference equinox", status );
/* Latitude of native north pole. */
val = GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, "LATPOLE", &val, AST__FLOAT,
"Latitude of native north pole", status );
/* Longitude of native north pole. */
val = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, "LONPOLE", &val, AST__FLOAT,
"Longitude of native north pole", status );
/* Date of observation. */
val = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, method, class, status );
if( val != AST__BAD ) {
/* The format used for the DATE-OBS keyword depends on the value of the
keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format.
Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */
palCaldj( 99, 1, 1, &mjd99, &jj );
if( val < mjd99 ) {
palDjcal( 0, val, iymdf, &jj );
sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ],
iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) );
} else {
palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj );
palDd2tf( 3, fd, sign, ihmsf );
sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d",
iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1],
ihmsf[2], ihmsf[3] );
}
/* Now store the formatted string in the FitsChan. */
cval = combuf;
SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING,
"Date of observation", status );
}
/* Projection parameters. */
if( axlat >= 0 && axlon >= 0 ) {
for( m = 0; m <= maxm; m++ ){
val = GetItem( &(store->pv), axlat, m, s, NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, FormatKey( "PROJP", m, -1, ' ', status ),
&val, AST__FLOAT, "Projection parameter", status );
}
}
/* Spectral stuff.. */
if( axspec >= 0 ) {
/* Rest frequency */
val = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, FormatKey( "RESTFREQ", -1, -1, s, status ),
&val, AST__FLOAT, "[Hz] Rest frequency", status );
}
}
/* Release CDELT workspace */
if( cdelt ) cdelt = (double *) astFree( (void *) cdelt );
/* Return zero or ret depending on whether an error has occurred. */
return astOK ? ok : 0;
}
static char *CardComm( AstFitsChan *this, int *status ){
/*
* Name:
* CardComm
* Purpose:
* Return the keyword comment from the current card.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* char *CardComm( AstFitsChan *this, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Returns a pointer to a string holding the keyword comment from the
* current card.
* Parameters:
* this
* Pointer to the FitsChan.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the keyword comment, or NULL if the FitsChan is at
* end-of-file, or does not have a comment.
* Notes:
* - The current card is not changed by this function.
* - This function attempts to execute even if an error has occurred.
*/
/* Local Variables: */
char *ret;
/* Check the supplied object. */
if( !this ) return NULL;
/* If the current card is defined, store a pointer to its keyword comment. */
if( this->card ){
ret = ( (FitsCard *) this->card )->comment;
/* Otherwise store a NULL pointer. */
} else {
ret = NULL;
}
/* Return the answer. */
return ret;
}
static void *CardData( AstFitsChan *this, size_t *size, int *status ){
/*
* Name:
* CardData
* Purpose:
* Return a pointer to the keyword data value for the current card.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void *CardData( AstFitsChan *this, size_t *size, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Returns a pointer to keyword data value from the current card.
* Parameters:
* this
* Pointer to the FitsChan.
* size
* A pointer to a location at which to return the number of bytes
* occupied by the data value. NULL can be supplied if this
* information is not required.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the keyword data, or NULL if the FitsChan is at
* end-of-file, or if the keyword does not have any data.
* Notes:
* - For text data, the returned value for "size" includes the
* terminating null character.
* - The current card is not changed by this function.
* - This function attempts to execute even if an error has occurred.
*/
/* Local Variables: */
void *ret;
/* Check the supplied object. */
if( !this ) return NULL;
/* If the current card is defined, store a pointer to its keyword data. */
if( this->card ){
ret = ( (FitsCard *) this->card )->data;
if( size ) *size = ( (FitsCard *) this->card )->size;
/* Otherwise store a NULL pointer. */
} else {
ret = NULL;
if( size ) *size = 0;
}
/* Return the answer. */
return ret;
}
static int *CardFlags( AstFitsChan *this, int *status ){
/*
* Name:
* CardFlags
* Purpose:
* Return a pointer to the flags mask for the current card.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int *CardFlags( AstFitsChan *this, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Returns a pointer to the flags mask for the current card.
* Parameters:
* this
* Pointer to the FitsChan.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The pointer to the flags mask.
* Notes:
* - The current card is not changed by this function.
* - NULL is returned if the current card is not defined.
* - This function attempts to execute even if an error has occurred.
*/
/* Local Variables: */
int *ret;
/* Check the supplied object. */
if( !this ) return NULL;
/* If the current card is defined, store its deletion flag. */
if( this->card ){
ret = &( ( (FitsCard *) this->card )->flags );
/* Otherwise store zero. */
} else {
ret = NULL;
}
/* Return the answer. */
return ret;
}
static char *CardName( AstFitsChan *this, int *status ){
/*
* Name:
* CardName
* Purpose:
* Return the keyword name from the current card.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* char *CardName( AstFitsChan *this, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Returns a pointer to a string holding the keyword name from the
* current card.
* Parameters:
* this
* Pointer to the FitsChan.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the keyword name, or NULL if the FitsChan is at
* end-of-file.
* Notes:
* - The current card is not changed by this function.
* - This function attempts to execute even if an error has occurred.
*/
/* Local Variables: */
char *ret;
/* Check the supplied object. */
if( !this ) return NULL;
/* If the current card is defined, store a pointer to its keyword name. */
if( this->card ){
ret = ( (FitsCard *) this->card )->name;
/* Otherwise store a NULL pointer. */
} else {
ret = NULL;
}
/* Return the answer. */
return ret;
}
static int CardType( AstFitsChan *this, int *status ){
/*
* Name:
* CardType
* Purpose:
* Return the keyword type from the current card.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int CardType( AstFitsChan *this, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Returns the keyword type from the current card.
* Parameters:
* this
* Pointer to the FitsChan.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The keyword type.
* Notes:
* - The current card is not changed by this function.
* - AST__NOTYPE is returned if the current card is not defined.
* - This function attempts to execute even if an error has occurred.
*/
/* Local Variables: */
int ret;
/* Check the supplied object. */
if( !this ) return AST__NOTYPE;
/* If the current card is defined, store the keyword type. */
if( this->card ){
ret = ( (FitsCard *) this->card )->type;
/* Otherwise store AST__NOTYPE. */
} else {
ret = AST__NOTYPE;
}
/* Return the answer. */
return ret;
}
static AstMapping *CelestialAxes( AstFitsChan *this, AstFrameSet *fs, double *dim,
int *wperm, char s, FitsStore *store, int *axis_done,
int isoff, const char *method, const char *class, int *status ){
/*
* Name:
* CelestialAxes
* Purpose:
* Add values to a FitsStore describing celestial axes in a Frame.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstMapping *CelestialAxes( AstFitsChan *this, AstFrameSet *fs, double *dim,
* int *wperm, char s, FitsStore *store, int *axis_done,
* int isoff, const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* The current Frame of the supplied FrameSet is searched for celestial
* axes. If any are found, FITS WCS keyword values describing the axis
* are added to the supplied FitsStore, if possible (the conventions
* of FITS-WCS paper II are used). Note, this function does not store
* values for keywords which define the transformation from pixel
* coords to Intermediate World Coords (CRPIX, PC and CDELT), but a
* Mapping is returned which embodies these values. This Mapping is
* from the current Frame in the FrameSet (WCS coords) to a Frame
* representing IWC. The IWC Frame has the same number of axes as the
* WCS Frame which may be greater than the number of base Frame (i.e.
* pixel) axes.
* Parameters:
* this
* Pointer to the FitsChan.
* fs
* Pointer to the FrameSet. The base Frame should represent FITS pixel
* coordinates, and the current Frame should represent FITS WCS
* coordinates. The number of base Frame axes should not exceed the
* number of current Frame axes.
* dim
* An array holding the image dimensions in pixels. AST__BAD can be
* supplied for any unknown dimensions.
* wperm
* Pointer to an array of integers with one element for each axis of
* the current Frame. Each element holds the zero-based
* index of the FITS-WCS axis (i.e. the value of "i" in the keyword
* names "CTYPEi", "CRVALi", etc) which describes the Frame axis.
* s
* The co-ordinate version character. A space means the primary
* axis descriptions. Otherwise the supplied character should be
* an upper case alphabetical character ('A' to 'Z').
* store
* The FitsStore in which to store the FITS WCS keyword values.
* axis_done
* An array of flags, one for each Frame axis, which indicate if a
* description of the corresponding axis has yet been stored in the
* FitsStore.
* isoff
* If greater than zero, the description to add to the FitsStore
* should describe offset coordinates. If less than zero, the
* description to add to the FitsStore should describe absolute
* coordinates but should include the SkyRefIs, SkyRef and SkyRefP
* attributes. If zero, ignore all offset coordinate info. The
* absolute value indicates the nature of the reference point:
* 1 == "pole", 2 == "origin", otherwise "ignored".
* 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:
* If celestial axes were found which can be described using the
* conventions of FITS-WCS paper II, then a Mapping from the current Frame
* of the supplied FrameSet, to the IWC Frame is returned. Otherwise,
* a UnitMap is returned. Note, the Mapping only defines the IWC
* transformation for celestial axes. Any non-celestial axes are passed
* unchanged by the returned Mapping.
*/
/* Local Variables: */
AstFitsTable *table; /* Pointer to structure holding -TAB table info */
AstFrame *pframe; /* Primary Frame containing current WCS axis*/
AstFrame *wcsfrm; /* WCS Frame within FrameSet */
AstMapping *map1; /* Pointer to pre-WcsMap Mapping */
AstMapping *map3; /* Pointer to post-WcsMap Mapping */
AstMapping *map; /* Pixel -> WCS mapping */
AstMapping *ret; /* Returned Mapping */
AstMapping *tmap0; /* A temporary Mapping */
AstMapping *tmap1; /* A temporary Mapping */
AstMapping *tmap2; /* A temporary Mapping */
AstMapping *tmap3; /* A temporary Mapping */
AstMapping *tmap4; /* A temporary Mapping */
AstSkyFrame *skyfrm; /* The SkyFrame defining current WCS axis */
AstWcsMap *map2; /* Pointer to WcsMap */
AstWcsMap *map2b; /* Pointer to WcsMap with cleared lat/lonpole */
char *cval; /* Pointer to keyword value */
char *temp; /* Pointer to temporary string */
double *mat; /* Pointer to matrix diagonal elements */
double *ppcfid; /* Pointer to array holding PPC at fiducial point */
double con; /* Constant value for unassigned axes */
double crval[ 2 ]; /* Psi coords of reference point */
double pv; /* Projection parameter value */
double skyfid[ 2 ]; /* Sky coords of fiducial point */
double val; /* Keyword value */
int *inperm; /* Input axis permutation array */
int *outperm; /* Output axis permutation array */
int *tperm; /* Pointer to new FITS axis numbering array */
int axlat; /* Index of latitude output from WcsMap */
int axlon; /* Index of longitude output from WcsMap */
int extver; /* Table version number for -TAB headers */
int fits_ilat; /* FITS WCS axis index for latitude axis */
int fits_ilon; /* FITS WCS axis index for longitude axis */
int i; /* Loop index */
int iax; /* Axis index */
int icolindexlat; /* Index of table column holding lat index vector */
int icolindexlon; /* Index of table column holding lon index vector */
int icolmainlat; /* Index of table column holding main lat coord array */
int icolmainlon; /* Index of table column holding main lon coord array */
int interplat; /* INterpolation method for latitude look-up tables */
int interplon; /* INterpolation method for longitude look-up tables */
int ilat; /* Index of latitude axis within total WCS Frame */
int ilon; /* Index of longitude axis within total WCS Frame */
int j; /* Loop index */
int m; /* Projection parameter index */
int maxm; /* Largest used "m" value */
int mlat; /* Index of latitude axis in main lat coord array */
int mlon; /* Index of longitude axis in main lon coord array */
int nwcs; /* Number of WCS axes */
int nwcsmap; /* Number of inputs/outputs for the WcsMap */
int paxis; /* Axis index within primary Frame */
int skylataxis; /* Index of latitude axis within SkyFrame */
int skylonaxis; /* Index of longitude axis within SkyFrame */
int tpn; /* Is the WCS projectiona TPN projection? */
/* Initialise */
ret = NULL;
/* Other initialisation to avoid compiler warnings. */
mlon = 0;
mlat = 0;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Get a pointer to the WCS Frame. */
wcsfrm = astGetFrame( fs, AST__CURRENT );
/* Store the number of WCS axes. */
nwcs = astGetNout( fs );
/* Check each axis in the WCS Frame to see if it is a celestial axis. */
skyfrm = NULL;
map = NULL;
ilon = -1;
ilat = -1;
for( iax = 0; iax < nwcs; iax++ ) {
/* Obtain a pointer to the primary Frame containing the current WCS axis. */
astPrimaryFrame( wcsfrm, iax, &pframe, &paxis );
/* If the current axis belongs to a SkyFrame, we have found a celestial
axis. Keep a pointer to it, and note the indices of the celestial axes
within the complete WCS Frame. The MakeFitsFrameSet function will have
ensured that the WCS Frame only contains at most a single SkyFrame. */
if( IsASkyFrame( pframe ) ) {
if( !skyfrm ) skyfrm = astClone( pframe );
if( paxis == 0 ) {
ilon = iax;
} else {
ilat = iax;
}
/* Indicate that this axis has been classified. */
axis_done[ iax ] = 1;
}
/* Release resources. */
pframe = astAnnul( pframe );
}
/* Only proceed if we found celestial axes. */
if( ilon != -1 && ilat != -1 ) {
/* Note the FITS WCS axis indices for the longitude and latitude axes */
fits_ilon = wperm[ ilon ];
fits_ilat = wperm[ ilat ];
/* Create an array to hold the Projection Plane Coords corresponding to the
CRVALi keywords. */
ppcfid = (double *) astMalloc( sizeof( double )*nwcs );
/* Get the pixel->wcs Mapping. */
map = astGetMapping( fs, AST__BASE, AST__CURRENT );
/* Get the table version number to use if we end up using the -TAB
algorithm. This is the set value of the TabOK attribute (if positive). */
extver = astGetTabOK( this );
/* Some of the required FITS Keyword values are defined by the WcsMap
contained within the Mapping. Split the mapping up into a list of serial
component mappings, and locate the first WcsMap in this list. The first
Mapping returned by this call is the result of compounding all the
Mappings up to (but not including) the WcsMap, the second returned Mapping
is the (inverted) WcsMap, and the third returned Mapping is anything
following the WcsMap. Only proceed if one and only one WcsMap is found. */
if( SplitMap( map, astGetInvert( map ), ilon, ilat, &map1, &map2, &map3,
status ) ){
/* Get the indices of the latitude and longitude axes within the SkyFrame
(not necessarily (1,0) because they may have been permuted). */
skylataxis = astGetLatAxis( skyfrm );
skylonaxis = astGetLonAxis( skyfrm );
/* The reference point in the celestial coordinate system is found by
transforming the fiducial point in native spherical co-ordinates
into WCS coordinates using map3. */
if( GetFiducialWCS( map2, map3, ilon, ilat, skyfid + skylonaxis,
skyfid + skylataxis, status ) ){
/* We also need to find the indices of the longitude and latitude outputs
from the WcsMap. These may not be the same as ilat and ilon because of
axis permutations in "map3". */
axlon = astGetWcsAxis( map2, 0 );
axlat = astGetWcsAxis( map2, 1 );
/* Normalise the latitude and longitude values at the fiducial point. The
longitude and latitude values found above will be in radians, but after
normalization we convert them to degrees, as expected by other functions
which handle FitsStores. */
if( skyfid[ skylonaxis ] == AST__BAD ) skyfid[ skylonaxis ] = 0.0;
if( skyfid[ skylataxis ] == AST__BAD ) skyfid[ skylataxis ] = 0.0;
if( ZEROANG( skyfid[ 0 ] ) ) skyfid[ 0 ] = 0.0;
if( ZEROANG( skyfid[ 1 ] ) ) skyfid[ 1 ] = 0.0;
astNorm( skyfrm, skyfid );
SetItem( &(store->crval), fits_ilon, 0, s, AST__DR2D*skyfid[ skylonaxis ], status );
SetItem( &(store->crval), fits_ilat, 0, s, AST__DR2D*skyfid[ skylataxis ], status );
/* Set a flag if we have a TPN projection. This is an AST-specific
projection which mimicks the old "TAN with correction terms" projection
which was removed from the final version of the FITS-WCS paper II. */
tpn = ( astGetWcsType( map2 ) == AST__TPN );
/* Store the WCS projection parameters. Except for TPN projections, always
exclude parameters 3 and 4 on the longitude axis since these are
reserved to hold copies of LONPOLE and LATPOLE. */
for( m = 0; m < WCSLIB_MXPAR; m++ ){
if( astTestPV( map2, axlon, m ) ) {
if( m < 3 || m > 4 || tpn ) {
pv = astGetPV( map2, axlon, m );
if( pv != AST__BAD ) SetItem( &(store->pv), fits_ilon, m,
s, pv, status );
}
}
if( astTestPV( map2, axlat, m ) ) {
pv = astGetPV( map2, axlat, m );
if( pv != AST__BAD ) SetItem( &(store->pv), fits_ilat, m,
s, pv, status );
}
}
/* If PVi_0 (for the longitude axis) is non-zero, the Cartesian coordinates
used by the WcsMap (Projection Plane Coordinates, PPC) need to be shifted
to produce Intermediate World Coordinates (IWC). This shift results in
the pixel reference position specified by the CRPIXi values (and which
corresponds to the origin of IWC) mapping on to the fiducial position
specified by the CRVALi values. The required shifts are just the PPC
coordinates of the fiducial point. The AST-specific "TPN" projection uses
longitude projection parameters to define correction terms, and so cannot
use the above convention (which is part of FITS-WCS paper II). Therefore
TPN projections always use zero shift between PPC and IWC. */
for( iax = 0; iax < nwcs; iax++ ) ppcfid[ iax ] = 0.0;
if( !tpn && astGetPV( map2, axlon, 0 ) != 0.0 ) {
GetFiducialPPC( (AstWcsMap *) map2, ppcfid + ilon, ppcfid + ilat, status );
if( ppcfid[ ilon ] == AST__BAD ) ppcfid[ ilon ] = 0.0;
if( ppcfid[ ilat ] == AST__BAD ) ppcfid[ ilat ] = 0.0;
ppcfid[ ilon ] *= AST__DR2D;
ppcfid[ ilat ] *= AST__DR2D;
}
/* Store the CTYPE, CNAME, EQUINOX, MJDOBS, and RADESYS values. */
SkySys( this, skyfrm, 1, astGetWcsType( map2 ), store, fits_ilon,
fits_ilat, s, isoff, method, class, status );
/* Store the LONPOLE and LATPOLE values in the FitsStore. */
SkyPole( map2, map3, ilon, ilat, wperm, s, store, method, class, status );
/* The values of LONPOLE and LATPOLE stored above (in the FitsStore) will be
ignored by WcsNative if the WcsMap contains set values for projection
parameters PVi_3a and/or PVi_4a (these will be used in preference to
the values in the FitsStore). To avoid this happening we take a copy
of the WcsMap and clear the relevant parameters (but not if the WcsMap is
for a TPN projection because TPN uses PVi_3a and PVi_4a for other
purposes). */
if( astGetWcsType( map2 ) != AST__TPN ) {
map2b = astCopy( map2 );
astClearPV( map2b, axlon, 3 );
astClearPV( map2b, axlon, 4 );
} else {
map2b = astClone( map2 );
}
/* We will now create the Mapping from WCS coords to IWC coords. In fact,
we produce the Mapping from IWC to WCS and then invert it. Create the
first component of this Mapping which implements any shift of origin
from IWC to PPC. */
tmap0 = (AstMapping *) astShiftMap( nwcs, ppcfid, "", status );
/* The next component of this Mapping scales the PPC coords from degrees
to radians on the celestial axes. */
mat = astMalloc( sizeof( double )*(size_t) nwcs );
if( astOK ) {
for( iax = 0; iax < nwcs; iax++ ) mat[ iax ] = 1.0;
mat[ ilon ] = AST__DD2R;
mat[ ilat ] = AST__DD2R;
tmap1 = (AstMapping *) astMatrixMap( nwcs, nwcs, 1, mat, "", status );
mat = astFree( mat );
} else {
tmap1 = NULL;
}
/* Now create the Mapping from Native Spherical Coords to WCS. */
tmap2 = WcsNative( NULL, store, s, map2b, fits_ilon, fits_ilat,
method, class, status );
/* Combine the WcsMap with the above Mapping, to get the Mapping from PPC
to WCS. */
tmap3 = (AstMapping *) astCmpMap( map2b, tmap2, 1, "", status );
tmap2 = astAnnul( tmap2 );
/* If there are more WCS axes than IWC axes, create a UnitMap for the extra
WCS axes and add it in parallel with tmap3. */
nwcsmap = astGetNin( map3 );
if( nwcsmap < nwcs ) {
tmap2 = (AstMapping *) astUnitMap( nwcs - nwcsmap, "", status );
tmap4 = (AstMapping *) astCmpMap( tmap3, tmap2, 0, "", status );
tmap3 = astAnnul( tmap3 );
tmap2 = astAnnul( tmap2 );
tmap3 = tmap4;
nwcsmap = nwcs;
}
/* The pixel->wcs mapping may include a PermMap which selects some sub-set
or super-set of the orignal WCS axes. In this case the number of inputs
and outputs for "tmap3" created above may not equal "nwcs". To avoid this,
we embed "tmap3" between 2 PermMaps which select the required axes. */
if( nwcsmap != nwcs || ilon != axlon || ilat != axlat ) {
inperm = astMalloc( sizeof( int )*(size_t) nwcs );
outperm = astMalloc( sizeof( int )*(size_t) nwcsmap );
if( astOK ) {
/* Indicate that no inputs of the PermMap have yet been assigned to any
outputs */
for( i = 0; i < nwcs; i++ ) inperm[ i ] = -1;
/* Assign the WcsMap long/lat axes to the WCS Frame long/lat axes */
inperm[ ilon ] = axlon;
inperm[ ilat ] = axlat;
/* Assign the remaining inputs arbitrarily (doesn't matter how we do this
since the WcsMap is effectively a UnitMap on all non-celestial axes). */
iax = 0;
for( i = 0; i < nwcs; i++ ) {
while( iax == axlon || iax == axlat ) iax++;
if( inperm[ i ] == -1 ) inperm[ i ] = iax++;
}
/* Do the same for the outputs. */
for( i = 0; i < nwcsmap; i++ ) outperm[ i ] = -1;
outperm[ axlon ] = ilon;
outperm[ axlat ] = ilat;
iax = 0;
for( i = 0; i < nwcsmap; i++ ) {
while( iax == ilon || iax == ilat ) iax++;
if( outperm[ i ] == -1 ) outperm[ i ] = iax++;
}
/* Create the PermMap. */
con = AST__BAD;
tmap2 = (AstMapping *) astPermMap( nwcs, inperm, nwcsmap,
outperm, &con, "", status );
/* Sandwich the WcsMap between the PermMap and its inverse. */
tmap4 = (AstMapping *) astCmpMap( tmap2, tmap3, 1, "", status );
tmap3 = astAnnul( tmap3 );
astInvert( tmap2 );
tmap3 = (AstMapping *) astCmpMap( tmap4, tmap2, 1, "", status );
tmap2 = astAnnul( tmap2 );
tmap4 = astAnnul( tmap4 );
}
inperm = astFree( inperm );
outperm = astFree( outperm );
}
/* Combine these Mappings together. */
tmap4 = (AstMapping *) astCmpMap( tmap0, tmap1, 1, "", status );
tmap0 = astAnnul( tmap0 );
tmap1 = astAnnul( tmap1 );
ret = (AstMapping *) astCmpMap( tmap4, tmap3, 1, "", status );
tmap3 = astAnnul( tmap3 );
tmap4 = astAnnul( tmap4 );
/* Invert this Mapping to get the Mapping from WCS to IWC. */
astInvert( ret );
/* The spherical rotation involved in converting WCS to IWC can result in
inappropriate numbering of the FITS axes. For instance, a LONPOLE
value of 90 degrees causes the IWC axes to be transposed. For this
reason we re-asses the FITS axis numbers assigned to the celestial
axes in order to make the IWC axes as close as possible to the pixel
axes with the same number (but only if the axis order is being
determined automatically). To do this, we need the Mapping from
pixel to IWC, which is formed by concatenating the pixel->WCS
Mapping with the WCS->IWC Mapping. */
if( astChrMatch( astGetFitsAxisOrder( this ), "" ) ) {
tmap0 = (AstMapping *) astCmpMap( map, ret, 1, "", status );
/* Find the outputs of this Mapping which should be associated with each
input. */
tperm = astMalloc( sizeof(int)*(size_t) nwcs );
if( ! WorldAxes( this, tmap0, dim, tperm, status ) ) {
ret = astAnnul( ret );
}
/* If the index associated with the celestial axes appear to have been
swapped... */
if( ret && astOK && fits_ilon == tperm[ ilat ] &&
fits_ilat == tperm[ ilon ] ) {
/* Swap the fits axis indices associated with each WCS axis to match. */
wperm[ ilon ] = fits_ilat;
wperm[ ilat ] = fits_ilon;
/* Swap the stored CRVAL value for the longitude and latitude axis. */
val = GetItem( &(store->crval), fits_ilat, 0, s, NULL, method, class, status );
SetItem( &(store->crval), fits_ilat, 0, s,
GetItem( &(store->crval), fits_ilon, 0, s, NULL,
method, class, status ), status );
SetItem( &(store->crval), fits_ilon, 0, s, val, status );
/* Swap the stored CTYPE value for the longitude and latitude axis. */
cval = GetItemC( &(store->ctype), fits_ilat, 0, s, NULL, method, class, status );
if( cval ) {
temp = astStore( NULL, (void *) cval, strlen( cval ) + 1 );
cval = GetItemC( &(store->ctype), fits_ilon, 0, s, NULL, method, class, status );
if( cval ) {
SetItemC( &(store->ctype), fits_ilat, 0, s, cval, status );
SetItemC( &(store->ctype), fits_ilon, 0, s, temp, status );
}
temp = astFree( temp );
}
/* Swap the stored CNAME value for the longitude and latitude axis. */
cval = GetItemC( &(store->cname), fits_ilat, 0, s, NULL, method, class, status );
if( cval ) {
temp = astStore( NULL, (void *) cval, strlen( cval ) + 1 );
cval = GetItemC( &(store->cname), fits_ilon, 0, s, NULL, method, class, status );
if( cval ) {
SetItemC( &(store->cname), fits_ilat, 0, s, cval, status );
SetItemC( &(store->cname), fits_ilon, 0, s, temp, status );
}
temp = astFree( temp );
}
/* Swap the projection parameters asociated with the longitude and latitude
axes. */
maxm = GetMaxJM( &(store->pv), s, status );
for( m = 0; m <= maxm; m++ ){
val = GetItem( &(store->pv), fits_ilat, m, s, NULL, method, class, status );
SetItem( &(store->pv), fits_ilat, m, s,
GetItem( &(store->pv), fits_ilon, m, s, NULL,
method, class, status ), status );
SetItem( &(store->pv), fits_ilon, m, s, val, status );
}
}
/* Release resources. */
tperm = astFree( tperm );
tmap0 = astAnnul( tmap0 );
}
map2b = astAnnul( map2b );
}
/* Release resources. */
map1 = astAnnul( map1 );
map2 = astAnnul( map2 );
map3 = astAnnul( map3 );
/* If no WcsMap was found in the pixel->WCS Mapping, it may be possible
to describe the celestial axes using a tabular look-up table (i.e. the
FITS-WCS "_TAB" algorithm). Only do this if the -TAB algorithm is to
be supported. */
} else if( extver > 0 ) {
/* Get any pre-existing FitsTable from the FitsStore. This is the table
in which the tabular data will be stored (if the Mapping can be expressed
in -TAB form). */
if( !astMapGet0A( store->tables, AST_TABEXTNAME, &table ) ) table = NULL;
/* See if the transformations for the celestial axes can be expressed in -TAB
form. The returned Mapping (if any) is the Mapping from (lon,lat)
(rads) to (psi_lon,psi_lat) (pixels). See FITS-WCS paper III section 6.1.2
for definition of psi. Scale the values stored in the table from radians
to degrees. */
tmap0 = IsMapTab2D( map, AST__DR2D, "deg", wcsfrm, dim, ilon, ilat,
fits_ilon, fits_ilat, &table, &icolmainlon,
&icolmainlat, &icolindexlon, &icolindexlat,
&mlon, &mlat, &interplon, &interplat, status );
if( tmap0 ) {
/* Store the CTYPE, CNAME, EQUINOX, MJDOBS, and RADESYS values. */
SkySys( this, skyfrm, 0, 0, store, fits_ilon, fits_ilat, s, isoff,
method, class, status );
/* If possible, choose the two CRVAL values (which are values on the psi
axes) so that transforming them using the Mapping returned by
IsMapTab2D gives the sky reference position stored in the SkyFrame.
Check the SkyFrame has a defined reference position. */
if( astTestSkyRef( skyfrm, 0 ) && astTestSkyRef( skyfrm, 1 ) ){
/* Get the longitude and latitude at the reference point in radians. */
skyfid[ 0 ] = astGetSkyRef( skyfrm, astGetLonAxis( skyfrm ));
skyfid[ 1 ] = astGetSkyRef( skyfrm, astGetLatAxis( skyfrm ));
/* We use the WCS->psi Mapping to convert the reference point WCS coords
(rads) into psi coords (pixels). We can only do this if the WCS->psi
Mapping has a defined forward transformation. */
if( astGetTranForward( tmap0 ) ) {
astTran2( tmap0, 1, skyfid, skyfid + 1, 1, crval,
crval + 1 );
/* If the WCS->psi mapping has an undefined forward transformation, then
just store the sky reference point coords (in degs) in keywords
AXREFn, and use 1.0 for the CRVAL values, so that IWC becomes equal
to (psi-1) i.e. (grid coords - 1). This means the reference point is
at grid coords (1.0,1.0). Note this choice of 1.0 for CRVAL is not
arbitrary since it is required by the trick used to create invertable CD
matrix in function MakeInvertable. */
} else {
SetItem( &(store->axref), fits_ilon, 0, s,
AST__DR2D*skyfid[ 0 ], status );
SetItem( &(store->axref), fits_ilat, 0, s,
AST__DR2D*skyfid[ 1 ], status );
crval[ 0 ] = 1.0;
crval[ 1 ] = 1.0;
}
/* If the SkyFrame has no reference position, use 1.0 for the CRVAL values. */
} else {
crval[ 0 ] = 1.0;
crval[ 1 ] = 1.0;
}
/* Create a Mapping that describes the transformation from the lon and lat
psi axes to the lon and lat IWC axes (i.e. a ShiftMap that just subtracts
the CRVAL values from each axis). */
crval[ 0 ] = -crval[ 0 ];
crval[ 1 ] = -crval[ 1 ];
tmap1 = (AstMapping *) astShiftMap( 2, crval, " ", status );
crval[ 0 ] = -crval[ 0 ];
crval[ 1 ] = -crval[ 1 ];
/* Create a series compound Mapping that applies the Mapping returned
by IsMapTab2D first (the Mapping from WCS to psi), followed by the
Mapping from psi to IWC created above. There-after, use this compound
Mapping in place of the Mapping returned by IsMapTab2D. It maps WCS to
IWC. */
tmap2 = (AstMapping *) astCmpMap( tmap0, tmap1, 1, " ", status );
(void) astAnnul( tmap0 );
tmap1 = astAnnul( tmap1 );
tmap0 = tmap2;
/* Store the CRVAL values */
SetItem( &(store->crval), fits_ilon, 0, s, crval[ 0 ], status );
SetItem( &(store->crval), fits_ilat, 0, s, crval[ 1 ], status );
/* Store TAB-specific values in the FitsStore. First the name of the
FITS binary table extension holding the coordinate info. */
SetItemC( &(store->ps), fits_ilon, 0, s, AST_TABEXTNAME, status );
SetItemC( &(store->ps), fits_ilat, 0, s, AST_TABEXTNAME, status );
/* Next the table version number. This is the set (positive) value for the
TabOK attribute. */
SetItem( &(store->pv), fits_ilon, 1, s, extver, status );
SetItem( &(store->pv), fits_ilat, 1, s, extver, status );
/* Also store the table version in the binary table header. */
astSetFitsI( table->header, "EXTVER", extver, "Table version number",
0 );
/* Next the name of the table column containing the main coords array. */
SetItemC( &(store->ps), fits_ilon, 1, s,
astColumnName( table, icolmainlon ), status );
SetItemC( &(store->ps), fits_ilat, 1, s,
astColumnName( table, icolmainlat ), status );
/* Next the name of the column containing the index array. */
if( icolindexlon >= 0 ) SetItemC( &(store->ps), fits_ilon, 2, s,
astColumnName( table, icolindexlon ), status );
if( icolindexlat >= 0 ) SetItemC( &(store->ps), fits_ilat, 2, s,
astColumnName( table, icolindexlat ), status );
/* The one-based index of the axes within the coordinate array that
describes FITS WCS axes "fits_ilon" and "fits_ilat". */
SetItem( &(store->pv), fits_ilon, 3, s, mlon, status );
SetItem( &(store->pv), fits_ilat, 3, s, mlat, status );
/* The interpolation method (an AST extension to the published -TAB
algorithm, communicated through the QVi_4a keyword). */
SetItem( &(store->pv), fits_ilon, 4, s, interplon, status );
SetItem( &(store->pv), fits_ilat, 4, s, interplat, status );
/* Also store the FitsTable itself in the FitsStore. */
astMapPut0A( store->tables, AST_TABEXTNAME, table, NULL );
/* Allocate space for the arrays that define the permutations required
for the inputs and outputs of a PermMap. */
inperm = astMalloc( sizeof( double )*nwcs );
outperm = astMalloc( sizeof( double )*nwcs );
if( astOK ) {
/* Create the WCS -> IWC Mapping. First create a parallel CmpMap that
combines the Mapping returned by IsMapTab2D (which transforms the celestial
axes), with a UnitMap which transforms the non-celestial axes. */
if( nwcs > 2 ) {
tmap1 = (AstMapping *) astUnitMap( nwcs - 2, " ", status );
tmap2 = (AstMapping *) astCmpMap( tmap0, tmap1, 0, " ", status );
tmap1 = astAnnul( tmap1 );
} else {
tmap2 = astClone( tmap0 );
}
/* Now create a PermMap that permutes the inputs of this CmpMap into the
order of the axes in the WCS Frame. */
outperm[ 0 ] = ilon;
outperm[ 1 ] = ilat;
j = 0;
for( i = 2; i < nwcs; i++ ) {
while( j == ilon || j == ilat ) j++;
outperm[ i ] = j++;
}
for( i = 0; i < nwcs; i++ ) inperm[ outperm[ i ] ] = i;
tmap1 = (AstMapping *) astPermMap( nwcs, inperm, nwcs, outperm,
NULL, " ", status );
/* Use this PermMap (and its inverse) to permute the inputs (and outputs)
of the parallel CmpMap created above. */
tmap3 = (AstMapping *) astCmpMap( tmap1, tmap2, 1, " ", status );
tmap2 = astAnnul( tmap2 );
astInvert( tmap1 );
tmap2 = (AstMapping *) astCmpMap( tmap3, tmap1, 1, " ", status );
tmap1 = astAnnul( tmap1 );
tmap3 = astAnnul( tmap3 );
/* Now create a PermMap that permutes the WCS axes into the FITS axis order. */
for( i = 0; i < nwcs; i++ ) {
inperm[ i ] = wperm[ i ];
outperm[ wperm[ i ] ] = i;
}
tmap1 = (AstMapping *) astPermMap( nwcs, inperm, nwcs, outperm,
NULL, "", status );
/* Use this PermMap to permute the outputs of the "tmap2" Mapping. The
resulting Mapping is the Mapping from the current Frame to IWC and is
the Mapping to be returned as the function value. */
ret = (AstMapping *) astCmpMap( tmap2, tmap1, 1, " ", status );
tmap1 = astAnnul( tmap1 );
tmap2 = astAnnul( tmap2 );
}
/* Free remaining resources. */
inperm = astFree( inperm );
outperm = astFree( outperm );
tmap0 = astAnnul( tmap0 );
}
if( table ) table = astAnnul( table );
}
/* Release resources. */
ppcfid = astFree( ppcfid );
}
/* Release resources. */
wcsfrm = astAnnul( wcsfrm );
if( skyfrm ) skyfrm = astAnnul( skyfrm );
if( map ) map = astAnnul( map );
/* If we have a Mapping to return, simplify it. Otherwise, create
a UnitMap to return. */
if( ret ) {
tmap0 = ret;
ret = astSimplify( tmap0 );
tmap0 = astAnnul( tmap0 );
} else {
ret = (AstMapping *) astUnitMap( nwcs, "", status );
}
/* Return the result. */
return ret;
}
static void ChangePermSplit( AstMapping *map, int *status ){
/*
* Name:
* ChangePermSplit
* Purpose:
* Change all PermMaps in a Mapping to use the alternate
* implementation of the astMapSplit method.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void ChangePermSplit( AstMapping *map, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* The PemMap class provides two implementations of the astMapSplit
* method. The implementation used by each PermMap is determined by
* the value of the PermMap's "PermSplit" attribute. This function
* searches the supplied Mapping for any PermMaps, and set their
* PermSplit attribute to 1, indicating that the alternate
* implementation of astMapSplit should be used.
* Parameters:
* map
* Pointer to the Mapping. Modified on exit by setting all
* PermSplit attributes to 1.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
AstMapping *map1;
AstMapping *map2;
int series;
int invert1;
int invert2;
/* Check inherited status */
if( !astOK ) return;
/* If the supplied Mapping is a PermMap, set its PermSplit attribute
non-zero. */
if( astIsAPermMap( map ) ) {
astSetPermSplit( map, 1 );
/* If the supplied Mapping is not a PermMap, attempt to decompose the
Mapping into two component Mappings. */
} else {
astDecompose( map, &map1, &map2, &series, &invert1, &invert2 );
/* If the Mapping could be decomposed, use this function recursively to
set the PermSplit attributes in each component Mapping. */
if( map1 && map2 ) {
ChangePermSplit( map1, status );
ChangePermSplit( map2, status );
/* Annul the component Mappings. */
map1 = astAnnul( map1 );
map2 = astAnnul( map2 );
} else if( map1 ) {
map1 = astAnnul( map1 );
} else if( map2 ) {
map2 = astAnnul( map2 );
}
}
}
static double *Cheb2Poly( double *c, int nx, int ny, double xmin, double xmax,
double ymin, double ymax, int *status ){
/*
* Name:
* Cheb2Poly
* Purpose:
* Converts a two-dimensional Chebyshev polynomial to standard form and
* scale the arguments.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* double *Cheb2Poly( double *c, int nx, int ny, double xmin, double xmax,
* double ymin, double ymax, int *status )
* Class Membership:
* FitsChan
* Description:
* Given the coefficients of a two-dimensional Chebychev polynomial P(u,v),
* find the coefficients of the equivalent standard two-dimensional
* polynomial Q(x,y). The allowed range of u and v is assumed to be the
* unit square, and this maps on to the rectangle in (x,y) given by
* (xmin:xmax,ymin:ymax).
* Parameters:
* c
* An array of (nx,ny) elements supplied holding the coefficients of
* P, such that the coefficient of (Ti(u)*Tj(v)) is held in element
* (i + j*nx), where "Ti(u)" is the Chebychev polynomial (of the
* first kind) of order "i" evaluated at "u", and "Tj(v)" is the
* Chebychev polynomial of order "j" evaluated at "v".
* nx
* One more than the maximum power of u within P.
* ny
* One more than the maximum power of v within P.
* xmin
* X value corresponding to u = -1
* xmax
* X value corresponding to u = +1
* ymin
* Y value corresponding to v = -1
* ymax
* Y value corresponding to v = +1
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to a dynamically allocated array of (nx,ny) elements holding
* the coefficients of Q, such that the coefficient of (x^i*y^j) is held
* in element (i + j*nx). Free it using astFree when no longer needed.
*/
/* Local Variables: */
double *d;
double *pa;
double *pw;
double *work1;
double *work2;
double *work3;
int *iw1;
int *iw2;
int i;
int j;
/* Check the status and supplied value pointer. */
if( !astOK ) return NULL;
/* Allocate returned array. */
d = astMalloc( sizeof( *d )*nx*ny );
/* Allocate workspace. */
work1 = astMalloc( sizeof( *work1 )*ny );
work2 = astMalloc( sizeof( *work2 )*ny );
work3 = astMalloc( sizeof( *work2 )*nx );
iw1 = astMalloc( sizeof(int)*( nx > ny ? nx : ny ) );
iw2 = astMalloc( sizeof(int)*( nx > ny ? nx : ny ) );
if( astOK ) {
/* Thinking of P as a 1D polynomial in v, each coefficient would itself then
be a 1D polynomial in u:
P = ( c[0] + c[1]*T1(u) + c[2]*T2(u) + ... ) +
( c[nx] + c[nx+1]*T1(u) + c[nx+2]*T2(u) + ... )*T1(v) +
(c[2*nx] + c[2*nx+1]*T1(u) + c[2*nx+2]*T2(u) + ... )*T2(v) +
...
(c[(ny-1)*nx] + c[(ny-1)*nx+1]*T1(u) + c[(ny-1)*nx+2]*T2(u) + ... )T{ny-1}(v)
Use Chpc1 to convert these "polynomial coefficients" to standard
form, storing the result in the corresponding row of "d" . Also,
convert them from u to x. */
for( j = 0; j < ny; j++ ) {
Chpc1( c + j*nx, work3, nx, iw1, iw2, status );
Shpc1( xmin, xmax, nx, work3, d + j*nx, status );
}
/* The polynomial value is now:
( d[0] + d[1]*x + d[2]*x*x + ... ) +
( d[nx] + d[nx+1]*x + d[nx+2]*x*x + ... )*T1(v) +
(d[2*nx] + d[2*nx+1]*x + d[2*nx+2]*x*x + ... )*T2(v) +
...
(d[(ny-1)*nx] + d[(ny-1)*nx+1]*x + d[(ny-1)*nx+2]*x*x + ... )*T{ny-1}(v)
If we rearrange this expression to view it as a 1D polynomial in x,
rather than v, each coefficient of the new 1D polynomial is then
itself a polynomial in v:
( d[0] + d[nx]*T1(v) + d[2*nx]*T2(v) + ... d[(ny-1)*nx]*T{ny-1}(v) ) +
( d[1] + d[nx+1]*T1(v) + d[2*nx+1]*T2(v) + ... d[(ny-1)*nx+1]T{ny-1}(v)... )*x +
( d[2] + d[nx+2]*T1(v) + d[2*nx+2]*T2(v) + ... d[(ny-1)*nx+2]T{ny-1}(v)... )*x*x +
...
( d[nx-1] + d[2*nx-1]*T1(v) + d[3*nx-1]*T2(v) + ... d[ny*nx-1]*T{ny-1}(v) )*x*x*...
Now use Chpc1 to convert each of these "polynomial coefficients"
to standard form. We copy each column of the d array into a 1D work array,
use Shpc1 to modify the values in the work array, and then write
the modified values back into the current column of d. Also convert
from v to y. */
for( i = 0; i < nx; i++ ) {
pa = d + i;
pw = work1;
for( j = 0; j < ny; j++ ) {
*(pw++) = *pa;
pa += nx;
}
Chpc1( work1, work2, ny, iw1, iw2, status );
Shpc1( ymin, ymax, ny, work2, work1, status );
pa = d + i;
pw = work1;
for( j = 0; j < ny; j++ ) {
*pa = *(pw++);
pa += nx;
}
}
/* So the polynomial is now:
( d[0] + d[nx]*y + d[2*nx]*y*y + ... d[(ny-1)*nx]*y*y*... ) +
( d[1] + d[nx+1]*y + d[2*nx+1]*y*y + ... d[(ny-1)*nx+1]*y*y*... )*x +
( d[2] + d[nx+2]*y + d[2*nx+2]*y*y + ... d[(ny-1)*nx+2]*y*y*... )*x*x +
...
( d[nx-1] + d[2*nx-1]*y + d[3*nx-1]*y*y + ... d[ny*nx-1]*y*y*... )*x*x*...
Re-arranging, this is:
( d[0] + d[1]*x + d[2]*x*x + ... ) +
( d[nx] + d[nx+1]*x + d[nx+2]*x*x + ... )*y +
(d[2*nx] + d[2*nx+1]*x + d[2*nx+2]*x*x + ... )*y*y +
...
(d[(ny-1)*nx] + d[(ny-1)*nx+1]*x + d[(ny-1)*nx+2]*x*x + ... )*y*y*...
as required. */
}
/* Free the workspace. */
work1 = astFree( work1 );
work2 = astFree( work2 );
work3 = astFree( work3 );
iw1 = astFree( iw1 );
iw2 = astFree( iw2 );
/* Return the result. */
return d;
}
static int CheckFitsName( AstFitsChan *this, const char *name,
const char *method, const char *class, int *status ){
/*
* Name:
* CheckFitsName
* Purpose:
* Check a keyword name conforms to FITS standards.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int CheckFitsName( AstFitsChan *this, const char *name, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* FITS keywords must contain between 1 and 8 characters, and each
* character must be an upper-case Latin alphabetic character, a digit,
* an underscore, or a hyphen. Leading, trailing or embedded white space
* is not allowed, with the exception of totally blank or null keyword
* names.
*
* If the supplied keyword name is invalid, either a warning is issued
* (for violations that can be handled - such as illegal characters in
* keywords), or an error is reported (for more major violations such
* as the keyname containing an equals sign).
* Parameters:
* this
* Pointer to the FitsChan.
* name
* Pointer to a string holding the name to check.
* 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:
* A value of 0 is returned if the supplied name was blank. A value of 1
* is returned otherwise.
* Notes:
* - An error is reported if the supplied keyword name does not
* conform to FITS requirements, and zero is returned.
*/
/* Local Variables: */
char buf[100]; /* Buffer for warning text */
const char *c; /* Pointer to next character in name */
size_t n; /* No. of characters in supplied name */
int ret; /* Returned value */
/* Check the global status. */
if( !astOK ) return 0;
/* Initialise the returned value to indicate that the supplied name was
blank. */
ret = 0;
/* Check that the supplied pointer is not NULL. */
if( name ){
/* Get the number of characters in the name. */
n = strlen( name );
/* Report an error if the name has too many characters in it. */
if( n > FITSNAMLEN ){
astError( AST__BDFTS, "%s(%s): The supplied FITS keyword name ('%s') "
"has %d characters. FITS only allows up to %d.", status, method,
class, name, (int) n, FITSNAMLEN );
/* If the name has no characters in it, then assume it is a legal blank
keyword name. Otherwise, check that no illegal characters occur in the
name. */
} else if( n != 0 ) {
/* Whitespace is only allowed in the special case of a name consisting
entirely of whitespace. Such keywords are used to indicate that the rest
of the card is a comment. Find the first non-whitespace character in the
name. */
c = name;
while( isspace( ( int ) *(c++) ) );
/* If the name is filled entirely with whitespace, then the name is acceptable
as the special case. Otherwise, we need to do more checks. */
if( c - name - 1 < n ){
/* Indicate that the supplied name is not blank. */
ret = 1;
/* Loop round every character checking that it is one of the legal characters.
Report an error if any illegal characters are found. */
c = name;
while( *c ){
if( !isFits( (int) *c ) ){
if( *c == '=' ){
astError( AST__BDFTS, "%s(%s): An equals sign ('=') was found "
"before column %d within a FITS keyword name or header "
"card.", status, method, class, FITSNAMLEN + 1 );
} else if( *c < ' ' ) {
sprintf( buf, "The FITS keyword name ('%s') contains an "
"illegal non-printing character (ascii value "
"%d).", name, *c );
Warn( this, "badkeyname", buf, method, class, status );
} else if( *c > ' ' ) {
sprintf( buf, "The FITS keyword name ('%s') contains an "
"illegal character ('%c').", name, *c );
Warn( this, "badkeyname", buf, method, class, status );
}
break;
}
c++;
}
}
}
/* Report an error if no pointer was supplied. */
} else if( astOK ){
astError( AST__INTER, "CheckFitsName(%s): AST internal error; a NULL "
"pointer was supplied for the keyword name. ", status,
astGetClass( this ) );
}
/* If an error has occurred, return 0. */
if( !astOK ) ret = 0;
/* Return the answer. */
return ret;
}
static void CheckZero( char *text, double value, int width, int *status ){
/*
* Name:
* CheckZero
* Purpose:
* Ensure that the formatted value zero has no minus sign.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void CheckZero( char *text, double value, int width, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* There is sometimes a problem (perhaps only on DEC UNIX) when formatting
* the floating-point value 0.0 using C. Sometimes it gives the string
* "-0". This function fixed this by checking the first character of
* the supplied string (if the supplied value is zero), and shunting the
* remaining text one character to the right if it is a minus sign. It
* returns without action if the supplied value is not zero.
*
* In addition, this function also rounds out long sequences of
* adjacent zeros or nines in the number.
* Parameters:
* text
* The formatted value.
* value
* The floating value which was formatted.
* width
* The minimum field width to use. The value is right justified in
* this field width. Ignored if zero.
* status
* Pointer to the inherited status variable.
* Notes:
* - This function attempts to execute even if an error has occurred.
*/
/* Local Variables: */
char *c;
/* Return if no text was supplied. */
if( !text ) return;
/* If the numerical value is zero, check for the leading minus sign. */
if( value == 0.0 ) {
/* Find the first non-space character. */
c = text;
while( *c && isspace( (int) *c ) ) c++;
/* If the first non-space character is a minus sign, replace it with a
space. */
if( *c == '-' ) *c = ' ';
/* Otherwise, round out sequences of zeros or nines. */
} else {
RoundFString( text, width, status );
}
}
static double ChooseEpoch( AstFitsChan *this, FitsStore *store, char s,
const char *method, const char *class, int *status ){
/*
* Name:
* ChooseEpoch
* Purpose:
* Choose a FITS keyword value to use for the AST Epoch attribute.
* Type:
* Private function.
* Synopsis:
* double ChooseEpoch( AstFitsChan *this, FitsStore *store, char s,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* This function returns an MJD value in the TDB timescale, which can
* be used as the Epoch value in an AST Frame. It uses the following
* preference order: secondary MJD-AVG, primary MJD-AVG, secondary MJD-OBS,
* primary MJD-OBS. Note, DATE-OBS keywords are converted into MJD-OBS
* keywords by the SpecTrans function before this function is called.
* Parameters:
* this
* Pointer to the FitsChan.
* store
* A structure containing values for FITS keywords relating to
* the World Coordinate System.
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* method
* The calling method. Used only in error messages.
* class
* The object class. Used only in error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The MJD value.
* Notes:
* - A value of AST__BAD is returned if an error occurs, or if none
* of the required keywords can be found in the FitsChan.
*/
/* Local Variables: */
const char *timesys; /* The TIMESYS value in the FitsStore */
double mjd; /* The returned MJD */
/* Initialise the returned value. */
mjd = AST__BAD;
/* Check the global status. */
if( !astOK ) return mjd;
/* Otherwise, try to get the secondary MJD-AVG value. */
mjd = GetItem( &(store->mjdavg), 0, 0, s, NULL, method, class, status );
/* Otherwise, try to get the primary MJD-AVG value. */
if( mjd == AST__BAD ) mjd = GetItem( &(store->mjdavg), 0, 0, ' ', NULL,
method, class, status );
/* If the secondary MJD-OBS keyword is present in the FitsChan, gets its
value. */
if( mjd == AST__BAD ) mjd = GetItem( &(store->mjdobs), 0, 0, s, NULL,
method, class, status );
/* Otherwise, try to get the primary MJD-OBS value. */
if( mjd == AST__BAD ) mjd = GetItem( &(store->mjdobs), 0, 0, ' ', NULL,
method, class, status );
/* Now convert the MJD value to the TDB timescale. */
timesys = GetItemC( &(store->timesys), 0, 0, ' ', NULL, method, class, status );
mjd = TDBConv( mjd, TimeSysToAst( this, timesys, method, class, status ),
0, method, class, status );
/* Return the answer. */
return mjd;
}
static void Chpc1( double *c, double *d, int n, int *w0, int *w1, int *status ){
/*
* Name:
* Chpc1
* Purpose:
* Converts a one-dimensional Chebyshev polynomial to standard form.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void Chpc1( double *c, double *d, int n, int *w0, int *w1, int *status )
* Class Membership:
* FitsChan
* Description:
* Given the coefficients of a one-dimensional Chebychev polynomial P(u),
* find the coefficients of the equivalent standard 1D polynomial Q(u).
* The allowed range of u is assumed to be the unit interval.
* Parameters:
* c
* An array of n elements supplied holding the coefficients of
* P, such that the coefficient of (Ti(u)) is held in element
* (i), where "Ti(u)" is the Chebychev polynomial (of the
* first kind) of order "i" evaluated at "u".
* d
* An array of n elements returned holding the coefficients of
* Q, such that the coefficient of (u^i) is held in element (i).
* n
* One more than the highest power of u in P.
* w0
* Pointer to a work array of n elements.
* w1
* Pointer to a work array of n elements.
* status
* Inherited status value
* Notes:
* - Vaguely inspired by the Numerical Recipes routine "chebpc". But the
* original had bugs, so I wrote this new version from first principles.
*/
/* Local Variables: */
int sv;
int j;
int k;
/* Check inherited status */
if( !astOK ) return;
/* Initialise the returned coefficients array. */
for( j = 0; j < n; j++ ) d[ j ] = 0.0;
/* Use the recurrence relation
T{k+1}(x) = 2.x.T{k}(x) - T{k-1}(x).
w0[i] holds the coefficient of x^i in T{k-1}. w1[i] holds the
coefficient of x^i in T{k}. Initialise them for T0 (="1") and
T1 (="x"). */
for( j = 0; j < n; j++ ) w0[ j ] = w1[ j ] = 0;
w0[ 0 ] = 1;
w1[ 1 ] = 1;
/* Update the returned coefficients array to include the T0 and T1 terms. */
d[ 0 ] = c[ 0 ];
d[ 1 ] = c[ 1 ];
/* Loop round using the above recurrence relation until we have found
T{n-1}. */
for( k = 1; k < n - 1; k++ ){
/* To get the coefficients of T{k+1} shift the contents of w1 up one
element, introducing a zero at the low end, and then double all the
values in w1. Finally subtract off the values in w0. This implements
the above recurrence relationship. Starting at the top end and working
down to the bottom, store a new value for each element of w1. */
for( j = n - 1; j > 0; j-- ) {
/* First save the original element of w1 in w0 for use next time. But we
also need the original w0 element later on so save it first. */
sv = w0[ j ];
w0[ j ] = w1[ j ];
/* Double the lower neighbouring w1 element and subtract off the w0
element saved above. This forms the new value for w1. */
w1[ j ] = 2*w1[ j - 1 ] - sv;
}
/* Introduce a zero into the lowest element of w1, saving the original
value first in w0. Then subtract off the original value of w0. */
sv = w0[ 0 ];
w0[ 0 ] = w1[ 0 ];
w1[ 0 ] = -sv;
/* W1 now contains the coefficients of T{k+1} in w1, and the coefficients
of T{k} in w0. Multiply these by the supplied coefficient for T{k+1},
and add them into the returned array. */
for( j = 0; j <= k + 1; j++ ){
d[ j ] += c[ k + 1 ]*w1[ j ];
}
}
}
static int ChrLen( const char *string, int *status ){
/*
* Name:
* ChrLen
* Purpose:
* Return the length of a string excluding any trailing white space.
* Type:
* Private function.
* Synopsis:
* int ChrLen( const char *string, int *status )
* Class Membership:
* FitsChan
* Description:
* This function returns the length of a string excluding any trailing
* white space, or non-printable characters.
* Parameters:
* string
* Pointer to the string.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The length of a string excluding any trailing white space and
* non-printable characters.
* Notes:
* - A value of zero is returned if a NULL pointer is supplied, or if an
* error has already occurred.
*/
/* Local Variables: */
const char *c; /* Pointer to the next character to check */
int ret; /* The returned string length */
/* Check the global status. */
if( !astOK ) return 0;
/* Initialise the returned string length. */
ret = 0;
/* Check a string has been supplied. */
if( string ){
/* Check each character in turn, starting with the last one. */
ret = strlen( string );
c = string + ret - 1;
while( ret ){
if( isprint( (int) *c ) && !isspace( (int) *c ) ) break;
c--;
ret--;
}
}
/* Return the answer. */
return ret;
}
static int CLASSFromStore( AstFitsChan *this, FitsStore *store,
AstFrameSet *fs, double *dim, const char *method,
const char *class, int *status ){
/*
* Name:
* CLASSFromStore
* Purpose:
* Store WCS keywords in a FitsChan using FITS-CLASS encoding.
* Type:
* Private function.
* Synopsis:
* int CLASSFromStore( AstFitsChan *this, FitsStore *store,
* AstFrameSet *fs, double *dim, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* A FitsStore is a structure containing a generalised represention of
* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
* from a set of FITS header cards (using a specified encoding), or
* an AST FrameSet. In other words, a FitsStore is an encoding-
* independant intermediary staging post between a FITS header and
* an AST FrameSet.
*
* This function copies the WCS information stored in the supplied
* FitsStore into the supplied FitsChan, using FITS-CLASS encoding.
* Parameters:
* this
* Pointer to the FitsChan.
* store
* Pointer to the FitsStore.
* fs
* Pointer to the FrameSet from which the values in the FitsStore
* were derived.
* dim
* Pointer to an array holding the main array dimensions (AST__BAD
* if a dimension is not known).
* 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:
* A value of 1 is returned if succesfull, and zero is returned
* otherwise.
*/
/* Local Variables: */
AstFrame *azelfrm; /* (az,el) frame */
AstFrame *curfrm; /* Current Frame in supplied FrameSet */
AstFrame *freqfrm; /* Frame for reference frequency value */
AstFrame *radecfrm; /* Spatial frame for CRVAL values */
AstFrame *velofrm; /* Frame for reference velocity value */
AstFrameSet *fsconv1;/* FrameSet connecting "curfrm" & "radecfrm" */
AstFrameSet *fsconv2;/* FrameSet connecting "curfrm" & "azelfrm" */
AstMapping *map1; /* Axis permutation to get (lonaxis,lataxis) = (0,1) */
AstMapping *map2; /* Mapping from FITS CTYPE to (az,el) */
AstMapping *map3; /* Mapping from (lon,lat) to (az,el) */
char *comm; /* Pointer to comment string */
char *cval; /* Pointer to string keyword value */
char attbuf[20]; /* Buffer for AST attribute name */
char combuf[80]; /* Buffer for FITS card comment */
char lattype[MXCTYPELEN];/* Latitude axis CTYPE */
char lontype[MXCTYPELEN];/* Longitude axis CTYPE */
char s; /* Co-ordinate version character */
char sign[2]; /* Fraction's sign character */
char spectype[MXCTYPELEN];/* Spectral axis CTYPE */
double *cdelt; /* Pointer to CDELT array */
double aval[ 2 ]; /* General purpose array */
double azel[ 2 ]; /* Reference (az,el) values */
double cdl; /* CDELT term */
double crval[ 3 ]; /* CRVAL values converted to rads, etc */
double delta; /* Spectral axis increment */
double equ; /* Epoch of reference equinox */
double fd; /* Fraction of a day */
double latval; /* CRVAL for latitude axis */
double lonpole; /* LONPOLE value */
double lonval; /* CRVAL for longitude axis */
double mjd99; /* MJD at start of 1999 */
double p1, p2; /* Projection parameters */
double radec[ 2 ]; /* Reference (lon,lat) values */
double rf; /* Rest freq (Hz) */
double specfactor; /* Factor for converting internal spectral units */
double val; /* General purpose value */
double xin[ 3 ]; /* Grid coords at centre of first pixel */
double xout[ 3 ]; /* WCS coords at centre of first pixel */
int axlat; /* Index of latitude FITS WCS axis */
int axlon; /* Index of longitude FITS WCS axis */
int axspec; /* Index of spectral FITS WCS axis */
int i; /* Axis index */
int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */
int iymdf[ 4 ]; /* Year, month, date, fractional day */
int j; /* Axis index */
int jj; /* SlaLib status */
int naxis2; /* Length of pixel axis 2 */
int naxis3; /* Length of pixel axis 3 */
int naxis; /* No. of axes */
int ok; /* Is FitsSTore OK for IRAF encoding? */
int prj; /* Projection type */
/* Other initialisation to avoid compiler warnings. */
lonval = 0.0;
latval = 0.0;
/* Check the inherited status. */
if( !astOK ) return 0;
/* Initialise */
specfactor = 1.0;
/* First check that the values in the FitsStore conform to the
requirements of the CLASS encoding. Assume they do not to begin with. */
ok = 0;
/* Just do primary axes. */
s = ' ';
/* Look for the primary celestial axes. */
FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status );
/* Get the current Frame from the supplied FrameSet. */
curfrm = astGetFrame( fs, AST__CURRENT );
/* Spectral and celestial axes must be present in axes 1,2 and 3. */
if( axspec >= 0 && axspec < 3 &&
axlon >= 0 && axlon < 3 &&
axlat >= 0 && axlat < 3 ) {
ok = 1;
/* If the spatial pixel axes are degenerate (i.e. span only a single
pixel), modify the CRPIX and CRVAL values in the FitsStore to put
the reference point at the centre of the one and only spatial pixel. */
if( store->naxis >= 3 && dim[ axlon ] == 1.0 && dim[ axlat ] == 1.0 ){
xin[ 0 ] = 1.0;
xin[ 1 ] = 1.0;
xin[ 2 ] = 1.0;
astTranN( fs, 1, 3, 1, xin, 1, 3, 1, xout );
if( xout[ axlon ] != AST__BAD && xout[ axlat ] != AST__BAD ) {
/* The indices of the spatial axes in the FITS header may not be the same
as the indices of the spatial axes in the WCS Frame of the supplied
FrameSet. So search the current Frame for longitude and latitude axes,
and store the corresponding elements of the "xout" array for later use. */
for( i = 0; i < 3; i++ ) {
sprintf( attbuf, "IsLonAxis(%d)", i + 1 );
if( astHasAttribute( curfrm, attbuf ) ) {
if( astGetI( curfrm, attbuf ) ) {
lonval = xout[ i ];
} else {
latval = xout[ i ];
}
}
}
/* Store them in the FitsStore. */
SetItem( &(store->crval), axlon, 0, ' ', lonval*AST__DR2D, status );
SetItem( &(store->crval), axlat, 0, ' ', latval*AST__DR2D, status );
SetItem( &(store->crpix), 0, axlon, ' ', 1.0, status );
SetItem( &(store->crpix), 0, axlat, ' ', 1.0, status );
}
}
/* Get the CRVAL values for both spatial axes. */
latval = GetItem( &( store->crval ), axlat, 0, s, NULL, method, class, status );
if( latval == AST__BAD ) ok = 0;
lonval = GetItem( &( store->crval ), axlon, 0, s, NULL, method, class, status );
if( lonval == AST__BAD ) ok = 0;
/* Get the CTYPE values for both axes. Extract the projection type as
specified by the last 4 characters in the latitude CTYPE keyword value. */
cval = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status );
if( !cval ) {
ok = 0;
} else {
strcpy( lontype, cval );
}
cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status );
if( !cval ) {
ok = 0;
prj = AST__WCSBAD;
} else {
strcpy( lattype, cval );
prj = astWcsPrjType( cval + 4 );
}
/* Check the projection type is OK. */
if( prj == AST__WCSBAD ){
ok = 0;
} else if( prj != AST__SIN ){
/* Check the projection code is OK. */
ok = 0;
if( prj == AST__TAN ||
prj == AST__ARC ||
prj == AST__STG ||
prj == AST__AIT ||
prj == AST__SFL ) {
ok = 1;
/* For AIT, and SFL, check that the reference point is the origin of
the celestial co-ordinate system. */
if( prj == AST__AIT ||
prj == AST__SFL ) {
if( latval != 0.0 || lonval != 0.0 ){
ok = 0;
/* Change the new SFL projection code to to the older equivalent GLS */
} else if( prj == AST__SFL ){
(void) strcpy( lontype + 4, "-GLS" );
(void) strcpy( lattype + 4, "-GLS" );
/* Change the new AIT projection code to to the older equivalent ATF */
} else if( prj == AST__AIT ){
(void) strcpy( lontype + 4, "-ATF" );
(void) strcpy( lattype + 4, "-ATF" );
}
}
}
/* SIN projections are only acceptable if the associated projection
parameters are both zero. */
} else {
p1 = GetItem( &( store->pv ), axlat, 1, s, NULL, method, class, status );
p2 = GetItem( &( store->pv ), axlat, 2, s, NULL, method, class, status );
if( p1 == AST__BAD ) p1 = 0.0;
if( p2 == AST__BAD ) p2 = 0.0;
ok = ( p1 == 0.0 && p2 == 0.0 );
}
/* Identify the celestial coordinate system from the first 4 characters of the
longitude CTYPE value. Only RA and galactic longitude can be stored using
FITS-CLASS. */
if( ok && strncmp( lontype, "RA--", 4 ) &&
strncmp( lontype, "GLON", 4 ) ) ok = 0;
/* Get the CTYPE values for the spectral axis, and find the CLASS equivalent,
if possible. */
cval = GetItemC( &(store->ctype), axspec, 0, s, NULL, method, class, status );
if( !cval ) {
ok = 0;
} else {
if( !strncmp( cval, "FREQ", astChrLen( cval ) ) ) {
strcpy( spectype, "FREQ" );
} else {
ok = 0;
}
}
/* If OK, check the SPECSYS value is SOURCE. */
cval = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status );
if( !cval ) {
ok = 0;
} else if( ok ) {
if( strncmp( cval, "SOURCE", astChrLen( cval ) ) ) ok = 0;
}
/* If still OK, ensure the spectral axis units are Hz. */
cval = GetItemC( &(store->cunit), axspec, 0, s, NULL, method, class, status );
if( !cval ) {
ok = 0;
} else if( ok ) {
if( !strcmp( cval, "Hz" ) ) {
specfactor = 1.0;
} else if( !strcmp( cval, "kHz" ) ) {
specfactor = 1.0E3;
} else if( !strcmp( cval, "MHz" ) ) {
specfactor = 1.0E6;
} else if( !strcmp( cval, "GHz" ) ) {
specfactor = 1.0E9;
} else {
ok = 0;
}
}
}
/* Save the number of WCS axes */
naxis = GetMaxJM( &(store->crpix), ' ', status ) + 1;
/* If this is larger than 3, ignore the surplus WCS axes. Note, the
above code has checked that the spatial and spectral axes are
WCS axes 0, 1 and 2. */
if( naxis > 3 ) naxis = 3;
/* Allocate memory to store the CDELT values */
if( ok ) {
cdelt = (double *) astMalloc( sizeof(double)*naxis );
if( !cdelt ) ok = 0;
} else {
cdelt = NULL;
}
/* Check that there is no rotation, and extract the CDELT (diagonal) terms,
etc. If the spatial axes are degenerate (i.e. cover only a single pixel)
then ignore any rotation. */
if( !GetValue( this, FormatKey( "NAXIS", axlon + 1, -1, s, status ), AST__INT,
&naxis2, 0, 0, method, class, status ) ) {
naxis2 = 0;
}
if( !GetValue( this, FormatKey( "NAXIS", axlat + 1, -1, s, status ), AST__INT,
&naxis3, 0, 0, method, class, status ) ) {
naxis3 = 0;
}
for( i = 0; i < naxis && ok; i++ ){
cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
if( cdl == AST__BAD ) cdl = 1.0;
for( j = 0; j < naxis && ok; j++ ){
val = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0;
val *= cdl;
if( i == j ){
cdelt[ i ] = val;
} else if( val != 0.0 ){
if( naxis2 != 1 || naxis3 != 1 ) ok = 0;
}
}
}
/* Get RADECSYS and the reference equinox. */
cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status );
equ = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status );
/* If RADECSYS was available... */
if( cval ){
/* Only FK4 and FK5 are supported in this encoding. */
if( strcmp( "FK4", cval ) && strcmp( "FK5", cval ) ) ok = 0;
/* If epoch was not available, set a default epoch. */
if( equ == AST__BAD ){
if( !strcmp( "FK4", cval ) ){
equ = 1950.0;
} else if( !strcmp( "FK5", cval ) ){
equ = 2000.0;
} else {
ok = 0;
}
/* If an epoch was supplied, check it is consistent with the IAU 1984
rule. */
} else {
if( !strcmp( "FK4", cval ) ){
if( equ >= 1984.0 ) ok = 0;
} else if( !strcmp( "FK5", cval ) ){
if( equ < 1984.0 ) ok = 0;
} else {
ok = 0;
}
}
/* Check we have a rest frequency */
rf = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status );
if( rf == AST__BAD ) ok = 0;
}
/* If the spatial Frame covers more than a single Frame and requires a LONPOLE
or LATPOLE keyword, it cannot be encoded using FITS-CLASS. However since
FITS-CLASS imposes a no rotation restriction, it can tolerate lonpole
values of +/- 180 degrees. */
if( ok && ( naxis2 != 1 || naxis3 != 1 ) ) {
lonpole = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status );
if( lonpole != AST__BAD && lonpole != -180.0 && lonpole == 180 ) ok = 0;
if( GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status )
!= AST__BAD ) ok = 0;
}
/* Only create the keywords if the FitsStore conforms to the requirements
of the FITS-CLASS encoding. */
if( ok ) {
/* If celestial axes were added by MakeFitsFrameSet, we need to ensure
the header contains 3 main array axes. This is because the CLASS
encoding does not support the WCSAXES keyword. */
if( store->naxis == 1 ) {
/* Update the "NAXIS" value to 3 or put a new card in at the start. */
astClearCard( this );
i = 3;
SetValue( this, "NAXIS", &i, AST__INT, NULL, status );
/* Put NAXIS2/3 after NAXIS1, or after NAXIS if the FitsChan does not contain
NAXIS1. These are set to 1 since the spatial axes are degenerate. */
if( FindKeyCard( this, "NAXIS1", method, class, status ) ) {
MoveCard( this, 1, method, class, status );
}
i = 1;
SetValue( this, "NAXIS2", &i, AST__INT, NULL, status );
SetValue( this, "NAXIS3", &i, AST__INT, NULL, status );
}
/* Find the last WCS related card. */
FindWcs( this, 1, 1, 0, method, class, status );
/* Get and save CRPIX for all pixel axes. These are required, so break
if they are not available. */
for( j = 0; j < naxis && ok; j++ ){
val = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status );
if( val == AST__BAD ) {
ok = 0;
} else {
sprintf( combuf, "Reference pixel on axis %d", j + 1 );
SetValue( this, FormatKey( "CRPIX", j + 1, -1, s, status ), &val,
AST__FLOAT, combuf, status );
}
}
/* Get and save CRVAL for all intermediate axes. These are required, so
break if they are not available. Note, the frequency axis CRVAL is
redefined by FITS-CLASS by reducing it by the RESTFREQ value. */
for( i = 0; i < naxis && ok; i++ ){
val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
if( val == AST__BAD ) {
ok = 0;
} else {
crval[ i ] = val;
if( i == axspec ) {
val *= specfactor;
val -= rf;
}
sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 );
SetValue( this, FormatKey( "CRVAL", i + 1, -1, s, status ), &val,
AST__FLOAT, combuf, status );
}
}
/* Get and save CTYPE for all intermediate axes. These are required, so
break if they are not available. Use the potentially modified versions
saved above for the celestial axes. */
for( i = 0; i < naxis && ok; i++ ){
if( i == axlat ) {
cval = lattype;
} else if( i == axlon ) {
cval = lontype;
} else if( i == axspec ) {
cval = spectype;
} else {
cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
}
if( cval && ( strlen(cval) < 5 || strcmp( cval + 4, "-TAB" ) ) ) {
comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status );
if( !comm ) {
sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 );
comm = combuf;
}
SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval,
AST__STRING, comm, status );
} else {
ok = 0;
}
}
/* CDELT values */
if( axspec != -1 ) cdelt[ axspec ] *= specfactor;
for( i = 0; i < naxis; i++ ){
SetValue( this, FormatKey( "CDELT", i + 1, -1, s, status ), cdelt + i,
AST__FLOAT, "Pixel size", status );
}
/* Reference equinox */
if( equ != AST__BAD ) SetValue( this, "EQUINOX", &equ, AST__FLOAT,
"Epoch of reference equinox", status );
/* Date of observation. */
val = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, method, class, status );
if( val != AST__BAD ) {
/* The format used for the DATE-OBS keyword depends on the value of the
keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format.
Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */
palCaldj( 99, 1, 1, &mjd99, &jj );
if( val < mjd99 ) {
palDjcal( 0, val, iymdf, &jj );
sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ],
iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) );
} else {
palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj );
palDd2tf( 3, fd, sign, ihmsf );
sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d",
iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1],
ihmsf[2], ihmsf[3] );
}
/* Now store the formatted string in the FitsChan. */
cval = combuf;
SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING,
"Date of observation", status );
}
/* Rest frequency */
SetValue( this, "RESTFREQ", &rf, AST__FLOAT, "[Hz] Rest frequency", status );
/* The image frequency corresponding to the rest frequency (only used for
double sideband data). */
val = GetItem( &(store->imagfreq), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) {
SetValue( this, "IMAGFREQ", &val, AST__FLOAT, "[Hz] Image frequency", status );
}
/* Ensure the FitsChan contains OBJECT and LINE headers */
if( !HasCard( this, "OBJECT", method, class, status ) ) {
cval = " ";
SetValue( this, "OBJECT", &cval, AST__STRING, NULL, status );
}
if( !HasCard( this, "LINE", method, class, status ) ) {
cval = " ";
SetValue( this, "LINE", &cval, AST__STRING, NULL, status );
}
/* CLASS expects the VELO-LSR keyword to hold the radio velocity of the
reference channel (NOT of the source as I was told!!) with respect to
the LSRK rest frame. The "crval" array holds the frequency of the
reference channel in the source rest frame, so we need to convert this
to get the value for VELO-LSR. Create a SpecFrame describing the
required frame (other attributes such as Epoch etc are left unset and
so will be picked up from the supplied FrameSet). We set MinAxes
and MaxAxes so that the Frame can be used as a template to match the
1D or 3D current Frame in the supplied FrameSet. */
velofrm = (AstFrame *) astSpecFrame( "System=vrad,StdOfRest=lsrk,"
"Unit=m/s,MinAxes=1,MaxAxes=3", status );
/* Find the spectral axis within the current Frame of the supplied
FrameSet, using the above "velofrm" as a template. */
fsconv1 = astFindFrame( curfrm, velofrm, "" );
/* If OK, extract the SpecFrame from the returned FraneSet (this will
have the attribute values that were assigned explicitly to "velofrm"
and will have inherited all unset attributes from the supplied
FrameSet). */
if( fsconv1 ) {
velofrm = astAnnul( velofrm );
velofrm = astGetFrame( fsconv1, AST__CURRENT );
fsconv1 = astAnnul( fsconv1 );
/* Take a copy of the velofrm and modify its attributes so that it
describes frequency in the sources rest frame in units of Hz. This is
the system that CLASS expects for the CRVAL3 keyword. */
freqfrm = astCopy( velofrm );
astSet( freqfrm, "System=freq,StdOfRest=Source,Unit=Hz", status );
/* Get a Mapping from frequency to velocity. */
fsconv1 = astConvert( freqfrm, velofrm, "" );
if( fsconv1 ) {
/* Use this Mapping to convert the spectral crval value from frequency to
velocity. Also convert the value for the neighbouring channel. */
aval[ 0 ] = crval[ axspec ]*specfactor;
aval[ 1 ] = aval[ 0 ] + cdelt[ axspec ]*specfactor;
astTran1( fsconv1, 2, aval, 1, aval );
/* Store the value. Also store it as VLSR since this keyword seems to be
used for the same thing. */
SetValue( this, "VELO-LSR", aval, AST__FLOAT, "[m/s] Reference velocity", status );
SetValue( this, "VLSR", aval, AST__FLOAT, "[m/s] Reference velocity", status );
/* The DELTAV keyword holds the radio velocity channel spacing in the
LSR. */
delta = aval[ 1 ] - aval[ 0 ];
SetValue( this, "DELTAV", &delta, AST__FLOAT, "[m/s] Velocity resolution", status );
/* Free remaining resources. */
fsconv1 = astAnnul( fsconv1 );
}
}
velofrm = astAnnul( velofrm );
/* AZIMUTH and ELEVATIO - the (az,el) equivalent of CRVAL. We need a
Mapping from the CTYPE spatial system to (az,el). This depends on all
the extra info like telescope position, epoch, etc. This info is in
the current Frame in the supplied FrameSet. First get a conversion
from a sky frame with default axis ordering to the supplied Frame. All
the extra info is picked up from the supplied Frame since it is not set
in the template. */
radecfrm = (AstFrame *) astSkyFrame( "Permute=0,MinAxes=3,MaxAxes=3", status );
fsconv1 = astFindFrame( curfrm, radecfrm, "" );
/* Now get conversion from the an (az,el) Frame to the supplied Frame. */
azelfrm = (AstFrame *) astSkyFrame( "System=AZEL,Permute=0,MinAxes=3,MaxAxes=3", status );
fsconv2 = astFindFrame( curfrm, azelfrm, "" );
/* If both conversions werew possible, concatenate their Mappings to get
a Mapping from (lon,lat) in the CTYPE system, to (az,el). */
if( fsconv1 && fsconv2 ) {
map1 = astGetMapping( fsconv1, AST__CURRENT, AST__BASE );
map2 = astGetMapping( fsconv2, AST__BASE, AST__CURRENT );
map3 = (AstMapping *) astCmpMap( map1, map2, 1, "", status );
/* Store the CRVAL (ra,dec) values in the default order. */
radec[ 0 ] = crval[ axlon ]*AST__DD2R;
radec[ 1 ] = crval[ axlat ]*AST__DD2R;
/* Transform to (az,el), normalise, convert to degrees and store. */
astTranN( map3, 1, 2, 1, radec, 1, 2, 1, azel );
if( azel[ 0 ] != AST__BAD && azel[ 1 ] != AST__BAD ) {
astNorm( azelfrm, azel );
azel[ 0 ] *= AST__DR2D;
azel[ 1 ] *= AST__DR2D;
SetValue( this, "AZIMUTH", azel, AST__FLOAT, "[Deg] Telescope azimuth", status );
SetValue( this, "ELEVATIO", azel + 1, AST__FLOAT, "[Deg] Telescope elevation", status );
}
/* Free resources */
map1 = astAnnul( map1 );
map2 = astAnnul( map2 );
map3 = astAnnul( map3 );
fsconv1 = astAnnul( fsconv1 );
fsconv2 = astAnnul( fsconv2 );
}
radecfrm = astAnnul( radecfrm );
azelfrm = astAnnul( azelfrm );
}
curfrm = astAnnul( curfrm );
/* Release CDELT workspace */
if( cdelt ) cdelt = (double *) astFree( (void *) cdelt );
/* Return zero or ret depending on whether an error has occurred. */
return astOK ? ok : 0;
}
static void ClassTrans( AstFitsChan *this, AstFitsChan *ret, int axlat,
int axlon, const char *method, const char *class, int *status ){
/*
* Name:
* ClassTrans
* Purpose:
* Translated non-standard FITS-CLASS headers into equivalent standard
* ones.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void ClassTrans( AstFitsChan *this, AstFitsChan *ret, int axlat,
* int axlon, const char *method, const char *class )
* Class Membership:
* FitsChan member function.
* Description:
* This function extends the functionality of the SpecTrans function,
* by converting non-standard WCS keywords into standard FITS-WCS
* keywords, using the conventions of the FITS-CLASS encoding.
* Parameters:
* this
* Pointer to the FitsChan containing the original header cards.
* ret
* Pointer to a FitsChan in which to return the standardised header
* cards.
* axlat
* Zero-based index of the celestial latitude axis.
* axlon
* Zero-based index of the celestial longitude axis.
* method
* Pointer to string holding name of calling method.
* class
* Pointer to a string holding the name of the supplied object class.
*/
/* Local Variables: */
char *cval; /* Pointer to character string */
char newtype[ 10 ]; /* New CTYPE value */
const char *keyname; /* Pointer to keyword name */
const char *ssyssrc; /* Pointer to SSYSSRC keyword value string */
double crval; /* CRVAL value */
double restfreq; /* Rest frequency (Hz) */
double v0; /* Ref channel velocity in source frame */
double vref; /* Ref channel velocity in LSR or whatever */
double vsource; /* Source velocity */
double zsource; /* Source redshift */
int axspec; /* Index of spectral axis */
/* Check the global error status. */
if ( !astOK ) return;
/* Get the rest frequency. */
restfreq = AST__BAD;
if( !GetValue2( ret, this, "RESTFRQ", AST__FLOAT, (void *) &restfreq, 0,
method, class, status ) ){
GetValue2( ret, this, "RESTFREQ", AST__FLOAT, (void *) &restfreq, 0,
method, class, status );
}
if( restfreq == AST__BAD ) {
astError( AST__BDFTS, "%s(%s): Keyword RESTFREQ not found in CLASS "
"FITS header.", status, method, class );
}
/* Get the index of the spectral axis. */
if( axlat + axlon == 1 ) {
axspec = 2;
} else if( axlat + axlon == 3 ) {
axspec = 0;
} else {
axspec = 1;
}
/* Get the spectral CTYPE value */
if( GetValue2( ret, this, FormatKey( "CTYPE", axspec + 1, -1, ' ', status ),
AST__STRING, (void *) &cval, 0, method, class, status ) ){
/* We can only handle frequency axes at the moment. */
if( !astChrMatch( "FREQ", cval ) ) {
astError( AST__BDFTS, "%s(%s): FITS-CLASS keyword %s has value "
"\"%s\" - CLASS support in AST only includes \"FREQ\" axes.", status,
method, class, FormatKey( "CTYPE", axspec + 1, -1, ' ', status ),
cval );
/* CRVAL for the spectral axis needs to be incremented by RESTFREQ if the
axis represents frequency. */
} else {
keyname = FormatKey( "CRVAL", axspec + 1, -1, ' ', status );
if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &crval, 1,
method, class, status ) ) {
crval += restfreq;
SetValue( ret, keyname, (void *) &crval, AST__FLOAT, NULL, status );
}
}
/* CLASS frequency axes describe source frame frequencies. */
cval = "SOURCE";
SetValue( ret, "SPECSYS", (void *) &cval, AST__STRING, NULL, status );
}
/* If no projection code is supplied for the longitude and latitude axes,
use "-GLS". This will be translated to "-SFL" by SpecTrans. */
keyname = FormatKey( "CTYPE", axlon + 1, -1, ' ', status );
if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, method,
class, status ) ){
if( strlen(cval) > 4 && !strncmp( " ", cval + 4, 4 ) ) {
strncpy( newtype, cval, 4 );
strcpy( newtype + 4, "-GLS" );
cval = newtype;
SetValue( ret, keyname, (void *) &cval, AST__STRING, NULL, status );
}
}
keyname = FormatKey( "CTYPE", axlat + 1, -1, ' ', status );
if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, method,
class, status ) ){
if( strlen(cval) > 4 && !strncmp( " ", cval + 4, 4 ) ) {
strncpy( newtype, cval, 4 );
strcpy( newtype + 4, "-GLS" );
cval = newtype;
SetValue( ret, keyname, (void *) &cval, AST__STRING, NULL, status );
}
}
/* Look for a keyword with name "VELO-...". This specifies the radio velocity
at the reference channel, in a standard of rest specified by the "..."
in the keyword name. If "VELO-..." is not found, look for "VLSR",
which is the same as "VELO-LSR". */
if( GetValue2( ret, this, "VELO-%3c", AST__FLOAT, (void *) &vref, 0,
method, class, status ) ||
GetValue2( ret, this, "VLSR", AST__FLOAT, (void *) &vref, 0,
method, class, status ) ){
/* Calculate the radio velocity (in the rest frame of the source) corresponding
to the frequency at the reference channel. */
v0 = AST__C*( restfreq - crval )/restfreq;
/* Assume that the source velocity is the difference between this velocity
and the reference channel velocity given by "VELO-..." */
vsource = vref - v0;
/* Get the keyword name and find the corresponding SSYSSRC keyword value. */
keyname = CardName( this, status );
if( !strcmp( keyname, "VELO-HEL" ) ) {
ssyssrc = "BARYCENT";
} else if( !strcmp( keyname, "VELO-OBS" ) || !strcmp( keyname, "VELO-TOP" ) ) {
ssyssrc = "TOPOCENT";
} else if( !strcmp( keyname, "VELO-EAR" ) || !strcmp( keyname, "VELO-GEO" ) ) {
ssyssrc = "GEOCENTR";
} else {
ssyssrc = "LSRK";
}
SetValue( ret, "SSYSSRC", (void *) &ssyssrc, AST__STRING, NULL, status );
/* Convert from radio velocity to redshift and store as ZSOURCE */
zsource = ( AST__C / (AST__C - vsource) ) - 1.0;
SetValue( ret, "ZSOURCE", (void *) &zsource, AST__FLOAT, NULL, status );
}
}
static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
* Name:
* ClearAttrib
* Purpose:
* Clear an attribute value for a FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void ClearAttrib( AstObject *this, const char *attrib, int *status )
* Class Membership:
* FitsChan 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
* FitsChan, so that the default value will subsequently be used.
* Parameters:
* this
* Pointer to the FitsChan.
* 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: */
AstFitsChan *this; /* Pointer to the FitsChan structure */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_object;
/* Check the attribute name and clear the appropriate attribute. */
/* Card. */
/* ----- */
if ( !strcmp( attrib, "card" ) ) {
astClearCard( this );
/* Encoding. */
/* --------- */
} else if ( !strcmp( attrib, "encoding" ) ) {
astClearEncoding( this );
/* CDMatrix */
/* -------- */
} else if ( !strcmp( attrib, "cdmatrix" ) ) {
astClearCDMatrix( this );
/* FitsAxisOrder. */
/* ----------- */
} else if ( !strcmp( attrib, "fitsaxisorder" ) ) {
astClearFitsAxisOrder( this );
/* FitsDigits. */
/* ----------- */
} else if ( !strcmp( attrib, "fitsdigits" ) ) {
astClearFitsDigits( this );
/* DefB1950 */
/* -------- */
} else if ( !strcmp( attrib, "defb1950" ) ) {
astClearDefB1950( this );
/* TabOK */
/* ----- */
} else if ( !strcmp( attrib, "tabok" ) ) {
astClearTabOK( this );
/* CarLin */
/* ------ */
} else if ( !strcmp( attrib, "carlin" ) ) {
astClearCarLin( this );
/* PolyTan */
/* ------- */
} else if ( !strcmp( attrib, "polytan" ) ) {
astClearPolyTan( this );
/* Iwc */
/* --- */
} else if ( !strcmp( attrib, "iwc" ) ) {
astClearIwc( this );
/* Clean */
/* ----- */
} else if ( !strcmp( attrib, "clean" ) ) {
astClearClean( this );
/* Warnings. */
/* -------- */
} else if ( !strcmp( attrib, "warnings" ) ) {
astClearWarnings( this );
/* If the name was not recognised, test if it matches any of the
read-only attributes of this class. If it does, then report an
error. */
} else if ( astOK && ( !strcmp( attrib, "ncard" ) ||
!strcmp( attrib, "allwarnings" ) ) ){
astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" "
"value for a %s.", status, attrib, astGetClass( this ) );
astError( AST__NOWRT, "This is a read-only attribute." , status);
/* 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 void ClearCard( AstFitsChan *this, int *status ){
/*
*+
* Name:
* astClearCard
* Purpose:
* Clear the Card attribute.
* Type:
* Protected virtual function.
* Synopsis:
* #include "fitschan.h"
* void astClearCard( AstFitsChan *this )
* Class Membership:
* FitsChan method.
* Description:
* This function clears the Card attribute for the supplied FitsChan by
* setting it to the index of the first un-used card in the FitsChan.
* This causes the next read operation performed on the FitsChan to
* read the first card. Thus, it is equivalent to "rewinding" the FitsChan.
* Parameters:
* this
* Pointer to the FitsChan.
* Notes:
* - This function attempts to execute even if an error has occurred.
*-
*/
/* Local Variables; */
astDECLARE_GLOBALS /* Declare the thread specific global data */
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Check the supplied FitsChan. If its is empty, return. */
if ( !this || !(this->head) ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this);
/* Set the pointer to the current card so that it points to the card at
the head of the list. */
this->card = this->head;
/* If the current card has been read into an AST object, move on to the
first card which has not, unless we are not skipping such cards. */
if( CARDUSED(this->card) ){
MoveCard( this, 1, "astClearCard", astGetClass( this ), status );
}
}
static int CnvValue( AstFitsChan *this, int type, int undef, void *buff,
const char *method, int *status ){
/*
*
* Name:
* CnvValue
* Purpose:
* Convert a data value into a given FITS data type.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int CnvValue( AstFitsChan *this, int type, int undef, void *buff,
* const char *method, int *status )
* Class Membership:
* FitsChan method.
* Description:
* This function produces a copy of the data value for the current card
* converted from its stored data type to the supplied data type.
* Parameters:
* this
* Pointer to the FitsChan.
* type
* The FITS data type in which to return the data value of the
* current card.
* undef
* Determines what happens if the current card has an undefined
* value. If "undef" is zero, an error will be reported identifying
* the undefined keyword value. If "undef" is non-zero, no error is
* reported and the contents of the output buffer are left unchanged.
* buf
* A pointer to a buffer to recieve the converted value. It is the
* responsibility of the caller to ensure that a suitable buffer is
* supplied.
* method
* Pointer to a string holding the name of the calling method.
* This is only for use in constructing error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Zero if the conversion was not possible (in which case NO error is
* reported), one otherwise.
* Notes:
* - When converting from floating point to integer, the floating
* point value is truncated using a C cast.
* - Non-zero numerical values are considered TRUE, and zero
* numerical values are considered FALSE. Any string starting with a
* 'T' or a 'Y' (upper or lower case) is considered TRUE, and anything
* starting with an 'F' or an 'N' (upper or lower case) is considered
* FALSE. In addition, a dot ('.') may be placed in front of a 'T' or an
* 'F'.
* - A logical TRUE value is represented as a real numerical value of
* one and the character string "Y". A logical FALSE value is represented
* by a real numerical value of zero and the character string "N".
* - When converting from a string to any numerical value, zero is
* returned if the string is not a formatted value which can be converted
* into the corresponding type using astSscanf.
* - Real and imaginary parts of a complex value should be separated by
* spaces within strings. If a string does contains only a single numerical
* value, it is assumed to be the real part, and the imaginary part is
* assumed to be zero.
* - When converting a complex numerical type to a non-complex numerical
* type, the returned value is derived from the real part only, the
* imaginary part is ignored.
* - Zero is returned if an error has occurred, or if this function
* should fail for any reason.
* - If the supplied value is undefined an error will be reported.
*/
/* Local Variables: */
int otype; /* Stored data type */
size_t osize; /* Size of stored data */
void *odata; /* Pointer to stored data */
/* Check the global error status, and the supplied buffer. */
if ( !astOK || !buff ) return 0;
/* Get the type in which the data value is stored. */
otype = CardType( this, status );
/* Get a pointer to the stored data value, and its size. */
osize = 0;
odata = CardData( this, &osize, status );
/* Do the conversion. */
return CnvType( otype, odata, osize, type, undef, buff,
CardName( this, status ), method, astGetClass( this ),
status );
}
static int CnvType( int otype, void *odata, size_t osize, int type, int undef,
void *buff, const char *name, const char *method,
const char *class, int *status ){
/*
*
* Name:
* CnvType
* Purpose:
* Convert a data value into a given FITS data type.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int CnvType( int otype, void *odata, size_t osize, int type, int undef,
* void *buff, const char *name, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan method.
* Description:
* This function produces a copy of the data value for the current card
* converted from its stored data type to the supplied data type.
* Parameters:
* otype
* The type of the supplied data value.
* odata
* Pointer to a buffer holding the supplied data value.
* osize
* The size of the data value (in bytes - strings include the
* terminating null).
* type
* The FITS data type in which to return the data value of the
* current card.
* undef
* Determines what happens if the supplied data value type is
* undefined If "undef" is zero, an error will be reported identifying
* the undefined keyword value. If "undef" is non-zero, no error is
* reported and the contents of the output buffer are left unchanged.
* buff
* A pointer to a buffer to recieve the converted value. It is the
* responsibility of the caller to ensure that a suitable buffer is
* supplied.
* name
* A pointer to a string holding a keyword name to include in error
* messages.
* 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 object class.
* This is only for use in constructing error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Zero if the conversion was not possible (in which case NO error is
* reported), one otherwise.
* Notes:
* - When converting from floating point to integer, the floating
* point value is truncated using a C cast.
* - Non-zero numerical values are considered TRUE, and zero
* numerical values are considered FALSE. Any string starting with a
* 'T' or a 'Y' (upper or lower case) is considered TRUE, and anything
* starting with an 'F' or an 'N' (upper or lower case) is considered
* FALSE. In addition, a dot ('.') may be placed in front of a 'T' or an
* 'F'.
* - A logical TRUE value is represented as a real numerical value of
* one and the character string "Y". A logical FALSE value is represented
* by a real numerical value of zero and the character string "N".
* - When converting from a string to any numerical value, zero is
* returned if the string isn not a formatted value which can be converted
* into the corresponding type using astSscanf.
* - Real and imaginary parts of a complex value should be separated by
* spaces within strings. If a string does contains only a single numerical
* value, it is assumed to be the real part, and the imaginary part is
* assumed to be zero.
* - When converting a complex numerical type to a non-complex numerical
* type, the returned value is derived from the real part only, the
* imaginary part is ignored.
* - Zero is returned if an error has occurred, or if this function
* should fail for any reason.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
const char *c; /* Pointer to next character */
const char *ostring; /* String data value */
double odouble; /* Double data value */
int oint; /* Integer data value */
int ival; /* Integer value read from string */
int len; /* Length of character string */
int nc; /* No. of characetsr used */
int ret; /* Returned success flag */
/* Check the global error status, and the supplied buffer. */
if ( !astOK || !buff ) return 0;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(NULL);
/* Assume success. */
ret = 1;
/* If the supplied data type is undefined, report an error unless the
returned data type is also undefined or an undefined value is
acceptable for the keyword. */
if( otype == AST__UNDEF ) {
if( type != AST__UNDEF && !undef ) {
ret = 0;
astError( AST__FUNDEF, "The FITS keyword '%s' has an undefined "
"value.", status, name );
}
/* If the returned data type is undefined, the returned value is
immaterial, so leave the buffer contents unchanged. */
} else if( type == AST__UNDEF ) {
/* If there is no data value and this is not a COMMENT keyword, or if
there is a data value and this is a COMMENT card, conversion is not
possible. */
} else if( ( odata && otype == AST__COMMENT ) ||
( !odata && otype != AST__COMMENT ) ) {
ret = 0;
/* If there is no data value (and therefore this is a comment card),
conversion is only possible if the output type is also a comment. */
} else if( !odata ) {
if( type != AST__COMMENT ) ret = 0;
/* Otherwise we have a data value, so do each possible combination of
supplied and stored data types... */
} else {
/* Convert a AST__FLOAT data value to ... */
if( otype == AST__FLOAT ){
odouble = *( (double *) odata );
if( type == AST__FLOAT ){
(void) memcpy( buff, odata, osize );
} else if( type == AST__STRING || type == AST__CONTINUE ){
if( odouble != AST__BAD ) {
(void) sprintf( cnvtype_text, "%.*g", DBL_DIG, odouble );
CheckZero( cnvtype_text, odouble, 0, status );
} else {
strcpy( cnvtype_text, BAD_STRING );
}
*( (char **) buff ) = cnvtype_text;
} else if( type == AST__INT ){
*( (int *) buff ) = (int) odouble;
} else if( type == AST__LOGICAL ){
*( (int *) buff ) = ( odouble == 0.0 ) ? 0 : 1;
} else if( type == AST__COMPLEXF ){
( (double *) buff )[ 0 ] = odouble;
( (double *) buff )[ 1 ] = 0.0;
} else if( type == AST__COMPLEXI ){
( (int *) buff )[ 0 ] = (int) odouble;
( (int *) buff )[ 1 ] = 0;
} else if( astOK ){
ret = 0;
astError( AST__INTER, "CnvType: AST internal programming error - "
"FITS data-type no. %d not yet supported.", status, type );
}
/* Convert a AST__STRING data value to ... */
} else if( otype == AST__STRING || type == AST__CONTINUE ){
ostring = (char *) odata;
len = (int) strlen( ostring );
if( type == AST__FLOAT ){
if( nc = 0,
( 0 == astSscanf( ostring, BAD_STRING " %n", &nc ) )
&& (nc >= len ) ){
*( (double *) buff ) = AST__BAD;
} else if( nc = 0,
( 1 != astSscanf( ostring, "%lf %n", (double *) buff, &nc ) )
|| (nc < len ) ){
ret = 0;
}
} else if( type == AST__STRING || type == AST__CONTINUE ){
strncpy( cnvtype_text, (char *) odata, AST__FITSCHAN_FITSCARDLEN );
*( (char **) buff ) = cnvtype_text;
} else if( type == AST__INT ){
if( nc = 0,
( 1 != astSscanf( ostring, "%d %n", (int *) buff, &nc ) )
|| (nc < len ) ){
ret = 0;
}
} else if( type == AST__LOGICAL ){
if( nc = 0,
( 1 == astSscanf( ostring, "%d %n", &ival, &nc ) )
&& (nc >= len ) ){
*( (int *) buff ) = ival ? 1 : 0;
} else {
c = ostring;
while( *c && isspace( (int) *c ) ) c++;
if( *c == 'y' || *c == 'Y' || *c == 't' || *c == 'T' ||
( *c == '.' && ( c[1] == 't' || c[1] == 'T' ) ) ){
*( (int *) buff ) = 1;
} else if( *c == 'n' || *c == 'N' || *c == 'f' || *c == 'F' ||
( *c == '.' && ( c[1] == 'f' || c[1] == 'F' ) ) ){
*( (int *) buff ) = 0;
} else {
ret = 0;
}
}
} else if( type == AST__COMPLEXF ){
if( nc = 0,
( 1 != astSscanf( ostring, "%lf %lf %n", (double *) buff,
(double *) buff + 1, &nc ) )
|| (nc < len ) ){
if( nc = 0,
( 1 != astSscanf( ostring, "%lf %n", (double *) buff,
&nc ) )
|| (nc < len ) ){
ret = 0;
} else {
( (double *) buff )[ 1 ] = 0.0;
}
}
} else if( type == AST__COMPLEXI ){
if( nc = 0,
( 1 != astSscanf( ostring, "%d %d %n", (int *) buff,
(int *) buff + 1, &nc ) )
|| (nc < len ) ){
if( nc = 0,
( 1 != astSscanf( ostring, "%d %n", (int *) buff, &nc ) )
|| (nc < len ) ){
ret = 0;
} else {
( (int *) buff )[ 1 ] = 0;
}
}
} else if( astOK ){
ret = 0;
astError( AST__INTER, "CnvType: AST internal programming error - "
"FITS data-type no. %d not yet supported.", status, type );
}
/* Convert an AST__INT data value to ... */
} else if( otype == AST__INT ){
oint = *( (int *) odata );
if( type == AST__FLOAT ){
*( (double *) buff ) = (double) oint;
} else if( type == AST__STRING || type == AST__CONTINUE ){
(void) sprintf( cnvtype_text, "%d", oint );
*( (char **) buff ) = cnvtype_text;
} else if( type == AST__INT ){
(void) memcpy( buff, odata, osize );
} else if( type == AST__LOGICAL ){
*( (int *) buff ) = oint ? 1 : 0;
} else if( type == AST__COMPLEXF ){
( (double *) buff )[ 0 ] = (double) oint;
( (double *) buff )[ 1 ] = 0.0;
} else if( type == AST__COMPLEXI ){
( (int *) buff )[ 0 ] = oint;
( (int *) buff )[ 1 ] = 0;
} else if( astOK ){
ret = 0;
astError( AST__INTER, "CnvType: AST internal programming error - "
"FITS data-type no. %d not yet supported.", status, type );
}
/* Convert a LOGICAL data value to ... */
} else if( otype == AST__LOGICAL ){
oint = *( (int *) odata );
if( type == AST__FLOAT ){
*( (double *) buff ) = oint ? 1.0 : 0.0;
} else if( type == AST__STRING || type == AST__CONTINUE ){
if( oint ){
strcpy( cnvtype_text, "Y" );
} else {
strcpy( cnvtype_text, "N" );
}
*( (char **) buff ) = cnvtype_text;
} else if( type == AST__INT ){
*( (int *) buff ) = oint;
} else if( type == AST__LOGICAL ){
(void) memcpy( buff, odata, osize );
} else if( type == AST__COMPLEXF ){
( (double *) buff )[ 0 ] = oint ? 1.0 : 0.0;
( (double *) buff )[ 1 ] = 0.0;
} else if( type == AST__COMPLEXI ){
( (int *) buff )[ 0 ] = oint ? 1 : 0;
( (int *) buff )[ 1 ] = 0;
} else if( astOK ){
ret = 0;
astError( AST__INTER, "CnvType: AST internal programming error - "
"FITS data-type no. %d not yet supported.", status, type );
}
/* Convert a AST__COMPLEXF data value to ... */
} else if( otype == AST__COMPLEXF ){
odouble = ( (double *) odata )[ 0 ];
if( type == AST__FLOAT ){
*( (double *) buff ) = odouble;
} else if( type == AST__STRING || type == AST__CONTINUE ){
(void) sprintf( cnvtype_text0, "%.*g", DBL_DIG, ( (double *) odata )[ 0 ] );
CheckZero( cnvtype_text0, ( (double *) odata )[ 0 ], 0, status );
(void) sprintf( cnvtype_text1, "%.*g", DBL_DIG, ( (double *) odata )[ 1 ] );
CheckZero( cnvtype_text1, ( (double *) odata )[ 1 ], 0, status );
(void) sprintf( cnvtype_text, "%s %s", cnvtype_text0, cnvtype_text1 );
*( (char **) buff ) = cnvtype_text;
} else if( type == AST__INT ){
*( (int *) buff ) = (int) odouble;
} else if( type == AST__LOGICAL ){
*( (int *) buff ) = ( odouble == 0.0 ) ? 0 : 1;
} else if( type == AST__COMPLEXF ){
(void) memcpy( buff, odata, osize );
} else if( type == AST__COMPLEXI ){
( (int *) buff )[ 0 ] = (int) odouble;
( (int *) buff )[ 1 ] = (int) ( (double *) odata )[ 1 ];
} else if( astOK ){
ret = 0;
astError( AST__INTER, "CnvType: AST internal programming error - "
"FITS data-type no. %d not yet supported.", status, type );
}
/* Convert a AST__COMPLEXI data value to ... */
} else if( otype == AST__COMPLEXI ){
oint = ( (int *) odata )[ 0 ];
if( type == AST__FLOAT ){
*( (double *) buff ) = (double) oint;
} else if( type == AST__STRING || type == AST__CONTINUE ){
(void) sprintf( cnvtype_text, "%d %d", ( (int *) odata )[ 0 ],
( (int *) odata )[ 1 ] );
*( (char **) buff ) = cnvtype_text;
} else if( type == AST__INT ){
*( (int *) buff ) = oint;
} else if( type == AST__LOGICAL ){
*( (int *) buff ) = oint ? 1 : 0;
} else if( type == AST__COMPLEXF ){
( (double *) buff )[ 0 ] = (double) oint;
( (double *) buff )[ 1 ] = (double) ( (int *) odata )[ 1 ];
} else if( type == AST__COMPLEXI ){
(void) memcpy( buff, odata, osize );
} else if( astOK ){
ret = 0;
astError( AST__INTER, "CnvType: AST internal programming error - "
"FITS data-type no. %d not yet supported.", status, type );
}
} else if( astOK ){
ret = 0;
astError( AST__INTER, "CnvType: AST internal programming error - "
"FITS data-type no. %d not yet supported.", status, type );
}
}
return ret;
}
static int ComBlock( AstFitsChan *this, int incr, const char *method,
const char *class, int *status ){
/*
* Name:
* ComBlock
* Purpose:
* Delete a AST comment block in a Native-encoded FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int ComBlock( AstFitsChan *this, int incr, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function looks for a block of comment cards as defined below,
* and deletes all the cards in the block, if a suitable block is found.
*
* Comment blocks consist of a contiguous sequence of COMMENT cards. The
* text of each card should start and end with the 3 characters "AST".
* The block is delimited above by a card containing all +'s (except
* for the two "AST" strings), and below by a card containing all -'s.
*
* The block is assumed to start on the card which is adjacent to the
* current card on entry.
* Parameters:
* this
* Pointer to the FitsChan.
* incr
* This should be either +1 or -1, and is the increment between
* adjacent cards in the comment block. A value of +1 means
* that the card following the current card is taken as the first in
* the block, and subsequent cards are checked. The block must then
* end with a line of -'s. If -1 is supplied, then the card
* preceding the current card is taken as the first in the block,
* and preceding cards are checked. The block must then end with
* a row of +'s.
* 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:
* 1 if a block was found and deleted, 0 otherwise.
* Notes:
* - The pointer to the current card is returned unchanged.
*/
/* Local Variables: */
FitsCard *card0; /* Pointer to current FitsCard on entry */
char del; /* Delimiter character */
char *text; /* Pointer to the comment text */
int i; /* Card index within the block */
int ncard; /* No. of cards in the block */
int ret; /* The returned flag */
size_t len; /* Length of the comment text */
/* Check the global status. */
if( !astOK ) return 0;
/* Save the pointer to the current card. */
card0 = this->card;
/* Initialise the returned flag to indicate that we have not found a
comment block. */
ret = 0;
/* Move on to the first card in the block. If this is not possible (due to
us already being at the start or end of the FitsChan), then return. */
if( MoveCard( this, incr, method, class, status ) == 1 ) {
/* Store the character which is used in the delimiter line for the
comment block. */
del = ( incr == 1 ) ? '-' : '+';
/* Initialise the number of cards in the comment block to zero. */
ncard = 0;
/* Loop round until the end (or start) of the comment block is found.
Leave the loop if an error occurs. */
while( astOK ) {
/* Is this card a comment card? If not, then we have failed to find a
complete comment block. Break out of the loop. */
if( CardType( this, status ) != AST__COMMENT ) break;
/* Increment the number of cards in the comment block. */
ncard++;
/* Get the text of the comment, and its length. */
text = CardComm( this, status );
if( text ){
len = strlen( text );
/* Check the first 3 characters. Break out of the loop if they are not
"AST". */
if( strncmp( "AST", text, 3 ) ) break;
/* Check the last 3 characters. Break out of the loop if they are not
"AST". */
if( strcmp( "AST", text + len - 3 ) ) break;
/* If the comment is the appropriate block delimiter (a line of +'s or
-'s depending on the direction), then set the flag to indicate that we
have a complete comment block and leave the loop. Allow spaces to be
included. Exclude the "AST" strings at begining and end from the check. */
ret = 1;
for( i = 3; i < len - 3; i++ ) {
if( text[ i ] != del && text[ i ] != ' ' ) {
ret = 0;
break;
}
}
}
if( ret ) break;
/* Move on to the next card. If this is not possible (due to us already
being at the start or end of the FitsChan), then break out of the loop. */
if( MoveCard( this, incr, method, class, status ) == 0 ) break;
}
/* Re-instate the original current card. */
this->card = card0;
/* If we found a complete comment block, mark it (which is equivalent to
deleting it except that memory of the cards location within the
FitsChan is preserved for future use), and then re-instate the original
current card. */
if( ret && astOK ) {
for( i = 0; i < ncard; i++ ) {
MoveCard( this, incr, method, class, status );
MarkCard( this, status );
}
this->card = card0;
}
}
/* If an error occurred, indicate that coment block has been deleted. */
if( !astOK ) ret = 0;
return ret;
}
static char *ConcatWAT( AstFitsChan *this, int iaxis, const char *method,
const char *class, int *status ){
/*
* Name:
* ConcatWAT
* Purpose:
* Concatenate all the IRAF "WAT" keywords for an axis.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* char *ConcatWAT( AstFitsChan *this, int iaxis, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function searches the supplied FitsChan for any keywords of
* the form "WATi_j", where i and j are integers and i is equal to the
* supplied "iaxis" value plus one, and concatenates their string
* values into a single string. Such keywords are created by IRAF to
* describe their non-standard ZPX and TNX projections.
* Parameters:
* this
* The FistChan.
* iaxis
* The zero-based index of the axis to be retrieved.
* method
* The name of the calling method to include in error messages.
* class
* The object type to include in error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to a dynamically allocated, null terminated string
* containing a copy of the concatentated WAT values. This string must
* be freed by the caller (using astFree) when no longer required.
*
* A NULL pointer will be returned if there are no WAT kewyords for
* the requested axis in the FitsChan.
* 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 keyname[ FITSNAMLEN + 5 ];/* Keyword name */
char *wat; /* Pointer to a single WAT string */
char *result; /* Returned string */
int watlen; /* Length of total WAT string (inc. term null)*/
int j; /* WAT index */
size_t size; /* Length of string value */
/* Initialise returned value. */
result = NULL;
/* Check inherited status */
if( !astOK ) return result;
/* Rewind the FitsChan. */
astClearCard( this );
/* Concatenate all the IRAF "WAT" keywords together for this axis. These
keywords are marked as having been used, so that they are not written
out when the FitsChan is deleted. */
watlen = 1;
j = 1;
size = 0;
sprintf( keyname, "WAT%d_%.3d", iaxis + 1, j );
while( astOK ) {
/* Search forward from the current card for the next WAT card. If no
found, try searching again from the start of the FitsChan. If not found
evenm then, break. */
if( ! FindKeyCard( this, keyname, method, class, status ) ) {
astClearCard( this );
if( ! FindKeyCard( this, keyname, method, class, status ) ) break;
}
wat = (char *) CardData( this, &size, status );
result = (char *) astRealloc( (void *) result,
watlen - 1 + size );
if( result ) {
strcpy( result + watlen - 1, wat );
watlen += size - 1;
MarkCard( this, status );
MoveCard( this, 1, method, class, status );
j++;
sprintf( keyname, "WAT%d_%.3d", iaxis + 1, j );
} else {
break;
}
}
/* Return the result. */
return result;
}
static int CountFields( const char *temp, char type, const char *method,
const char *class, int *status ){
/*
* Name:
* CountFields
* Purpose:
* Count the number of field specifiers in a template string.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int CountFields( const char *temp, char type, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function returns the number of fields which include the
* specified character type in the supplied string.
* Parameters:
* temp
* Pointer to a null terminated string holding the template.
* type
* A single character giving the field type to be counted (e.g.
* 'd', 'c' or 'f').
* 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 number of fields.
* Notes:
* - No error is reported if the parameter "type" is not a valid
* field type specifier, but zero will be returned.
* - An error is reported if the template has any invalid field
* specifiers in it.
* - A value of zero is returned if an error has already occurred,
* or if this function should fail for any reason.
*/
/* Local Variables: */
const char *b; /* Pointer to next template character */
int nf; /* No. of fields found so far */
/* Check global status. */
if( !astOK ) return 0;
/* Initialise a pointer to the start of the template string. */
b = temp;
/* Initialise the number of fields found so far. */
nf = 0;
/* Go through the string. */
while( *b && astOK ){
/* If the current character is a '%', a field is starting. */
if( *b == '%' ){
/* Skip over the field width (if supplied). */
if( isdigit( (int) *(++b) ) ) b++;
/* Report an error if the end of the string occurs within the field. */
if( !*b ) {
astError( AST__BDFMT, "%s(%s): Incomplete field specifier found "
"at end of filter template '%s'.", status, method, class,
temp );
break;
/* Report an error if the field type is illegal. */
} else if( *b != 'd' && *b != 'c' && *b != 'f' ) {
astError( AST__BDFMT, "%s(%s): Illegal field type or width "
"specifier '%c' found in filter template '%s'.", status,
method, class, *b, temp );
break;
}
/* Compare the field type with the supplied type, and increment the
number of fields found if it is the correct type. */
if( *b == type ) nf++;
}
/* Move on to the next character. */
b++;
}
/* If an error has occurred, return 0. */
if( !astOK ) nf = 0;
/* Return the answer. */
return nf;
}
static void CreateKeyword( AstFitsChan *this, const char *name,
char keyword[ FITSNAMLEN + 1 ], int *status ){
/*
* Name:
* CreateKeyword
* Purpose:
* Create a unique un-used keyword for a FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void CreateKeyword( AstFitsChan *this, const char *name,
* char keyword[ FITSNAMLEN + 1 ], int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function takes a name which forms the basis of a FITS
* keyword and appends a sequence number (encoded as a pair of
* legal FITS keyword characters) so as to generate a unique FITS
* keyword which has not previously been used in the FitsChan
* supplied.
*
* It is intended for use when several keywords with the same name
* must be stored in a FitsChan, since to comply strictly with the
* FITS standard keywords should normally be unique (otherwise
* external software which processes the keywords might omit one or
* other of the values).
*
* An attempt is also made to generate keywords in a form that is
* unlikely to clash with those from other sources (in as far as
* this is possible with FITS). In any event, a keyword that
* already appears in the FitsChan will not be re-used.
* Parameters:
* this
* Pointer to the FitsChan.
* name
* Pointer to a constant null-terminated string containing the
* name on which the new keyword should be based. This should be
* a legal FITS keyword in itself, except that it should be at
* least two characters shorter than the maximum length, in
* order to accommodate the sequence number characters.
*
* If this string is too long, it will be silently
* truncated. Mixed case is permitted, as all characters
* supplied are converted to upper case before use.
* keyword
* A character array in which the generated unique keyword will
* be returned, null terminated.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
const char *seq_chars = SEQ_CHARS;/* Pointer to characters used for encoding */
char seq_char; /* The first sequence character */
const char *class; /* Object clas */
int found; /* Keyword entry found in list? */
int limit; /* Sequence number has reached limit? */
int nc; /* Number of basic keyword characters */
int seq; /* The sequence number */
/* Check the global error status. */
if( !astOK ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this);
/* Store the object class. */
class = astGetClass( this );
/* On the first invocation only, determine the number of characters
being used to encode sequence number information and save this
value. */
if( createkeyword_seq_nchars < 0 ) createkeyword_seq_nchars = (int) strlen( seq_chars );
/* Copy the name supplied into the output array, converting to upper
case. Leave space for two characters to encode a sequence
number. Terminate the resulting string. */
for( nc = 0; ( nc < ( FITSNAMLEN - 2 ) ) && name[ nc ]; nc++ ) {
keyword[ nc ] = toupper( name[ nc ] );
}
keyword[ nc ] = '\0';
/* We now search the list of sequence numbers already allocated to
find the next one to use for this keyword. */
if( this->keyseq ) {
found = astMapGet0I( this->keyseq, keyword, &seq );
} else {
found = 0;
this->keyseq = astKeyMap( " ", status );
}
/* If the keyword was not found in the list, create a new list entry
to describe it. */
if( !found ) seq = 0;
/* If OK, loop to find a new sequence number which results in a FITS
keyword that hasn't already been used to store data in the
FitsChan. */
if( astOK ) {
while( 1 ) {
/* Determine if the sequence number just obtained has reached the
upper limit. This is unlikely to happen in practice, but if it
does, we simply re-use this maximum value. Otherwise, we increment
the sequence number last used for this keyword to obtain a new
one. */
limit = ( seq >= ( createkeyword_seq_nchars * createkeyword_seq_nchars - 1 ) );
if( !limit ) seq++;
/* Encode the sequence number into two characters and append them to
the original keyword (with a terminating null). */
seq_char = seq_chars[ seq / createkeyword_seq_nchars ];
keyword[ nc ] = seq_char;
keyword[ nc + 1 ] = seq_chars[ seq % createkeyword_seq_nchars ];
keyword[ nc + 2 ] = '\0';
/* If the upper sequence number limit has not been reached, try to
look up the resulting keyword in the FitsChan to see if it has
already been used. Quit searching when a suitable keyword is
found. */
if ( limit || !HasCard( this, keyword, "astWrite", class, status ) ) break;
}
/* Store the update sequence number in the keymap. The keys into this
keymap are the base keyword name without the appended sequence string, so
temporaily terminate the returned keyword name to exclude the sequence
string. */
keyword[ nc ] = '\0';
astMapPut0I( this->keyseq, keyword, seq, NULL );
keyword[ nc ] = seq_char;
}
}
static double DateObs( const char *dateobs, int *status ) {
/*
* Name:
* DateObs
* Purpose:
* Convert a FITS DATE-OBS keyword value to a MJD.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* double DateObs( const char *dateobs, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Extracts the date and time fields from the supplied string and converts
* them into a modified Julian Date. Supports both old "dd/mm/yy"
* format, and the new "ccyy-mm-ddThh:mm:ss[.sss...]" format.
* Parameters:
* dateobs
* Pointer to the DATE-OBS string.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The Modified Julian Date corresponding to the supplied DATE-OBS
* string.
* Notes:
* - The value AST__BAD is returned (without error) if the supplied
* string does not conform to the requirements of a FITS DATE-OBS value,
* or if an error has already occurred.
*/
/* Local Variables: */
double days; /* The hours, mins and secs as a fraction of a day */
double ret; /* The returned MJD value */
double secs; /* The total value of the two seconds fields */
int dd; /* The day field from the supplied string */
int fsc; /* The fractional seconds field from the supplied string */
int hr; /* The hour field from the supplied string */
int j; /* SLALIB status */
int len; /* The length of the supplied string */
int mm; /* The month field from the supplied string */
int mn; /* The minute field from the supplied string */
int nc; /* Number of characters used */
int ok; /* Was the string of a legal format? */
int rem; /* The least significant digit in fsc */
int sc; /* The whole seconds field from the supplied string */
int yy; /* The year field from the supplied string */
/* Check the global status. */
if( !astOK ) return AST__BAD;
/* Initialise the returned value. */
ret = AST__BAD;
/* Save the length of the supplied string. */
len = (int) strlen( dateobs );
/* Extract the year, month, day, hour, minute, second and fractional
seconds fields from the supplied string. Assume initially that the
string does not match any format. */
ok = 0;
/* First check for the old "dd/mm/yy" format. */
if( nc = 0,
( astSscanf( dateobs, " %2d/%2d/%d %n", &dd, &mm, &yy, &nc ) == 3 ) &&
( nc >= len ) ){
ok = 1;
hr = 0;
mn = 0;
sc = 0;
fsc = 0;
/* Otherwise, check for the new short format "ccyy-mm-dd". */
} else if( nc = 0,
( astSscanf( dateobs, " %4d-%2d-%2d %n", &yy, &mm, &dd, &nc ) == 3 ) &&
( nc >= len ) ){
ok = 1;
hr = 0;
mn = 0;
sc = 0;
fsc = 0;
/* Otherwise, check for the new format "ccyy-mm-ddThh:mm:ss" without a
fractional seconds field or the trailing Z. */
} else if( nc = 0,
( astSscanf( dateobs, " %4d-%2d-%2dT%2d:%2d:%2d %n", &yy, &mm, &dd,
&hr, &mn, &sc, &nc ) == 6 ) && ( nc >= len ) ){
ok = 1;
fsc = 0;
/* Otherwise, check for the new format "ccyy-mm-ddThh:mm:ss.sss" with a
fractional seconds field but without the trailing Z. */
} else if( nc = 0,
( astSscanf( dateobs, " %4d-%2d-%2dT%2d:%2d:%2d.%d %n", &yy, &mm, &dd,
&hr, &mn, &sc, &fsc, &nc ) == 7 ) && ( nc >= len ) ){
ok = 1;
/* Otherwise, check for the new format "ccyy-mm-ddThh:mm:ssZ" without a
fractional seconds field but with the trailing Z. */
} else if( nc = 0,
( astSscanf( dateobs, " %4d-%2d-%2dT%2d:%2d:%2dZ %n", &yy, &mm, &dd,
&hr, &mn, &sc, &nc ) == 6 ) && ( nc >= len ) ){
ok = 1;
fsc = 0;
/* Otherwise, check for the new format "ccyy-mm-ddThh:mm:ss.sssZ" with a
fractional seconds field and the trailing Z. */
} else if( nc = 0,
( astSscanf( dateobs, " %4d-%2d-%2dT%2d:%2d:%2d.%dZ %n", &yy, &mm, &dd,
&hr, &mn, &sc, &fsc, &nc ) == 7 ) && ( nc >= len ) ){
ok = 1;
}
/* If the supplied string was legal, create a MJD from the separate fields. */
if( ok ) {
/* Get the MJD at the start of the day. */
palCaldj( yy, mm, dd, &ret, &j );
/* If succesful, convert the hours, minutes and seconds to a fraction of
a day, and add it onto the MJD found above. */
if( j == 0 ) {
/* Obtain a floating point representation of the fractional seconds
field. */
secs = 0.0;
while ( fsc > 0 ) {
rem = ( fsc % 10 );
fsc /= 10;
secs = 0.1 * ( secs + (double) rem );
}
/* Add on the whole seconds field. */
secs += (double) sc;
/*Convert the hours, minutes and seconds to a fractional day. */
palDtf2d( hr, mn, secs, &days, &j );
/* If succesful, add this onto the returned MJD. */
if( j == 0 ) {
ret = ret + days;
/* If the conversion to MJD failed, return AST__BAD. */
} else {
ret = AST__BAD;
}
} else {
ret = AST__BAD;
}
}
/* Return the result. */
return ret;
}
static void DeleteCard( AstFitsChan *this, const char *method,
const char *class, int *status ){
/*
* Name:
* DeleteCard
* Purpose:
* Delete the current card from a FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void DeleteCard( AstFitsChan *this, const char *method,
* const char *class )
* Class Membership:
* FitsChan member function.
* Description:
* The current card is removed from the circular linked list of structures
* stored in the supplied FitsChan, and the memory used to store the
* structure is then freed.
* Parameters:
* this
* Pointer to the FitsChan containing the list.
* method
* Name of calling method.
* class
* Object class.
* Notes:
* - This function returns without action if the FitsChan is
* currently at "end-of-file".
* - The next card becomes the current card.
* - This function attempts to execute even if an error has occurred.
*/
/* Local Variables: */
FitsCard *card; /* Pointer to the current card */
FitsCard *next; /* Pointer to next card in list */
FitsCard *prev; /* Pointer to previous card in list */
/* Return if the supplied object or current card is NULL. */
if( !this || !this->card ) return;
/* Get a pointer to the card to be deleted (the current card). */
card = (FitsCard *) this->card;
/* Remove it from the KeyMap holding all keywords. */
astMapRemove( this->keywords, card->name );
/* Move the current card on to the next card. */
MoveCard( this, 1, method, class, status );
/* Save pointers to the previous and next cards in the list. */
prev = GetLink( card, PREVIOUS, method, class, status );
next = GetLink( card, NEXT, method, class, status );
/* If the backwards link points back to the supplied card, then it must
be the only one left on the list. */
if( prev == card ) prev = NULL;
if( next == card ) next = NULL;
/* If the list head is to be deleted, store a value for the new list
head. */
if( this->head == (void *) card ) this->head = (void *) next;
/* Free the memory used to hold the data value. */
(void) astFree( card->data );
/* Free the memory used to hold any comment. */
if( card->comment ) (void) astFree( (void *) card->comment );
/* Free the memory used to hold the whole structure. */
(void) astFree( (void *) card );
/* Fix up the links between the two adjacent cards in the list, unless the
supplied card was the last one in the list. */
if( prev && next ){
next->prev = prev;
prev->next = next;
} else {
this->head = NULL;
this->card = NULL;
}
/* Return. */
return;
}
static void DelFits( AstFitsChan *this, int *status ){
/*
*++
* Name:
c astDelFits
f AST_DELFITS
* Purpose:
* Delete the current FITS card in a FitsChan.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c void astDelFits( AstFitsChan *this )
f CALL AST_DELFITS( THIS, STATUS )
* Class Membership:
* FitsChan method.
* Description:
c This function deletes the current FITS card from a FitsChan. The
f This routine deletes the current FITS card from a FitsChan. The
* current card may be selected using the Card attribute (if its index
c is known) or by using astFindFits (if only the FITS keyword is
f is known) or by using AST_FINDFITS (if only the FITS keyword is
* known).
*
* After deletion, the following card becomes the current card.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Notes:
* - This function returns without action if the FitsChan is
* initially positioned at the "end-of-file" (i.e. if the Card
* attribute exceeds the number of cards in the FitsChan).
* - If there are no subsequent cards in the FitsChan, then the
* Card attribute is left pointing at the "end-of-file" after
* deletion (i.e. is set to one more than the number of cards in
* the FitsChan).
*--
*/
/* Check the global error status. */
if ( !astOK ) return;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Delete the current card. The next card will be made the current card. */
DeleteCard( this, "astDelFits", astGetClass( this ), status );
}
static void DistortMaps( AstFitsChan *this, FitsStore *store, char s,
int naxes, AstMapping **map1, AstMapping **map2,
AstMapping **map3, AstMapping **map4,
const char *method, const char *class, int *status ){
/*
* Name:
* DistortMap
* Purpose:
* Create a Mapping representing a FITS-WCS Paper IV distortion code.
* Type:
* Private function.
* Synopsis:
* void DistortMaps( AstFitsChan *this, FitsStore *store, char s,
* int naxes, AstMapping **map1, AstMapping **map2,
* AstMapping **map3, AstMapping **map4,
* const char *method, const char *class )
* Class Membership:
* FitsChan
* Description:
* This function checks the CTYPE keywords in the supplied FitsStore to see
* if they contain a known distortion code (following the syntax described
* in FITS-WCS paper IV). If so, Mappings are returned which represent the
* distortions to be applied at each stage in the pixel->IWC chain. If
* any distortion codes are found in the FitsStore CTYPE values, whether
* recognised or not, the CTYPE values in the FitsStore are modified to
* remove the distortion code. Warnings about any unknown or inappropriate
* distortion codes are added to the FitsChan.
* Parameters:
* this
* The FitsChan. ASTWARN cards may be added to this FitsChan if any
* anomalies are found in the keyword values in the FitsStore.
* store
* A structure containing information about the requested axis
* descriptions derived from a FITS header.
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* naxes
* The number of intermediate world coordinate axes (WCSAXES).
* map1
* Address of a location at which to store a pointer to a Mapping
* which describes any distortion to be applied to pixel
* coordinates, prior to performing the translation specified by the
* CRPIXj keywords. NULL is returned if no distortion is necessary.
* map2
* Address of a location at which to store a pointer to a Mapping
* which describes any distortion to be applied to translated pixel
* coordinates, prior to performing the PC matrix multiplication.
* NULL is returned if no distortion is necessary.
* map3
* Address of a location at which to store a pointer to a Mapping
* which describes any distortion to be applied to unscaled IWC
* coordinates, prior to performing the CDELT matrix multiplication.
* NULL is returned if no distortion is necessary.
* map4
* Address of a location at which to store a pointer to a Mapping
* which describes any distortion to be applied to scaled IWC
* coordinates, after performing the CDELT matrix multiplication.
* NULL is returned if no distortion is necessary.
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
*/
/* Local Variables: */
AstMapping *tmap1; /* Mapping pointer */
AstMapping *tmap2; /* Mapping pointer */
char *ctype; /* Pointer to CTYPE value */
char code[ 4 ]; /* Projection code extracted from CTYPE */
char dist[ 4 ]; /* Distortion code extracted from CTYPE */
char msgbuf[ 250 ]; /* Buffer for warning message */
char type[ 5 ]; /* Axis type extracted from CTYPE */
double *dim; /* Array holding array dimensions */
int found_axes[ 2 ]; /* Index of axes with the distortion code */
int i; /* FITS axis index */
int nc; /* No. of characters in CTYPE without "-SIP" */
int nfound; /* No. of axes with the distortion code */
int warned; /* Have any ASTWARN cards been issued? */
/* Initialise pointers to the returned Mappings. */
*map1 = NULL;
*map2 = NULL;
*map3 = NULL;
*map4 = NULL;
/* Check the global status. */
if ( !astOK ) return;
/* Allocate memory to hold the image dimensions. */
dim = (double *) astMalloc( sizeof(double)*naxes );
if( dim ){
/* Note the image dimensions, if known. If not, store AST__BAD values. */
for( i = 0; i < naxes; i++ ){
if( !astGetFitsF( this, FormatKey( "NAXIS", i + 1, -1, ' ', status ),
dim + i ) ) dim[ i ] = AST__BAD;
}
/* First check each known distortion type... */
/* "-SIP": Spitzer (http://irsa.ipac.caltech.edu/data/SPITZER/docs/files/spitzer/shupeADASS.pdf)
============= */
/* Spitzer distortion is limited to 2D. Check the first two axes to see if
they have "-SIP" codes at the end of their CTYPE values. If they do,
terminate the ctype string in order to exclude the distortion code (this
is so that later functions do not need to allow for the possibility of a
distortion code being present in the CTYPE value). */
ctype = GetItemC( &(store->ctype), 0, 0, s, NULL, method, class, status );
if( ctype ){
nc = astChrLen( ctype ) - 4;
if( nc >= 0 && !strcmp( ctype + nc, "-SIP" ) ) {
ctype[ nc ] = 0;
ctype = GetItemC( &(store->ctype), 1, 0, s, NULL, method, class, status );
if( ctype ) {
nc = astChrLen( ctype ) - 4;
if( nc >= 0 && !strcmp( ctype + nc, "-SIP" ) ) {
ctype[ nc ] = 0;
/* Create a Mapping describing the distortion (other axes are passed
unchanged by this Mapping), and add it in series with the returned map2
(Spitzer distortion is applied to the translated pixel coordinates). */
tmap1 = SIPMapping( dim, store, s, naxes, method, class, status );
if( ! *map2 ) {
*map2 = tmap1;
} else {
tmap2 = (AstMapping *) astCmpMap( *map2, tmap1, 1, "", status );
*map2 = astAnnul( *map2 );
tmap1 = astAnnul( tmap1 );
*map2 = tmap2;
}
}
}
}
}
/* Check that the "-SIP" code is not included in any axes other than axes
0 and 1. Issue a warning if it is, and remove it. */
warned = 0;
for( i = 2; i < naxes; i++ ){
ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
if( ctype ){
nc = astChrLen( ctype ) - 4;
if( nc >= 0 && !strcmp( ctype + nc, "-SIP" ) ) {
if( !warned ){
warned = 1;
sprintf( msgbuf, "The \"-SIP\" distortion code can only be "
"used on axes 1 and 2, but was found in keyword "
"%s (='%s'). The distortion will be ignored.",
FormatKey( "CTYPE", i + 1, -1, ' ', status ), ctype );
Warn( this, "distortion", msgbuf, method, class, status );
}
ctype[ nc ] = 0;
}
}
}
/* "-ZPX": IRAF (http://iraf.noao.edu/projects/ccdmosaic/zpx.html)
============= */
/* An IRAF ZPX header uses a ZPX projection within each CTYPE value in place
of the basic ZPN projection. The SpecTrans function converts -ZPX" to
"-ZPN-ZPX" (i.e. a basic projection of ZPN with a distortion code of
"-ZPX"). This function then traps and processes the "-ZPX" distortion
code. */
/* Look for axes that have the "-ZPX" code in their CTYPE values. If any
are found, check that there are exactly two such axes, and terminate the
ctype strings in order to exclude the distortion code (this is so that
later functions do not need to allow for the possibility of a distortion
code being present in the CTYPE value)*/
nfound = 0;
for( i = 0; i < naxes; i++ ){
ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
if( ctype && 3 == astSscanf( ctype, "%4s-%3s-%3s", type, code, dist ) ){
if( !strcmp( "ZPX", dist ) ){
if( nfound < 2 ) found_axes[ nfound ] = i;
nfound++;
ctype[ 8 ] = 0;
}
}
}
/* Issue a warning if more than two ZPX axes were found. */
if( nfound > 2 ) {
Warn( this, "distortion", "More than two axes were found "
"with the \"-ZPX\" projection code. A ZPN projection "
"will be used instead.", method, class, status );
/* Otherwise, create a Mapping describing the distortion (other axes are passed
unchanged by this Mapping), and add it in series with the returned map4
(ZPX distortion is applied to the translated, rotated, scaled IWC
coordinates). */
} else if( nfound == 2 ){
tmap1 = ZPXMapping( this, store, s, naxes, found_axes, method,
class, status );
if( ! *map4 ) {
*map4 = tmap1;
} else {
tmap2 = (AstMapping *) astCmpMap( *map4, tmap1, 1, "", status );
*map4 = astAnnul( *map4 );
tmap1 = astAnnul( tmap1 );
*map4 = tmap2;
}
}
/* (There are currently no other supported distortion codes.) */
/* Finally, check all axes looking for any remaining (and therefore
unsupported) distortion codes. Issue a warning about them and remove
them.
=================================================================== */
/* Indicate that we have not yet issued a warning. */
warned = 0;
/* Do each IWC axis. */
for( i = 0; i < naxes; i++ ){
/* Get the CTYPE value for this axis. */
ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
if( ctype ) {
/* See if has the "4-3-3" form described in FITS-WCS paper IV. */
if( 3 == astSscanf( ctype, "%4s-%3s-%3s", type, code, dist ) ){
/* Add an ASTWARN card to the FitsChan. Only issue one warning (this avoids
multiple warnings about the same distortion code in multiple CTYPE values). */
if( !warned ){
warned = 1;
sprintf( msgbuf, "The header contains CTYPE values (e.g. "
"%s = '%s') which "
"include a distortion code \"-%s\". AST "
"currently ignores this distortion. The code "
"has been removed from the CTYPE values.",
FormatKey( "CTYPE", i + 1, -1, ' ', status ), ctype, dist );
Warn( this, "distortion", msgbuf, method, class, status );
}
/* Terminate the CTYPE value in the FitsStore in order to exclude the distortion
code. This means that later functions will not need to take account of
distortion codes. */
ctype[ 8 ] = 0;
}
}
}
}
/* Free resources. */
dim = astFree( dim );
}
static void DSBSetUp( AstFitsChan *this, FitsStore *store,
AstDSBSpecFrame *dsb, char s, double crval,
const char *method, const char *class, int *status ){
/*
* Name:
* DSBSetUp
* Purpose:
* Modify an AstDSBSpecFrame object to reflect the contents of a FitsStore.
* Type:
* Private function.
* Synopsis:
* void DSBSetUp( AstFitsChan *this, FitsStore *store,
* AstDSBSpecFrame *dsb, char s, double crval,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* This function sets the attributes of the supplied DSBSpecFrame to
* reflect the values in the supplied FitsStore.
* Parameters:
* this
* The FitsChan.
* store
* A structure containing information about the requested axis
* descriptions derived from a FITS header.
* dsb
* Pointer to the DSBSpecFrame.
* s
* Alternate axis code.
* crval
* The spectral CRVAL value, in the spectral system represented by
* the supplied DSBSPecFrame.
* 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.
* Notes:
* - This implementation follows the conventions of the FITS-CLASS encoding.
*/
/* Local Variables: */
AstDSBSpecFrame *dsb_src; /* New DSBSpecFrame in which StdOfRest is source */
AstDSBSpecFrame *dsb_topo;/* New DSBSpecFrame in which StdOfRest is topo */
AstFrameSet *fs; /* FrameSet connecting two standards of rest */
double dsbcentre; /* Topocentric reference (CRVAL) frequency */
double in[2]; /* Source rest and image frequencies */
double lo; /* Topocentric Local Oscillator frequency */
double out[2]; /* Topocentric rest and image frequencies */
/* Check the global status. */
if ( !astOK ) return;
/* In order to determine the topocentric IF, we need the topocentric
frequencies corresponding to the RESTFREQ and IMAGFREQ values in the
FITS header. The values stored in the FITS header are measured in Hz,
in the source's rest frame, so we need a mapping from frequency in the
source rest frame to topocentric frequency. Take a copy of the supplied
DSBSpecFrame and then set its attributes to represent frequency in the
sources rest frame. */
dsb_src = astCopy( dsb );
astSetStdOfRest( dsb_src, AST__SCSOR );
astSetSystem( dsb_src, AST__FREQ );
astSetUnit( dsb_src, 0, "Hz" );
/* Take a copy of this DSBSpecFrame and set its standard of rest to
topocentric. */
dsb_topo = astCopy( dsb_src );
astSetStdOfRest( dsb_topo, AST__TPSOR );
/* Now get the Mapping between these. */
fs = astConvert( dsb_src, dsb_topo, "" );
dsb_src = astAnnul( dsb_src );
dsb_topo = astAnnul( dsb_topo );
/* Check a conversion was found. */
if( fs != NULL ) {
/* Use this Mapping to transform the rest frequency and the image
frequency from the standard of rest of the source to that of the
observer. */
in[ 0 ] = astGetRestFreq( dsb );
in[ 1 ] = GetItem( &(store->imagfreq), 0, 0, s, NULL, method, class, status );
astTran1( fs, 2, in, 1, out );
/* The intermediate frequency is half the distance between these two
frequencies. Note, the IF value is signed so as to put the rest
frequency in the observed sideband. */
if( out[ 0 ] != AST__BAD && out[ 1 ] != AST__BAD ) {
/* Store the spectral CRVAL value as the centre frequency of the
DSBSpecFrame. The public astSetD method interprets the supplied value
as a value in the spectral system described by the other SpecFrame
attributes. */
astSetD( dsb, "DSBCentre", crval );
/* To calculate the topocentric IF we need the topocentric frequency
equivalent of CRVAL. So take a copy of the DSBSpecFrame, then set it to
represent topocentric frequency, and read back the DSBCentre value. */
dsb_topo = astCopy( dsb );
astSetStdOfRest( dsb_topo, AST__TPSOR );
astSetSystem( dsb_topo, AST__FREQ );
astSetUnit( dsb_topo, 0, "Hz" );
dsbcentre = astGetD( dsb_topo, "DSBCentre" );
dsb_topo = astAnnul( dsb_topo );
/* We also need the topocentric Local Oscillator frequency. This is
assumed to be half way between the topocentric IMAGFREQ and RESTFREQ
values. */
lo = 0.5*( out[ 1 ] + out[ 0 ] );
/* Set the IF to be the difference between the Local Oscillator frequency
and the CRVAL frequency. */
astSetIF( dsb, lo - dsbcentre );
/* Set the DSBSpecFrame to represent the observed sideband */
astSetC( dsb, "SideBand", "observed" );
}
/* Free resources. */
fs = astAnnul( fs );
}
}
static int DSSFromStore( AstFitsChan *this, FitsStore *store,
const char *method, const char *class, int *status ){
/*
* Name:
* DSSFromStore
* Purpose:
* Store WCS keywords in a FitsChan using DSS encoding.
* Type:
* Private function.
* Synopsis:
* int DSSFromStore( AstFitsChan *this, FitsStore *store,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* A FitsStore is a structure containing a generalised represention of
* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
* from a set of FITS header cards (using a specified encoding), or
* an AST FrameSet. In other words, a FitsStore is an encoding-
* independant intermediary staging post between a FITS header and
* an AST FrameSet.
*
* This function copies the WCS information stored in the supplied
* FitsStore into the supplied FitsChan, using DSS encoding.
* Parameters:
* this
* Pointer to the FitsChan.
* store
* Pointer to the FitsStore.
* 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:
* A value of 1 is returned if succesfull, and zero is returned
* otherwise.
*/
/* Local Variables: */
const char *comm; /* Pointer to comment string */
char *cval; /* Pointer to string keyword value */
const char *pltdecsn;/* PLTDECSN keyword value */
double amdx[20]; /* AMDXi keyword value */
double amdy[20]; /* AMDYi keyword value */
double cdelt; /* CDELT element */
double cnpix1; /* CNPIX1 keyword value */
double cnpix2; /* CNPIX2 keyword value */
double pc; /* PC element */
double pltdecd; /* PLTDECD keyword value */
double pltdecm; /* PLTDECM keyword value */
double pltdecs; /* PLTDECS keyword value */
double pltrah; /* PLTRAH keyword value */
double pltram; /* PLTRAM keyword value */
double pltras; /* PLTRAS keyword value */
double pltscl; /* PLTSCL keyword value */
double ppo1; /* PPO1 keyword value */
double ppo2; /* PPO2 keyword value */
double ppo3; /* PPO3 keyword value */
double ppo4; /* PPO4 keyword value */
double ppo5; /* PPO5 keyword value */
double ppo6; /* PPO6 keyword value */
double pvx[22]; /* X projection parameter values */
double pvy[22]; /* Y projection parameter values */
double val; /* General purpose value */
double xpixelsz; /* XPIXELSZ keyword value */
double ypixelsz; /* YPIXELSZ keyword value */
int i; /* Loop count */
int gottpn; /* Is the projection a "TPN" projection? */
int m; /* Parameter index */
int ret; /* Returned value. */
/* Initialise */
ret = 0;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Check the image is 2 dimensional. */
if( GetMaxJM( &(store->crpix), ' ', status ) != 1 ) return ret;
/* Check the first axis is RA with a TAN or TPN projection. */
cval = GetItemC( &(store->ctype), 0, 0, ' ', NULL, method, class, status );
if( !cval ) return ret;
gottpn = !strcmp( "RA---TPN", cval );
if( strcmp( "RA---TAN", cval ) && !gottpn ) return ret;
/* Check the second axis is DEC with a TAN or TPN projection. */
cval = GetItemC( &(store->ctype), 1, 0, ' ', NULL, method, class, status );
if( !cval ) return ret;
if( gottpn ) {
if( strcmp( "DEC--TPN", cval ) ) return ret;
} else {
if( strcmp( "DEC--TAN", cval ) ) return ret;
}
/* Check that LONPOLE is undefined or is 180 degrees. */
val = GetItem( &(store->lonpole), 0, 0, ' ', NULL, method, class, status );
if( val != AST__BAD && val != 180.0 ) return ret;
/* Check that the RA/DEC system is FK5. */
cval = GetItemC( &(store->radesys), 0, 0, ' ', NULL, method, class, status );
if( !cval || strcmp( "FK5", cval ) ) return ret;
/* Check that equinox is not defined or is 2000.0 */
val = GetItem( &(store->equinox), 0, 0, ' ', NULL, method, class, status );
if( val != AST__BAD && val != 2000.0 ) return ret;
/* Get the pixel sizes from the PC/CDELT keywords. They must be defined and
not be zero. */
cdelt = GetItem( &(store->cdelt), 0, 0, ' ', NULL, method, class, status );
if( cdelt == AST__BAD ) return ret;
pc = GetItem( &(store->pc), 0, 0, ' ', NULL, method, class, status );
if( pc == AST__BAD ) pc = 1.0;
xpixelsz = cdelt*pc;
cdelt = GetItem( &(store->cdelt), 1, 0, ' ', NULL, method, class, status );
if( cdelt == AST__BAD ) return ret;
pc = GetItem( &(store->pc), 1, 1, ' ', NULL, method, class, status );
if( pc == AST__BAD ) pc = 1.0;
ypixelsz = cdelt*pc;
if( xpixelsz == 0.0 || ypixelsz == 0.0 ) return ret;
xpixelsz *= -1000.0;
ypixelsz *= 1000.0;
/* Check the off-diagonal PC terms are zero. DSS does not allow any rotation. */
val = GetItem( &(store->pc), 0, 1, ' ', NULL, method, class, status );
if( val != AST__BAD && val != 0.0 ) return ret;
val = GetItem( &(store->pc), 1, 0, ' ', NULL, method, class, status );
if( val != AST__BAD && val != 0.0 ) return ret;
/* Get the required projection parameter values from the store, supplying
appropriate values if a simple TAN projection is being used. */
for( m = 0; m < 22; m++ ){
pvx[ m ] = GetItem( &(store->pv), 0, m, ' ', NULL, method, class, status );
if( pvx[ m ] == AST__BAD || !gottpn ) pvx[ m ] = ( m == 1 ) ? 1.0 : 0.0;
pvy[ m ] = GetItem( &(store->pv), 1, m, ' ', NULL, method, class, status );
if( pvy[ m ] == AST__BAD || !gottpn ) pvy[ m ] = ( m == 1 ) ? 1.0 : 0.0;
}
/* Check that no other projection parameters have been set. */
if( GetMaxJM( &(store->pv), ' ', status ) > 21 ) return ret;
/* Check that specific parameters take their required zero value. */
if( pvx[ 3 ] != 0.0 || pvy[ 3 ] != 0.0 ) return ret;
for( m = 11; m < 17; m++ ){
if( pvx[ m ] != 0.0 || pvy[ m ] != 0.0 ) return ret;
}
if( pvx[ 18 ] != 0.0 || pvy[ 18 ] != 0.0 ) return ret;
if( pvx[ 20 ] != 0.0 || pvy[ 20 ] != 0.0 ) return ret;
/* Check that other projection parameters are related correctly. */
if( !EQUAL( 2*pvx[ 17 ], pvx[ 19 ] ) ) return ret;
if( !EQUAL( pvx[ 17 ], pvx[ 21 ] ) ) return ret;
if( !EQUAL( 2*pvy[ 17 ], pvy[ 19 ] ) ) return ret;
if( !EQUAL( pvy[ 17 ], pvy[ 21 ] ) ) return ret;
/* Initialise all polynomial co-efficients to zero. */
for( m = 0; m < 20; m++ ){
amdx[ m ] = 0.0;
amdy[ m ] = 0.0;
}
/* Polynomial co-efficients. There is redundancy here too, so we
arbitrarily choose to leave AMDX/Y7 and AMDX/Y12 set to zero. */
amdx[ 0 ] = 3600.0*pvx[ 1 ];
amdx[ 1 ] = 3600.0*pvx[ 2 ];
amdx[ 2 ] = 3600.0*pvx[ 0 ];
amdx[ 3 ] = 3600.0*pvx[ 4 ];
amdx[ 4 ] = 3600.0*pvx[ 5 ];
amdx[ 5 ] = 3600.0*pvx[ 6 ];
amdx[ 7 ] = 3600.0*pvx[ 7 ];
amdx[ 8 ] = 3600.0*pvx[ 8 ];
amdx[ 9 ] = 3600.0*pvx[ 9 ];
amdx[ 10 ] = 3600.0*pvx[ 10 ];
amdx[ 12 ] = 3600.0*pvx[ 17 ];
amdy[ 0 ] = 3600.0*pvy[ 1 ];
amdy[ 1 ] = 3600.0*pvy[ 2 ];
amdy[ 2 ] = 3600.0*pvy[ 0 ];
amdy[ 3 ] = 3600.0*pvy[ 4 ];
amdy[ 4 ] = 3600.0*pvy[ 5 ];
amdy[ 5 ] = 3600.0*pvy[ 6 ];
amdy[ 7 ] = 3600.0*pvy[ 7 ];
amdy[ 8 ] = 3600.0*pvy[ 8 ];
amdy[ 9 ] = 3600.0*pvy[ 9 ];
amdy[ 10 ] = 3600.0*pvy[ 10 ];
amdy[ 12 ] = 3600.0*pvy[ 17 ];
/* The plate scale is the mean of the first X and Y co-efficients. */
pltscl = 0.5*( amdx[ 0 ] + amdy[ 0 ] );
/* There is redundancy in the DSS encoding. We can choose an arbitrary
pixel corner (CNPIX1, CNPIX2) so long as we use the corresponding origin
for the cartesian co-ordinate system in which the plate centre is
specified (PPO3, PPO6). Arbitrarily set CNPIX1 and CNPIX2 to one. */
cnpix1 = 1.0;
cnpix2 = 1.0;
/* Find the corresponding plate centre PPO3 and PPO6 (other co-efficients
are set to zero). */
ppo1 = 0.0;
ppo2 = 0.0;
val = GetItem( &(store->crpix), 0, 0, ' ', NULL, method, class, status );
if( val == AST__BAD ) return ret;
ppo3 = xpixelsz*( val + cnpix1 - 0.5 );
ppo4 = 0.0;
ppo5 = 0.0;
val = GetItem( &(store->crpix), 0, 1, ' ', NULL, method, class, status );
if( val == AST__BAD ) return ret;
ppo6 = ypixelsz*( val + cnpix2 - 0.5 );
/* The reference RA. Get it in degrees. */
val = GetItem( &(store->crval), 0, 0, ' ', NULL, method, class, status );
if( val == AST__BAD ) return ret;
/* Convert to hours and ensure it is in the range 0 to 24 */
val /= 15.0;
while( val < 0 ) val += 24.0;
while( val >= 24.0 ) val -= 24.0;
/* Split into hours, mins and seconds. */
pltrah = (int) val;
val = 60.0*( val - pltrah );
pltram = (int) val;
pltras = 60.0*( val - pltram );
/* The reference DEC. Get it in degrees. */
val = GetItem( &(store->crval), 1, 0, ' ', NULL, method, class, status );
if( val == AST__BAD ) return ret;
/* Ensure it is in the range -180 to +180 */
while( val < -180.0 ) val += 360.0;
while( val >= 180.0 ) val -= 360.0;
/* Save the sign. */
if( val > 0.0 ){
pltdecsn = "+";
} else {
pltdecsn = "-";
val = -val;
}
/* Split into degrees, mins and seconds. */
pltdecd = (int) val;
val = 60.0*( val - pltdecd );
pltdecm = (int) val;
pltdecs = 60.0*( val - pltdecm );
/* Store the DSS keywords in the FitsChan. */
SetValue( this, "CNPIX1", &cnpix1, AST__FLOAT, "X corner (pixels)", status );
SetValue( this, "CNPIX2", &cnpix2, AST__FLOAT, "Y corner (pixels)", status );
SetValue( this, "PPO1", &ppo1, AST__FLOAT, "Orientation co-efficients", status );
SetValue( this, "PPO2", &ppo2, AST__FLOAT, "", status );
SetValue( this, "PPO3", &ppo3, AST__FLOAT, "", status );
SetValue( this, "PPO4", &ppo4, AST__FLOAT, "", status );
SetValue( this, "PPO5", &ppo5, AST__FLOAT, "", status );
SetValue( this, "PPO6", &ppo6, AST__FLOAT, "", status );
SetValue( this, "XPIXELSZ", &xpixelsz, AST__FLOAT, "X pixel size (microns)", status );
SetValue( this, "YPIXELSZ", &ypixelsz, AST__FLOAT, "Y pixel size (microns)", status );
SetValue( this, "PLTRAH", &pltrah, AST__FLOAT, "RA at plate centre", status );
SetValue( this, "PLTRAM", &pltram, AST__FLOAT, "", status );
SetValue( this, "PLTRAS", &pltras, AST__FLOAT, "", status );
SetValue( this, "PLTDECD", &pltdecd, AST__FLOAT, "DEC at plate centre", status );
SetValue( this, "PLTDECM", &pltdecm, AST__FLOAT, "", status );
SetValue( this, "PLTDECS", &pltdecs, AST__FLOAT, "", status );
SetValue( this, "PLTDECSN", &pltdecsn, AST__STRING, "", status );
SetValue( this, "PLTSCALE", &pltscl, AST__FLOAT, "Plate scale (arcsec/mm)", status );
comm = "Plate solution x co-efficients";
for( i = 0; i < 20; i++ ){
SetValue( this, FormatKey( "AMDX", i + 1, -1, ' ', status ), amdx + i,
AST__FLOAT, comm, status );
comm = NULL;
}
comm = "Plate solution y co-efficients";
for( i = 0; i < 20; i++ ){
SetValue( this, FormatKey( "AMDY", i + 1, -1, ' ', status ), amdy + i,
AST__FLOAT, comm, status );
comm = NULL;
}
/* If no error has occurred, return one. */
if( astOK ) ret = 1;
/* Return the answer. */
return ret;
}
static void DSSToStore( AstFitsChan *this, FitsStore *store,
const char *method, const char *class, int *status ){
/*
* Name:
* DSSToStore
* Purpose:
* Extract WCS information from the supplied FitsChan using a DSS
* encoding, and store it in the supplied FitsStore.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void DSSToStore( AstFitsChan *this, FitsStore *store,
const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* A FitsStore is a structure containing a generalised represention of
* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
* from a set of FITS header cards (using a specified encoding), or
* an AST FrameSet. In other words, a FitsStore is an encoding-
* independant intermediary staging post between a FITS header and
* an AST FrameSet.
*
* This function extracts DSS keywords from the supplied FitsChan, and
* stores the corresponding WCS information in the supplied FitsStore.
* The conversion from DSS encoding to standard WCS encoding is
* described in an ear;y draft of the Calabretta & Greisen paper
* "Representations of celestial coordinates in FITS" (A&A, in prep.),
* and uses the now deprecated "TAN with polynomial corrections",
* which is still supported by the WcsMap class as type AST__TPN.
* Here we use "lambda=1" (i.e. plate co-ordinate are measured in mm,
* not degrees).
*
* It is assumed that DSS images are 2 dimensional.
* Parameters:
* this
* Pointer to the FitsChan.
* store
* Pointer to the FitsStore structure.
* 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.
*/
/* Local Variables: */
char *text; /* Pointer to textual keyword value */
char pltdecsn[11]; /* First 10 non-blank characters from PLTDECSN keyword */
char keyname[10]; /* Buffer for keyword name */
double amdx[20]; /* AMDXi keyword value */
double amdy[20]; /* AMDYi keyword value */
double cnpix1; /* CNPIX1 keyword value */
double cnpix2; /* CNPIX2 keyword value */
double crval2; /* Equivalent CRVAL2 keyword value */
double dummy; /* Unused keyword value */
double pltdecd; /* PLTDECD keyword value */
double pltdecm; /* PLTDECM keyword value */
double pltdecs; /* PLTDECS keyword value */
double pltrah; /* PLTRAH keyword value */
double pltram; /* PLTRAM keyword value */
double pltras; /* PLTRAS keyword value */
double ppo3; /* PPO3 keyword value */
double ppo6; /* PPO6 keyword value */
double pv; /* Projection parameter value */
double xpixelsz; /* XPIXELSZ keyword value */
double ypixelsz; /* YPIXELSZ keyword value */
int i; /* Loop count */
/* Check the inherited status. */
if( !astOK ) return;
/* Get the optional DSS keywords, supplying defaults for any missing keywords. */
cnpix1 = 0.0;
cnpix2 = 0.0;
GetValue( this, "CNPIX1", AST__FLOAT, &cnpix1, 0, 1, method, class, status );
GetValue( this, "CNPIX2", AST__FLOAT, &cnpix2, 0, 1, method, class, status );
/* Get the required DSS keywords. Report an error if any are missing. */
GetValue( this, "PPO3", AST__FLOAT, &ppo3, 1, 1, method, class, status );
GetValue( this, "PPO6", AST__FLOAT, &ppo6, 1, 1, method, class, status );
GetValue( this, "XPIXELSZ", AST__FLOAT, &xpixelsz, 1, 1, method, class, status );
GetValue( this, "YPIXELSZ", AST__FLOAT, &ypixelsz, 1, 1, method, class, status );
GetValue( this, "PLTRAH", AST__FLOAT, &pltrah, 1, 1, method, class, status );
GetValue( this, "PLTRAM", AST__FLOAT, &pltram, 1, 1, method, class, status );
GetValue( this, "PLTRAS", AST__FLOAT, &pltras, 1, 1, method, class, status );
GetValue( this, "PLTDECD", AST__FLOAT, &pltdecd, 1, 1, method, class, status );
GetValue( this, "PLTDECM", AST__FLOAT, &pltdecm, 1, 1, method, class, status );
GetValue( this, "PLTDECS", AST__FLOAT, &pltdecs, 1, 1, method, class, status );
/* Copy the first 10 non-blank characters from the PLTDECSN keyword. */
GetValue( this, "PLTDECSN", AST__STRING, &text, 1, 1, method, class, status );
if( astOK ) {
text += strspn( text, " " );
text[ strcspn( text, " " ) ] = 0;
strncpy( pltdecsn, text, 10 );
}
/* Read other related keywords. We do not need these, but we read them
so that they are not propagated to any output FITS file. */
GetValue( this, "PLTSCALE", AST__FLOAT, &dummy, 0, 1, method, class, status );
GetValue( this, "PPO1", AST__FLOAT, &dummy, 0, 1, method, class, status );
GetValue( this, "PPO2", AST__FLOAT, &dummy, 0, 1, method, class, status );
GetValue( this, "PPO4", AST__FLOAT, &dummy, 0, 1, method, class, status );
GetValue( this, "PPO5", AST__FLOAT, &dummy, 0, 1, method, class, status );
/* Get the polynomial co-efficients. These can be defaulted if they are
missing, so do not report an error. */
for( i = 0; i < 20; i++ ){
(void) sprintf( keyname, "AMDX%d", i + 1 );
amdx[i] = AST__BAD;
GetValue( this, keyname, AST__FLOAT, amdx + i, 0, 1, method, class, status );
(void) sprintf( keyname, "AMDY%d", i + 1 );
amdy[i] = AST__BAD;
GetValue( this, keyname, AST__FLOAT, amdy + i, 0, 1, method, class, status );
}
/* Check the above went OK. */
if( astOK ) {
/* Calculate and store the equivalent PV projection parameters. */
if( amdx[2] != AST__BAD ) {
pv = amdx[2]/3600.0;
SetItem( &(store->pv), 0, 0, ' ', pv, status );
}
if( amdx[0] != AST__BAD ) {
pv = amdx[0]/3600.0;
SetItem( &(store->pv), 0, 1, ' ', pv, status );
}
if( amdx[1] != AST__BAD ) {
pv = amdx[1]/3600.0;
SetItem( &(store->pv), 0, 2, ' ', pv, status );
}
if( amdx[3] != AST__BAD && amdx[6] != AST__BAD ) {
pv = ( amdx[3] + amdx[6] )/3600.0;
SetItem( &(store->pv), 0, 4, ' ', pv, status );
}
if( amdx[4] != AST__BAD ) {
pv = amdx[4]/3600.0;
SetItem( &(store->pv), 0, 5, ' ', pv, status );
}
if( amdx[5] != AST__BAD && amdx[6] != AST__BAD ) {
pv = ( amdx[5] + amdx[6] )/3600.0;
SetItem( &(store->pv), 0, 6, ' ', pv, status );
}
if( amdx[7] != AST__BAD && amdx[11] != AST__BAD ) {
pv = ( amdx[7] + amdx[11] )/3600.0;
SetItem( &(store->pv), 0, 7, ' ', pv, status );
}
if( amdx[8] != AST__BAD ) {
pv = amdx[8]/3600.0;
SetItem( &(store->pv), 0, 8, ' ', pv, status );
}
if( amdx[9] != AST__BAD && amdx[11] != AST__BAD ) {
pv = ( amdx[9] + amdx[11] )/3600.0;
SetItem( &(store->pv), 0, 9, ' ', pv, status );
}
if( amdx[10] != AST__BAD ) {
pv = amdx[10]/3600.0;
SetItem( &(store->pv), 0, 10, ' ', pv, status );
}
if( amdx[12] != AST__BAD ) {
pv = amdx[12]/3600.0;
SetItem( &(store->pv), 0, 17, ' ', pv, status );
SetItem( &(store->pv), 0, 19, ' ', 2*pv, status );
SetItem( &(store->pv), 0, 21, ' ', pv, status );
}
if( amdy[2] != AST__BAD ) {
pv = amdy[2]/3600.0;
SetItem( &(store->pv), 1, 0, ' ', pv, status );
}
if( amdy[0] != AST__BAD ) {
pv = amdy[0]/3600.0;
SetItem( &(store->pv), 1, 1, ' ', pv, status );
}
if( amdy[1] != AST__BAD ) {
pv = amdy[1]/3600.0;
SetItem( &(store->pv), 1, 2, ' ', pv, status );
}
if( amdy[3] != AST__BAD && amdy[6] != AST__BAD ) {
pv = ( amdy[3] + amdy[6] )/3600.0;
SetItem( &(store->pv), 1, 4, ' ', pv, status );
}
if( amdy[4] != AST__BAD ) {
pv = amdy[4]/3600.0;
SetItem( &(store->pv), 1, 5, ' ', pv, status );
}
if( amdy[5] != AST__BAD && amdy[6] != AST__BAD ) {
pv = ( amdy[5] + amdy[6] )/3600.0;
SetItem( &(store->pv), 1, 6, ' ', pv, status );
}
if( amdy[7] != AST__BAD && amdy[11] != AST__BAD ) {
pv = ( amdy[7] + amdy[11] )/3600.0;
SetItem( &(store->pv), 1, 7, ' ', pv, status );
}
if( amdy[8] != AST__BAD ) {
pv = amdy[8]/3600.0;
SetItem( &(store->pv), 1, 8, ' ', pv, status );
}
if( amdy[9] != AST__BAD && amdy[11] != AST__BAD ) {
pv = ( amdy[9] + amdy[11] )/3600.0;
SetItem( &(store->pv), 1, 9, ' ', pv, status );
}
if( amdy[10] != AST__BAD ) {
pv = amdy[10]/3600.0;
SetItem( &(store->pv), 1, 10, ' ', pv, status );
}
if( amdy[12] != AST__BAD ) {
pv = amdy[12]/3600.0;
SetItem( &(store->pv), 1, 17, ' ', pv, status );
SetItem( &(store->pv), 1, 19, ' ', 2*pv, status );
SetItem( &(store->pv), 1, 21, ' ', pv, status );
}
/* Calculate and store the equivalent CRPIX values. */
if( xpixelsz != 0.0 ) {
SetItem( &(store->crpix), 0, 0, ' ',
( ppo3/xpixelsz ) - cnpix1 + 0.5, status );
} else if( astOK ){
astError( AST__BDFTS, "%s(%s): FITS keyword XPIXELSZ has illegal "
"value 0.0", status, method, class );
}
if( ypixelsz != 0.0 ) {
SetItem( &(store->crpix), 0, 1, ' ',
( ppo6/ypixelsz ) - cnpix2 + 0.5, status );
} else if( astOK ){
astError( AST__BDFTS, "%s(%s): FITS keyword YPIXELSZ has illegal "
"value 0.0", status, method, class );
}
/* Calculate and store the equivalent CRVAL values. */
SetItem( &(store->crval), 0, 0, ' ',
15.0*( pltrah + pltram/60.0 + pltras/3600.0 ), status );
crval2 = pltdecd + pltdecm/60.0 + pltdecs/3600.0;
if( !strcmp( pltdecsn, "-") ) crval2 = -crval2;
SetItem( &(store->crval), 1, 0, ' ', crval2, status );
/* Calculate and store the equivalent PC matrix. */
SetItem( &(store->pc), 0, 0, ' ', -0.001*xpixelsz, status );
SetItem( &(store->pc), 1, 1, ' ', 0.001*ypixelsz, status );
/* Set values of 1.0 for the CDELT values. */
SetItem( &(store->cdelt), 0, 0, ' ', 1.0, status );
SetItem( &(store->cdelt), 1, 0, ' ', 1.0, status );
/* Store remaining constant items */
SetItem( &(store->lonpole), 0, 0, ' ', 180.0, status );
SetItem( &(store->equinox), 0, 0, ' ', 2000.0, status );
SetItemC( &(store->radesys), 0, 0, ' ', "FK5", status );
SetItem( &(store->wcsaxes), 0, 0, ' ', 2.0, status );
store->naxis = 2;
SetItemC( &(store->ctype), 0, 0, ' ', "RA---TPN", status );
SetItemC( &(store->ctype), 1, 0, ' ', "DEC--TPN", status );
}
}
static void EmptyFits( AstFitsChan *this, int *status ){
/*
*++
* Name:
c astEmptyFits
f AST_EMPTYFITS
* Purpose:
* Delete all cards in a FitsChan.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c void astEmptyFits( AstFitsChan *this )
f CALL AST_EMPTYFITS( THIS, STATUS )
* Class Membership:
* FitsChan method.
* Description:
c This function
f This routine
* deletes all cards and associated information from a FitsChan.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Notes:
* - This method simply deletes the cards currently in the FitsChan.
c Unlike astWriteFits,
f Unlike AST_WRITEFITS,
* they are not first written out to the sink function or sink file.
* - Any Tables or warnings stored in the FitsChan are also deleted.
* - This method attempt to execute even if an error has occurred
* previously.
*--
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
const char *class; /* Pointer to string holding object class */
const char *method; /* Pointer to string holding calling method */
int old_ignore_used; /* Original setting of ignore_used variable */
/* Check a FitsChan was supplied. */
if( !this ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this);
/* Store the method and class strings. */
method = "astEmpty";
class = astGetClass( this );
/* Delete all cards from the circular linked list stored in the FitsChan,
starting with the card at the head of the list. */
old_ignore_used = ignore_used;
ignore_used = 0;
astClearCard( this );
while( !astFitsEof( this ) ) DeleteCard( this, method, class, status );
ignore_used = old_ignore_used;
/* Delete the KeyMap which holds keywords and the latest sequence number
used by each of them. */
if( this->keyseq ) this->keyseq = astAnnul( this->keyseq );
/* Delete the KeyMap holding the keyword names. */
if( this->keywords ) this->keywords = astAnnul( this->keywords );
/* Free any memory used to hold the Warnings attribute value. */
this->warnings = astFree( this->warnings );
/* Other objects in the FitsChan structure. */
if( this->tables ) this->tables = astAnnul( this->tables );
}
static int EncodeFloat( char *buf, int digits, int width, int maxwidth,
double value, int *status ){
/*
*
* Name:
* EncodeFloat
* Purpose:
* Formats a floating point value.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int EncodeFloat( char *buf, int digits, int width, int maxwidth,
* double value, int *status )
* Class Membership:
* FitsChan method.
* Description:
* This function formats the value using a G format specified in order
* to use the minimum field width (trailing zeros are not printed).
* However, the G specifier does not include a decimal point unless it
* is necessary. FITS requires that floating point values always include
* a decimal point, so this function inserts one, if necessary.
* Parameters:
* buf
* A character string into which the value is written.
* digits
* The number of digits after the decimal point. If the supplied value
* is negative, the number of digits actually used may be reduced if
* the string would otherwise extend beyond the number of columns
* allowed by the FITS standard. If the value is positive, the
* specified number of digits are always produced, even if it means
* breaking the FITS standard.
* width
* The minimum field width to use. The value is right justified in
* this field width.
* maxwidth
* The maximum field width to use. A value of zero is returned if
* the maximum field width is exceeded.
* value
* The value to format.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The field width actually used, or zero if the value could not be
* formatted. This does not include the trailing null character.
* Notes:
* - If there is room, a trailing zero is also added following the
* inserted decimal point.
*/
/* Local Variables: */
char *c;
char *w, *r;
int i;
int ldigits;
int n;
int ret;
/* Check the global error status. */
if ( !astOK ) return 0;
/* The supplied value of "digits" may be negative. Obtain the positive
value giving the initial number of decimal digits to use. */
ldigits = ( digits > 0 ) ? digits : -digits;
/* Loop until a suitably encoded value has been obtained. */
while( 1 ){
/* Write the value into the buffer. Most are formatted with a G specifier.
This will result in values between -0.001 and -0.0001 being formatted
without an exponent, and thus occupying (ldigits+6) characters. With
an exponent, these values would be formatted in (ldigits+5) characters
thus saving one character. This is important because the default value
of ldigits is 15, resulting in 21 characters being used by the G
specifier. This is one more than the maximum allowed by the FITS
standard. Using an exponent instead would result in 20 characters
being used without any loss of precision, thus staying within the FITS
limit. Note, the precision used with the E specifier is one less than
with the G specifier because the digit to the left of the decimal place
is significant with the E specifier, and so we only need (ldigits-1)
significant digits to the right of the decimal point. */
if( value > -0.001 && value < -0.0001 ) {
(void) sprintf( buf, "%*.*E", width, ldigits - 1, value );
} else {
(void) sprintf( buf, "%*.*G", width, ldigits, value );
}
/* Check that the value zero is not encoded with a minus sign (e.g. "-0.").
This also rounds out long sequences of zeros or nines. */
CheckZero( buf, value, width, status );
/* If the formatted value includes an exponent, it will have 2 digits.
If the exponent includes a leading zero, remove it. */
if( ( w = strstr( buf, "E-0" ) ) ) {
w += 2;
} else if( ( w = strstr( buf, "E+0" ) ) ){
w += 2;
} else if( ( w = strstr( buf, "E0" ) ) ){
w += 1;
}
/* If a leading zero was found, shuffle everything down from the start of
the string by one character, over-writing the redundant zero, and insert
a space at the start of the string. */
if( w ) {
r = w - 1 ;
while( w != buf ) *(w--) = *(r--);
*w = ' ';
}
/* If the used field width was too large, reduce it and try again, so
long as we are allowed to change the number of digits being used. */
ret = strlen( buf );
if( ret > width && digits < 0 ){
ldigits -= ( ret - width );
/* Otherwise leave the loop. Return zero field width if the maximum field
width was exceeded. */
} else {
if( ret > maxwidth ) ret = 0;
break;
}
}
/* If a formatted value was obtained, we need to ensure that the it includes
a decimal point. */
if( ret ){
/* Get a pointer to the first digit in the buffer. */
c = strpbrk( buf, "0123456789" );
/* Something funny is going on if there are no digits in the buffer,
so return a zero field width. */
if( !c ){
ret = 0;
/* Otherwise... */
} else {
/* Find the number of digits following and including the first digit. */
n = strspn( c, "0123456789" );
/* If the first non-digit character is a decimal point, do nothing. */
if( c[ n ] != '.' ){
/* If there are two or more leading spaces, move the start of the string
two character to the left, and insert ".0" in the gap created. This
keeps the field right justified within the desired field width. */
if( buf[ 0 ] == ' ' && buf[ 1 ] == ' ' ){
for( i = 2; i < c - buf + n; i++ ) buf[ i - 2 ] = buf[ i ];
c[ n - 2 ] = '.';
c[ n - 1 ] = '0';
/* If there is just one leading space, move the start of the string
one character to the left, and insert "." in the gap created. This
keeps the field right justified within the desired field width. */
} else if( buf[ 0 ] == ' ' ){
for( i = 0; i < n; i++ ) c[ i - 1 ] = c[ i ];
c[ n - 1 ] = '.';
/* If there are no leading spaces we need to move the end of the string
to the right. This will result in the string no longer being right
justified in the required field width. Return zero if there is
insufficient room for an extra character. */
} else {
ret++;
if( ret > maxwidth ){
ret = 0;
/* Otherwise, more the end of the string one place to the right and insert
the decimal point. */
} else {
for( i = strlen( c ); i >= n; i-- ) c[ i + 1 ] = c[ i ];
c[ n ] = '.';
}
}
}
}
}
/* Return the field width. */
return ret;
}
static int EncodeValue( AstFitsChan *this, char *buf, int col, int digits,
const char *method, int *status ){
/*
* Name:
* EncodeValue
* Purpose:
* Encode the current card's keyword value into a string.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int EncodeValue( AstFitsChan *this, char *buf, int col, int digits,
* const char *method, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function encodes the keyword value defined in the current card
* of the supplied FitsChan and stores it at the start of the supplied
* buffer. The number of characters placed in the buffer is returned
* (not including a terminating null).
* Parameters:
* this
* Pointer to the FitsChan.
* buf
* The buffer to receive the formatted value. This should be at least
* 70 characters long.
* col
* The column number within the FITS header card corresponding to the
* start of "buf".
* digits
* The number of digits to use when formatting floating point
* values. If the supplied value is negative, the number of digits
* actually used may be reduced if the string would otherwise extend
* beyond the number of columns allowed by the FITS standard. If the
* value is positive, the specified number of digits are always
* produced, even if it means breaking the FITS standard.
* method
* Pointer to a string holding the name of the calling method.
* This is only for use in constructing error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The number of columns used by the encoded value.
* Notes:
* - The function returns 0 if an error has already occurred
* or if an error occurs for any reason within this function.
*/
/* Local Variables: */
char *c; /* Pointer to next character */
char *name; /* Pointer to the keyword name */
double dval; /* Keyword value */
void *data; /* Pointer to keyword value */
int i; /* Loop count */
int ilen; /* Length of imaginary part */
int len; /* Returned length */
int quote; /* Quote character found? */
int rlen; /* Length of real part */
int type; /* Data type for keyword in current card */
/* Check the global status. */
if( !astOK ) return 0;
/* Initialise returned length. */
len = 0;
/* Get the data type of the keyword. */
type = CardType( this, status );
/* Get a pointer to the data value in the current card. */
data = CardData( this, NULL, status );
/* Return if there is no defined value associated with the keyword in the
current card. */
if( type != AST__UNDEF ) {
/* Get the name of the keyword. */
name = CardName( this, status );
/* Go through each supported data type (roughly in the order of
decreasing usage)... */
/* AST__FLOAT - stored internally in a variable of type "double". Right
justified to column 30 in the header card. */
if( type == AST__FLOAT ){
dval = *( (double *) data );
len = EncodeFloat( buf, digits, FITSRLCOL - FITSNAMLEN - 2,
AST__FITSCHAN_FITSCARDLEN - col + 1, dval, status );
if( len <= 0 && astOK ) {
astError( AST__BDFTS, "%s(%s): Cannot encode floating point value "
"%g into a FITS header card for keyword '%s'.", status, method,
astGetClass( this ), dval, name );
}
/* AST__STRING & AST__CONTINUE - stored internally in a null terminated array of
type "char". The encoded string is enclosed in single quotes, starting
at FITS column 11 and ending in at least column 20. Single quotes
in the string are replaced by two adjacent single quotes. */
} else if( type == AST__STRING || type == AST__CONTINUE ){
c = (char *) data;
/* Enter the opening quote. */
len = 0;
buf[ len++ ] = '\'';
/* Inspect each character, looking for quotes. */
for ( i = 0; c[ i ]; ) {
quote = ( c[ i ] == '\'' );
/* If it will not fit into the header card (allowing for doubled
quotes), give up here. */
if ( len + ( quote ? 2 : 1 ) > AST__FITSCHAN_FITSCARDLEN - col ) break;
/* Otherwise, copy it into the output buffer and double any quotes. */
buf[ len++ ] = c[ i ];
if ( quote ) buf[ len++ ] = '\'';
/* Look at the next character. */
i++;
}
/* Pad the string out to the required minimum length with blanks and
add the final quote. */
while( len < FITSSTCOL - col ) buf[ len++ ] = ' ';
buf[ len++ ] = '\'';
/* Inspect any characters that weren't used. If any are non-blank,
report an error. */
for ( ; c[ i ]; i++ ) {
if ( !isspace( c[ i ] ) ) {
astError( AST__BDFTS,
"%s(%s): Cannot encode string '%s' into a FITS "
"header card for keyword '%s'.", status, method, astGetClass( this ),
(char *) data, name );
break;
}
}
/* INTEGER - stored internally in a variable of type "int". Right justified
to column 30 in the header card. */
} else if( type == AST__INT ){
len = sprintf( buf, "%*d", FITSRLCOL - col + 1,
*( (int *) data ) );
if( len < 0 || len > AST__FITSCHAN_FITSCARDLEN - col ) {
astError( AST__BDFTS, "%s(%s): Cannot encode integer value %d into a "
"FITS header card for keyword '%s'.", status, method, astGetClass( this ),
*( (int *) data ), name );
}
/* LOGICAL - stored internally in a variable of type "int". Represented by
a "T" or "F" in column 30 of the FITS header card. */
} else if( type == AST__LOGICAL ){
for( i = 0; i < FITSRLCOL - col; i++ ) buf[ i ] = ' ';
if( *( (int *) data ) ){
buf[ FITSRLCOL - col ] = 'T';
} else {
buf[ FITSRLCOL - col ] = 'F';
}
len = FITSRLCOL - col + 1;
/* AST__COMPLEXF - stored internally in an array of two "doubles". The real
part is right justified to FITS column 30. The imaginary part is right
justified to FITS column 50. */
} else if( type == AST__COMPLEXF ){
dval = ( (double *) data )[ 0 ];
rlen = EncodeFloat( buf, digits, FITSRLCOL - FITSNAMLEN - 2,
AST__FITSCHAN_FITSCARDLEN - col + 1, dval, status );
if( rlen <= 0 || rlen > AST__FITSCHAN_FITSCARDLEN - col ) {
astError( AST__BDFTS, "%s(%s): Cannot encode real part of a complex "
"floating point value [%g,%g] into a FITS header card "
"for keyword '%s'.", status, method, astGetClass( this ), dval,
( (double *) data )[ 1 ], name );
} else {
dval = ( (double *) data )[ 1 ];
ilen = EncodeFloat( buf + rlen, digits,
FITSIMCOL - FITSRLCOL,
AST__FITSCHAN_FITSCARDLEN - col - rlen, dval, status );
if( ilen <= 0 ) {
astError( AST__BDFTS, "%s(%s): Cannot encode imaginary part of a "
"complex floating point value [%g,%g] into a FITS header "
"card for keyword '%s'.", status, method, astGetClass( this ),
( (double *) data )[ 0 ], dval, name );
} else {
len = ilen + rlen;
}
}
/* AST__COMPLEXI - stored internally in a an array of two "ints". */
} else if( type == AST__COMPLEXI ){
rlen = sprintf( buf, "%*d", FITSRLCOL - col + 1,
( (int *) data )[ 0 ] );
if( rlen < 0 || rlen > AST__FITSCHAN_FITSCARDLEN - col ) {
astError( AST__BDFTS, "%s(%s): Cannot encode real part of a complex "
"integer value [%d,%d] into a FITS header card "
"for keyword '%s'.", status, method, astGetClass( this ),
( (int *) data )[ 0 ],
( (int *) data )[ 1 ], name );
} else {
ilen = sprintf( buf + rlen, "%*d", FITSIMCOL - FITSRLCOL + 1,
( (int *) data )[ 1 ] );
if( ilen < 0 || ilen > AST__FITSCHAN_FITSCARDLEN - col - rlen ) {
astError( AST__BDFTS, "%s(%s): Cannot encode imaginary part of a "
"complex integer value [%d,%d] into a FITS header card "
"for keyword '%s'.", status, method, astGetClass( this ),
( (int *) data )[ 0 ],
( (int *) data )[ 1 ], name );
} else {
len = ilen + rlen;
}
}
/* Report an internal (ast) programming error if the keyword is of none of the
above types. */
} else if( astOK ){
astError( AST__INTER, "EncodeValue: AST internal programming error - "
"FITS %s data-type not yet supported.", status,
type_names[ type ] );
}
}
/* If an error has occurred, return zero length. */
if( !astOK ) len = 0;
/* Return the answer. */
return len;
}
static AstGrismMap *ExtractGrismMap( AstMapping *map, int iax,
AstMapping **new_map, int *status ){
/*
* Name:
* ExtractGrismMap
* Purpose:
* Extract a GrismMap from the end of the supplied Mapping.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstGrismMap *ExtractGrismMap( AstMapping *map, int iax,
* AstMapping **new_map, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function examines the supplied Mapping; if the specified output
* coordinate of the Mapping is created directly by an un-inverted GrismMap,
* then a pointer to the GrismMap is returned as the function value. A new
* Mapping is also returned via parameter "new_map" which is a copy of
* the supplied Mapping, except that the GrismMap is replaced with a
* UnitMap. If no GrismMap is found, NULL is returned for both Mappings.
* The condition that "the specified output coordinate of the Mapping is
* created directly by an un-inverted GrismMap" means that the output
* of the GrismMap is no subsequently modified by any further Mappings
* before being returned as the "iax"th output of the supplied Mapping.
* This means the GrismMap must be "at the end of" a CmpMap, not in
* the middle of the CmpMap.
* Parameters:
* map
* Pointer to the Mapping to check.
* iax
* The index for the output coordinate to be checked.
* new_map
* Pointer to a location at which to return a pointer to a new
* Mapping which is a copy of "map" except that the GrismMap is
* replaced by a UnitMap. NULL is returned if the specified output
* was not created by a GrismMap.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The extracted GrismMap, or NULL if the specified output was not
* created by a GrismMap.
*/
/* Local Variables: */
AstMapping *mapa; /* First component Mapping */
AstMapping *mapb; /* Second component Mapping */
AstMapping *new_mapa; /* Replacement for first component Mapping */
AstMapping *new_mapb; /* Replacement for second component Mapping */
AstGrismMap *ret; /* Returned GrismMap */
int inva; /* Invert attribute for mapa within the CmpMap */
int invb; /* Invert attribute for mapb within the CmpMap */
int na; /* Number of outputs for mapa */
int old_inva; /* Current Invert attribute for mapa */
int old_invb; /* Current Invert attribute for mapb */
int series; /* Are component Mappings applied in series? */
/* Initialise */
ret = NULL;
*new_map = NULL;
/* Check the inherited status. */
if( !astOK ) return ret;
/* If the supplied Mapping is a GrismMap which has not been inverted,
return it as the function value and return a UnitMap as the new
Mapping. */
if( astIsAGrismMap( map ) ) {
if( !astGetInvert( map ) ) {
ret = astClone( map );
*new_map = (AstMapping *) astUnitMap( 1, "", status );
}
/* If the supplied Mapping is a CmpMap, get its two component Mappings,
see if they are applied in parallel or series, and get the Invert
attribute values which the component Mappings had at the time the
CmpMap was created. */
} else if( astIsACmpMap( map ) ) {
astDecompose( map, &mapa, &mapb, &series, &inva, &invb );
/* Temporaily reset the Invert attributes of the component Mappings back to
the values they had when the CmpMap was created. */
old_inva = astGetInvert( mapa );
old_invb = astGetInvert( mapb );
astSetInvert( mapa, inva );
astSetInvert( mapb, invb );
/* If the supplied Mapping is a series CmpMap, attempt to extract a
GrismMap from the second component Mapping ("mapb"). The first
component Mapping ("mapa") is unchanged. We do not need to consdier
the first component since we are only interested in GrismMaps which are
at the end of the CmpMap. */
if( series ) {
ret = ExtractGrismMap( mapb, iax, &new_mapb, status );
if( ret ) new_mapa = astClone( mapa );
/* If the supplied Mapping is a parallel CmpMap, attempt to extract a
GrismMap from the component Mapping which produces output "iax". The
other component Mapping is unchanged. */
} else {
na = astGetNout( mapa );
if( iax < na ) {
ret = ExtractGrismMap( mapa, iax, &new_mapa, status );
if( ret ) new_mapb = astClone( mapb );
} else {
ret = ExtractGrismMap( mapb, iax - na, &new_mapb, status );
if( ret ) new_mapa = astClone( mapa );
}
}
/* If succesful, create a new CmpMap to return. */
if( ret ) {
*new_map = (AstMapping *) astCmpMap( new_mapa, new_mapb, series, "", status );
new_mapa = astAnnul( new_mapa );
new_mapb = astAnnul( new_mapb );
}
/* Re-instate the original Invert attributes of the component Mappings. */
astSetInvert( mapa, old_inva );
astSetInvert( mapb, old_invb );
/* Annul the component Mapping pointers. */
mapa = astAnnul( mapa );
mapb = astAnnul( mapb );
}
/* Return the result. */
return ret;
}
static int MakeBasisVectors( AstMapping *map, int nin, int nout,
double *g0, AstPointSet *psetg,
AstPointSet *psetw, int *status ){
/*
* Name:
* MakeBasisVectors
* Purpose:
* Create a set of basis vectors in grid coordinates
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int MakeBasisVectors( AstMapping *map, int nin, int nout,
* double *g0, AstPointSet *psetg,
* AstPointSet *psetw, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function returns a set of unit vectors in grid coordinates,
* one for each grid axis. Each unit vector is parallel to the
* corresponding grid axis, and rooted at a specified grid position
* ("g0"). The IWC coordinates corresponding to "g0" and to the end of
* each of the unit vectors are also returned, together with a flag
* indicating if all the IWC coordinate values are good.
* Parameters:
* map
* A pointer to a Mapping which transforms grid coordinates into
* intermediate world coordinates (IWC). The number of outputs must
* be greater than or equal to the number of inputs.
* nin
* The number of inputs for "map" (i.e. the number of grid axes).
* nout
* The number of outputs for "map" (i.e. the number of IWC axes).
* g0
* Pointer to an array of holding the grid coordinates at the
* "root" position.
* psetg
* A pointer to a PointSet which can be used to hold the required
* grid positions. This should have room for nin+1 positions. On
* return, the first position holds "g0", and the subsequent "nin"
* positions hold are offset from "g0" by unit vectors along the
* corresponding grid axis.
* psetw
* A pointer to a PointSet which can be used to hold the required
* IWC position. This should also have room for nin+1 positions. On
* return, the values are the IWC coordinates corresponding to the
* grid positions returned in "psetg".
* status
* Pointer to the inherited status variable.
* Returned Value:
* A value of 1 is returned if all the axis values in "psetw" are good.
* Zero is returned otherwise.
* Notes:
* - Zero is returned if an error occurs.
*/
/* Local Variables: */
double **ptrg;
double **ptrw;
double *c;
int i;
int ii;
int j;
int ret;
/* Initialise */
ret = 0;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Get pointers to the data in the two supplied PointSets. */
ptrg = astGetPoints( psetg );
ptrw = astGetPoints( psetw );
/* Check the pointers can be used safely. */
if( astOK ) {
/* Assume success. */
ret = 1;
/* Store the required grid positions in PointSet "pset1". The first
position is the supplied root grid position, g0. The next "nin"
positions are offset from the root position by a unit vector along
each grid axis in turn. Store values for each grid axis in turn. */
for( i = 0; i < nin; i++ ) {
/* Get a pointer to the first axis value for this grid axis. */
c = ptrg[ i ];
/* Initially set all values for this axis to the supplied root grid value. */
for( ii = 0; ii < nin + 1; ii++ ) c[ ii ] = g0[ i ];
/* Modify the value corresponding to the vector along this grid axis. */
c[ i + 1 ] += 1.0;
}
/* Transform these grid positions in IWC positions using the supplied
Mapping. */
(void) astTransform( map, psetg, 1, psetw );
/* Check that all the transformed positions are good. */
for( j = 0; j < nout; j++ ) {
c = ptrw[ j ];
for( ii = 0; ii < nin + 1; ii++, c++ ) {
if( *c == AST__BAD ) {
ret = 0;
break;
}
}
}
}
/* Return the result. */
return ret;
}
static int FindBasisVectors( AstMapping *map, int nin, int nout,
double *dim, AstPointSet *psetg,
AstPointSet *psetw, int *status ){
/*
* Name:
* FindBasisVectors
* Purpose:
* Find the a set of basis vectors in grid coordinates
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int FindBasisVectors( AstMapping *map, int nin, int nout,
* double *dim, AstPointSet *psetg,
* AstPointSet *psetw, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function returns a set of unit vectors in grid coordinates,
* one for each grid axis. Each unit vector is parallel to the
* corresponding grid axis, and rooted at a specified grid position
* ("g0"). The IWC coordinates corresponding to "g0" and to the end of
* each of the unit vectors are also returned, together with a flag
* indicating if all the IWC coordinate values are good.
* Parameters:
* map
* A pointer to a Mapping which transforms grid coordinates into
* intermediate world coordinates (IWC). The number of outputs must
* be greater than or equal to the number of inputs.
* nin
* The number of inputs for "map" (i.e. the number of grid axes).
* nout
* The number of outputs for "map" (i.e. the number of IWC axes).
* dim
* Array dimensions, in pixels, if known (otherwise supplied a NULL
* pointer to values of AST__BAD).
* psetg
* A pointer to a PointSet which can be used to hold the required
* grid position. This should have room for nin+1 positions. On
* return, the first position holds the "root" position and the
* subsequent "nin" positions hold are offset from root position
* by unit vectors along the corresponding grid axis.
* psetw
* A pointer to a PointSet which can be used to hold the required
* IWC position. This should also have room for nin+1 positions. On
* return, the values are the IWC coordinates corresponding to the
* grid positions returned in "psetg".
* status
* Pointer to the inherited status variable.
* Returned Value:
* A value of 1 is returned if a set of basis vectors was found
* succesfully. Zero is returned otherwise.
* Notes:
* - Zero is returned if an error occurs.
*/
/* Local Variables: */
double *g0;
double dd;
double ddlim;
int i;
int ii;
int ret;
/* Initialise */
ret = 0;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Allocate an array to store the candidate root position. */
g0 = astMalloc( sizeof( double )*(size_t) nin );
if( astOK ) {
/* First try the grid centre, if known. */
ddlim = 0;
ret = 0;
if( dim ) {
ret = 1;
for( i = 0; i < nin; i++ ) {
if( dim[ i ] != AST__BAD ) {
g0[ i ] = 0.5*( 1 + dim[ i ] );
if( dim[ i ] > ddlim ) ddlim = dim[ i ];
} else {
ret = 0;
break;
}
}
}
if( ret ) ret = MakeBasisVectors( map, nin, nout, g0, psetg, psetw, status );
/* If this did not produce a set of good IWC positions, try grid position
(1,1,1...). */
if( !ret ) {
for( i = 0; i < nin; i++ ) g0[ i ] = 1.0;
ret = MakeBasisVectors( map, nin, nout, g0, psetg, psetw, status );
}
/* If this did not produce a set of good IWC positions, try a sequence of
grid positions which move an increasing distance along each grid axis
from (1,1,1,...). Stop when we get further than "ddlim" from the
origin. */
dd = 10.0;
if( ddlim == 0.0 ) ddlim = 10240.0;
while( !ret && dd <= ddlim ) {
/* First try positions which extend across the middle of the data set.
If the image dimensions are known, make the line go from the "bottom
left corner" towards the "top right corner", taking the aspect ratio
of the image into account. Otherise, just use a vector of (1,1,1,..) */
for( i = 0; i < nin; i++ ) {
if( dim && dim[ i ] != AST__BAD ) {
g0[ i ] = dd*dim[ i ]/ddlim;
} else {
g0[ i ] = dd;
}
}
ret = MakeBasisVectors( map, nin, nout, g0, psetg, psetw, status );
/* If the above didn't produce good positions, try moving out along each
grid axis in turn. */
for( ii = 0; !ret && ii < nin; ii++ ) {
for( i = 0; i < nin; i++ ) g0[ i ] = 1.0;
g0[ ii ] = dd;
ret = MakeBasisVectors( map, nin, nout, g0, psetg, psetw, status );
}
/* Go further out from the origin for the next set of tests (if any). */
dd *= 2.0;
}
}
/* Free resources. */
g0 = astFree( g0 );
/* Return the result. */
return ret;
}
static int FindLonLatSpecAxes( FitsStore *store, char s, int *axlon, int *axlat,
int *axspec, const char *method, const char *class, int *status ) {
/*
* Name:
* FindLonLatSpecAxes
* Purpose:
* Search the CTYPE values in a FitsStore for celestial and spectral axes.
* Type:
* Private function.
* Synopsis:
* int FindLonLatSpecAxes( FitsStore *store, char s, int *axlon, int *axlat,
* int *axspec, const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* The supplied FitsStore is searched for axes with a specified axis
* description character which describe celestial longitude or latitude
* or spectral position.
* Parameters:
* store
* A structure containing values for FITS keywords relating to
* the World Coordinate System.
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* axlon
* Address of a location at which to return the index of the
* longitude axis (if found). This is the value of "i" within the
* keyword name "CTYPEi". A value of -1 is returned if no longitude
* axis is found.
* axlat
* Address of a location at which to return the index of the
* latitude axis (if found). This is the value of "i" within the
* keyword name "CTYPEi". A value of -1 is returned if no latitude
* axis is found.
* axspec
* Address of a location at which to return the index of the
* spectral axis (if found). This is the value of "i" within the
* keyword name "CTYPEi". A value of -1 is returned if no spectral
* axis is found.
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* One is returned if both celestial axes were found. Zero is returned if
* either axis was not found. The presence of a spectral axis does not
* affect the returned value.
* Notes:
* - If an error occurs, zero is returned.
*/
/* Local Variables: */
char *assys;
char *astype;
char algcode[5];
char stype[5];
const char *ctype;
double dval;
int i;
int wcsaxes;
/* Initialise */
*axlon = -1;
*axlat = -1;
*axspec = -1;
/* Check the global status. */
if ( !astOK ) return 0;
/* Obtain the number of FITS WCS axes in the header. If the WCSAXES header
was specified, use it. Otherwise assume it is the same as the number
of pixel axes. */
dval = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status );
if( dval != AST__BAD ) {
wcsaxes = (int) dval + 0.5;
} else {
wcsaxes = store->naxis;
}
/* Loop round the FITS WCS axes, getting each CTYPE value. */
for( i = 0; i < wcsaxes && astOK; i++ ){
ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
/* Check a value was found. */
if( ctype ) {
/* First check for spectral axes, either FITS-WCS or AIPS-like. */
if( IsSpectral( ctype, stype, algcode, status ) ||
IsAIPSSpectral( ctype, &astype, &assys, status ) ) {
*axspec = i;
/* Otherwise look for celestial axes. Celestial axes must have a "-" as the
fifth character in CTYPE. */
} else if( ctype[4] == '-' ) {
/* See if this is a longitude axis (e.g. if the first 4 characters of CTYPE
are "RA--" or "xLON" or "yzLN" ). */
if( !strncmp( ctype, "RA--", 4 ) ||
!strncmp( ctype, "AZ--", 4 ) ||
!strncmp( ctype + 1, "LON", 3 ) ||
!strncmp( ctype + 2, "LN", 2 ) ){
*axlon = i;
/* Otherwise see if it is a latitude axis. */
} else if( !strncmp( ctype, "DEC-", 4 ) ||
!strncmp( ctype, "EL--", 4 ) ||
!strncmp( ctype + 1, "LAT", 3 ) ||
!strncmp( ctype + 2, "LT", 2 ) ){
*axlat = i;
}
}
}
}
/* Indicate failure if an error occurred. */
if( !astOK ) {
*axlon = -1;
*axlat = -1;
*axspec = -1;
}
/* Return the result. */
return ( *axlat != -1 && *axlon != -1 );
}
static void FindWcs( AstFitsChan *this, int last, int all, int rewind,
const char *method, const char *class, int *status ){
/*
* Name:
* FindWcs
* Purpose:
* Find the first or last FITS WCS related keyword in a FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void FindWcs( AstFitsChan *this, int last, int all, int rewind,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* A search is made through the FitsChan for the first or last card
* which relates to a FITS WCS keyword (any encoding). If "last" is
* non-zero, the next card becomes the current card. If "last" is
* zero, the WCS card is left as the current card. Cards marked as
* having been read are included or not, as specified by "all".
* Parameters:
* this
* Pointer to the FitsChan.
* last
* If non-zero, the last WCS card is searched for. Otherwise, the
* first WCS card is searched for.
* all
* If non-zero, then cards marked as having been read are included
* in the search. Otherwise such cards are ignored.
* rewind
* Only used if "last" is zero (i.e. the first card is being
* searched for). If "rewind" is non-zero, then the search starts
* from the first card in the FitsChan. If zero, the search starts
* from the current card.
* 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.
* Notes:
* - The FitsChan is left at end-of-file if no FITS-WCS keyword cards
* are found in the FitsChan.
*-
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
const char *keyname; /* Keyword name from current card */
int nfld; /* Number of fields in keyword template */
int old_ignore_used; /* Original value of variable ignore_used */
/* Check the global status. Also check the FitsChan is not empty. */
if( !astOK || !this->head ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this);
/* Indicate that we should, or should not, skip over cards marked as having
been read. */
old_ignore_used = ignore_used;
ignore_used = all ? 0 : 1;
/* If required, set the FitsChan to start or end of file. */
if( last ) {
astSetCard( this, INT_MAX );
} else if( rewind ) {
astClearCard( this );
}
/* If the current card is marked as used, and we are skipping used cards,
move on to the next unused card */
if( CARDUSED( this->card ) ) MoveCard( this, last?-1:1, method, class, status );
/* Check each card moving backwards from the end to the start, or
forwards from the start to the end, until a WCS keyword is found,
or the other end of the FitsChan is reached. */
while( astOK ){
/* Get the keyword name from the current card. */
keyname = CardName( this, status );
/* Save a pointer to the keyword if it is the first non-null, non-comment
card. */
if( keyname ) {
/* If it matches any of the WCS keywords, move on one card
and break out of the loop. */
if( Match( keyname, "CRVAL%d%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "CRPIX%d%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "CDELT%d%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "CROTA%d", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "CTYPE%d%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "CUNIT%d%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PC%3d%3d%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "CD%3d%3d%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "CD%1d_%1d%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PC%1d_%1d%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "LONGPOLE", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "LONPOLE%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "LATPOLE%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PROJP%d", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PV%d_%d%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PS%d_%d%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "EPOCH", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "EQUINOX%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "MJD-OBS", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "DATE-OBS", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "TIMESYS", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "RADECSYS", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "RADESYS%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "C%1dVAL%d", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "C%1dPIX%d", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "C%1dELT%d", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "C%1dYPE%d", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "C%1dNIT%d", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "CNPIX1", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "CNPIX2", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PPO%d", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "AMDX%d", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "AMDY%d", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "XPIXELSZ", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "YPIXELSZ", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PLTRAH", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PLTRAM", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PLTRAS", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PLTDECD", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PLTDECM", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PLTDECS", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PLTDECSN", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PLTSCALE", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PPO1", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PPO2", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PPO4", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "PPO5", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "WCSNAME%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "SPECSYS%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "SSYSSRC%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "ZSOURCE%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "VELOSYS%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "RESTFRQ%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "MJD_AVG%0c", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "OBSGEO-X", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "OBSGEO-Y", 0, NULL, &nfld, method, class, status ) ||
Match( keyname, "OBSGEO-Z", 0, NULL, &nfld, method, class, status ) ) {
if( last ) MoveCard( this, 1, method, class, status );
break;
}
}
/* Leave the FitsChan at end-of-file if no WCS cards were found. */
if( (last && FitsSof( this, status ) ) ||
(!last && astFitsEof( this ) ) ) {
astSetCard( this, INT_MAX );
break;
} else {
MoveCard( this, last?-1:1, method, class, status );
}
}
/* Re-instate the original flag indicating if cards marked as having been
read should be skipped over. */
ignore_used = old_ignore_used;
/* Return. */
return;
}
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 "fitschan.h"
* int FindString( int n, const char *list[], const char *test,
* const char *text, const char *method, const char *class, int *status )
* Class Membership:
* FitsChan 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 int FitOK( int n, double *act, double *est, double tol, int *status ) {
/*
* Name:
* FitOK
* Purpose:
* See if a fit is usable.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int FitOK( int n, double *act, double *est, double tol, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function is supplied with a set of actual data values, and the
* corresponding values estimated by some fitting process. It tests
* that the RMS residual between them is no more than "tol".
* Parameters:
* n
* Number of data points.
* act
* Pointer to the start of the actual data values.
* est
* Pointer to the start of the estimated data values.
* tol
* The largest acceptable RMS error between "act" and "est".
* status
* Pointer to the inherited status variable.
* Returned Value:
* A value of 1 is returned if the two sets of values agree. Zero is
* returned otherwise.
* Notes:
* - Zero is returned if an error occurs.
*/
/* Local Variables: */
int ret, i;
double s1, s2;
double *px, *py, diff, mserr;
/* Initialise */
ret = 0;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Initialise the sum of the squared residuals, and the number summed. */
s1 = 0.0;
s2 = 0.0;
/* Initialise pointers to the next actual and estimated values to use. */
px = act;
py = est;
/* Loop round all pairs of good actual and estimate value. */
for( i = 0; i < n; i++, px++, py++ ){
if( *px != AST__BAD && *py != AST__BAD ) {
/* Increment the sums need to find the RMS residual between the actual
and estimated values. */
diff = *px - *py;
s1 += diff*diff;
s2 += 1.0;
}
}
/* If the sums are usable... */
if( s2 > 0.0 ) {
/* Form the mean squared residual, and check if it is less than the
squared error limit. */
mserr = s1/s2;
if( mserr < tol*tol ) ret = 1;
}
/* Return the result. */
return ret;
}
static int FitsAxisOrder( AstFitsChan *this, int nwcs, AstFrame *wcsfrm,
int *perm, int *status ){
/*
* Name:
* FitsAxisOrder
* Purpose:
* Return the order of WCS axes specified by attribute FitsAxisOrder.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int FitsAxisOrder( AstFitsChan *this, int nwcs, AstFrame *wcsfrm,
* int *perm, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function returns an array indicating the order of the WCS axes
* within the output FITS header, as specified by the FitsAxisOrder
* attribute.
* Parameters:
* this
* Pointer to the FitsChan.
* nwcs
* The number of axes in "wcsfrm".
* wcsfrm
* The Frame containing the output WCS axes.
* perm
* Pointer to an array of "nwcs" integers. On exit, element "k"
* of this array holds the zero-based index of the FITS-WCS axis
* (i.e. one less than the value of "i" in the keyword names
* "CTYPEi", "CRVALi", etc) that describes the k'th axis in "wcsfrm".
* In other words, "perm[ast_index] = fits_index". The order is
* determined by the FitsAxisOrder attribute. If this attribute is
* "" or "", then "perm[k]=k" for all k on exit (i.e.
* a unit mapping between axes in "wcsfrm" and the FITS header).
* status
* Pointer to the inherited status variable.
* Returned Value:
* Returns zero if the FitsAxisOrder attribute is ", and
* non-zero otherwise. This is a flag indicating if the returned
* values in "perm" can be used s they are.
*/
/* Local Variables: */
AstKeyMap *km; /* KeyMap holding axis indices keyed by axis symbols */
char **words; /* Pointer to array of words from FitsAxisOrder */
char attr_name[15];/* Attribute name */
const char *attr; /* Pointer to a string holding the FitsAxisOrder value */
int i; /* Loop count */
int j; /* Zero-based axis index */
int k; /* Zero-based axis index */
int nword; /* Number of words in FitsAxisOrder */
int result; /* Retrned value */
/* Check the inherited status. */
if( !astOK ) return 0;
/* Initialise the returned array to a unit mapping from Frame axis to
FITS axis. */
for( i = 0; i < nwcs; i++ ) perm[ i ] = i;
/* Get the FitsAxisOrder attribute value, and set the returned value to
indicate if it is "". */
attr = astGetFitsAxisOrder( this );
result = !astChrMatch( attr, "" );
/* Return immediately if it is "" or "". */
if( result && !astChrMatch( attr, "" ) ) {
/* Create a KeyMap in which each key is the Symbol for an axis and the
associated value is the zero based index of the axis within "wcsfrm". */
km = astKeyMap( "KeyCase=0", status );
for( i = 0; i < nwcs; i++ ){
sprintf( attr_name, "Symbol(%d)", i + 1 );
astMapPut0I( km, astGetC( wcsfrm, attr_name ), i, NULL );
}
/* Split the FitsAxisOrder value into a collection of space-separated words. */
words = astChrSplit( attr, &nword );
/* Loop round them all. */
k = 0;
for( i = 0; i < nword; i++ ) {
/* Get the zero based index within "wcsfrm" of the axis that has a Symbol
equal to the current word from FitsAxisOrder. */
if( astMapGet0I( km, words[ i ], &j ) ) {
/* If this "wcsfrm" axis has already been used, report an error. */
if( j < 0 ) {
if( astOK ) astError( AST__ATTIN, "astWrite(fitschan): "
"attribute FitsAxisOrder (%s) refers to axis "
"%s more than once.", status, attr, words[ i ] );
/* Otherwise, set the corresponding element of the returned array, and
ensure this axis cannot be used again by assigning it an index of -1
in the KeyMap. */
} else {
perm[ j ] = k++;
astMapPut0I( km, words[ i ], -1, NULL );
}
}
/* Free the memory holding the copy of the word. */
words[ i ] = astFree( words[ i ] );
}
/* Report an error if any wcsfrm axes were not included in FitsAxisOrder. */
if( astOK ) {
for( i = 0; i < nwcs; i++ ){
sprintf( attr_name, "Symbol(%d)", i + 1 );
if( astMapGet0I( km, astGetC( wcsfrm, attr_name ), &j ) ) {
if( j >= 0 ) {
astError( AST__ATTIN, "astWrite(fitschan): attribute FitsAxisOrder "
"(%s) does not specify a position for WCS axis '%s'.",
status, attr, astGetC( wcsfrm, attr_name ) );
break;
}
}
}
}
/* Free resources. */
words = astFree( words );
km = astAnnul( km );
}
return result;
}
static int FitsFromStore( AstFitsChan *this, FitsStore *store, int encoding,
double *dim, AstFrameSet *fs, const char *method,
const char *class, int *status ){
/*
* Name:
* FitsFromStore
* Purpose:
* Store WCS keywords in a FitsChan.
* Type:
* Private function.
* Synopsis:
* int FitsFromStore( AstFitsChan *this, FitsStore *store, int encoding,
* double *dim, AstFrameSet *fs, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* A FitsStore is a structure containing a generalised represention of
* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
* from a set of FITS header cards (using a specified encoding), or
* an AST FrameSet. In other words, a FitsStore is an encoding-
* independant intermediary staging post between a FITS header and
* an AST FrameSet.
*
* This function copies the WCS information stored in the supplied
* FitsStore into the supplied FitsChan, using a specified encoding.
* Parameters:
* this
* Pointer to the FitsChan.
* store
* Pointer to the FitsStore.
* encoding
* The encoding to use.
* dim
* Pointer to an array holding the array dimensions (AST__BAD
* indicates that the dimenson is not known).
* 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:
* A value of 1 is returned if succesfull, and zero is returned
* otherwise.
*/
/* Local Variables: */
int ret;
/* Initialise */
ret = 0;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Set the current card so that it points to the last WCS-related keyword
in the FitsChan (whether previously read or not). Any new WCS related
keywords either over-write pre-existing cards for the same keyword, or
(if no pre-existing card exists) are inserted after the last WCS related
keyword. */
FindWcs( this, 1, 1, 0, method, class, status );
/* Do each non-standard FITS encoding... */
if( encoding == DSS_ENCODING ){
ret = DSSFromStore( this, store, method, class, status );
} else if( encoding == FITSPC_ENCODING ){
ret = PCFromStore( this, store, method, class, status );
} else if( encoding == FITSIRAF_ENCODING ){
ret = IRAFFromStore( this, store, method, class, status );
} else if( encoding == FITSAIPS_ENCODING ){
ret = AIPSFromStore( this, store, method, class, status );
} else if( encoding == FITSAIPSPP_ENCODING ){
ret = AIPSPPFromStore( this, store, method, class, status );
} else if( encoding == FITSCLASS_ENCODING ){
ret = CLASSFromStore( this, store, fs, dim, method, class, status );
/* Standard FITS-WCS encoding */
} else {
ret = WcsFromStore( this, store, method, class, status );
}
/* If there are any Tables in the FitsStore move the KeyMap that contains
them from the FitsStore to the FitsChan, from where they can be
retrieved using the public astGetTables method. */
if( astMapSize( store->tables ) > 0 ) {
if( !this->tables ) this->tables = astKeyMap( " ", status );
astMapCopy( this->tables, store->tables );
(void) astAnnul( store->tables );
store->tables = astKeyMap( " ", status );
}
/* If an error has occurred, return zero. */
if( !astOK ) ret = 0;
/* Return the answer. */
return ret;
}
static FitsStore *FitsToStore( AstFitsChan *this, int encoding,
const char *method, const char *class, int *status ){
/*
* Name:
* FitsToStore
* Purpose:
* Return a pointer to a FitsStore structure containing WCS information
* read from the supplied FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* FitsStore *FitsToStore( AstFitsChan *this, int encoding,
* const char *method, const char *class )
* Class Membership:
* FitsChan member function.
* Description:
* A FitsStore is a structure containing a generalised represention of
* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
* from a set of FITS header cards (using a specified encoding), or
* an AST FrameSet. In other words, a FitsStore is an encoding-
* independant intermediary staging post between a FITS header and
* an AST FrameSet.
*
* This function creates a new FitsStore containing WCS information
* read from the supplied FitsChan using the specified encoding. An
* error is reported and a null pointer returned if the FitsChan does
* not contain usable WCS information with the specified encoding.
* Parameters:
* this
* Pointer to the FitsChan.
* encoding
* The encoding to use.
* 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.
* Returned Value:
* A pointer to a new FitsStore, or NULL if an error has occurred. The
* FitsStore should be released using FreeStore function when it is no
* longer needed.
*/
/* Local Variables: */
AstFitsChan *trans;
FitsStore *ret;
/* Initialise */
ret = NULL;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Allocate memory for the new FitsStore, and store NULL pointers in it. */
ret = (FitsStore *) astMalloc( sizeof(FitsStore) );
if( ret ) {
ret->cname = NULL;
ret->ctype = NULL;
ret->ctype_com = NULL;
ret->cunit = NULL;
ret->ps = NULL;
ret->radesys = NULL;
ret->wcsname = NULL;
ret->wcsaxes = NULL;
ret->pc = NULL;
ret->cdelt = NULL;
ret->crpix = NULL;
ret->crval = NULL;
ret->equinox = NULL;
ret->latpole = NULL;
ret->lonpole = NULL;
ret->mjdobs = NULL;
ret->mjdavg = NULL;
ret->dut1 = NULL;
ret->pv = NULL;
ret->specsys = NULL;
ret->ssyssrc = NULL;
ret->obsgeox = NULL;
ret->obsgeoy = NULL;
ret->obsgeoz = NULL;
ret->restfrq = NULL;
ret->restwav = NULL;
ret->zsource = NULL;
ret->velosys = NULL;
ret->asip = NULL;
ret->bsip = NULL;
ret->apsip = NULL;
ret->bpsip = NULL;
ret->imagfreq = NULL;
ret->axref = NULL;
ret->naxis = 0;
ret->timesys = NULL;
ret->tables = astKeyMap( " ", status );
ret->skyref = NULL;
ret->skyrefp = NULL;
ret->skyrefis = NULL;
}
/* Call the routine apropriate to the encoding. */
if( encoding == DSS_ENCODING ){
DSSToStore( this, ret, method, class, status );
/* All other foreign encodings are treated as variants of FITS-WCS. */
} else {
/* Create a new FitsChan containing standard translations for any
non-standard keywords in the supplied FitsChan. The non-standard
keywords are marked as provisionally read in the supplied FitsChan. */
trans = SpecTrans( this, encoding, method, class, status );
/* Copy the required values to the FitsStore, using keywords in "trans"
in preference to those in "this". */
WcsToStore( this, trans, ret, method, class, status );
/* Delete the temporary FitsChan holding translations of non-standard
keywords. */
if( trans ) trans = (AstFitsChan *) astDelete( trans );
/* Store the number of pixel axes. This is taken as the highest index used
in any primary CRPIX keyword. */
ret->naxis = GetMaxJM( &(ret->crpix), ' ', status ) + 1;
}
/* If an error has occurred, free the returned FitsStore, and return a null
pointer. */
if( !astOK ) ret = FreeStore( ret, status );
/* Return the answer. */
return ret;
}
static void FreeItem( double ****item, int *status ){
/*
* Name:
* FreeItem
* Purpose:
* Frees all dynamically allocated memory associated with a specified
* item in a FitsStore.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void FreeItem( double ****item, int *status );
* Class Membership:
* FitsChan member function.
* Description:
* Frees all dynamically allocated memory associated with the specified
* item in a FitsStore. A NULL pointer is stored in the FitsStore.
* Parameters:
* item
* The address of the pointer within the FitsStore which locates the
* arrays of values for the required keyword (eg &(store->crval) ).
* The array located by the supplied pointer contains a vector of
* pointers. Each of these pointers is associated with a particular
* co-ordinate version (s), and locates an array of pointers for that
* co-ordinate version. Each such array of pointers has an element
* for each intermediate axis number (j), and the pointer locates an
* array of axis keyword values. These arrays of keyword values have
* one element for every pixel axis (i) or projection parameter (m).
* status
* Pointer to the inherited status variable.
* Notes:
* - This function attempt to execute even if an error has occurred.
*/
/* Local Variables: */
int si; /* Integer co-ordinate version index */
int j; /* Intermediate co-ordinate axis index */
int oldstatus; /* Old error status value */
int oldreport; /* Old error reporting value */
/* Other initialisation to avoid compiler warnings. */
oldreport = 0;
/* Check the supplied pointer */
if( item && *item ){
/* Start a new error reporting context. */
oldstatus = astStatus;
if( !astOK ) {
oldreport = astReporting( 0 );
astClearStatus;
}
/* Loop round each coordinate version. */
for( si = 0; si < astSizeOf( (void *) *item )/sizeof(double **);
si++ ){
/* Check the pointer stored for this co-ordinate version is not null. */
if( (*item)[si] ) {
/* Loop round the intermediate axes. */
for( j = 0; j < astSizeOf( (void *) (*item)[si] )/sizeof(double *);
j++ ){
/* Free the pixel axis/parameter index pointer. */
(*item)[si][j] = (double *) astFree( (void *) (*item)[si][j] );
}
/* Free the intermediate axes pointer */
(*item)[si] = (double **) astFree( (void *) (*item)[si] );
}
}
/* Free the co-ordinate versions pointer */
*item = (double ***) astFree( (void *) *item );
/* If there was an error status on entry to this function, re-instate it.
Otherwise, allow any new error status to remain. */
if( oldstatus ){
if( !astOK ) astClearStatus;
astSetStatus( oldstatus );
astReporting( oldreport );
}
}
}
static void FreeItemC( char *****item, int *status ){
/*
* Name:
* FreeItemC
* Purpose:
* Frees all dynamically allocated memory associated with a specified
* string item in a FitsStore.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void FreeItemC( char *****item, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Frees all dynamically allocated memory associated with the specified
* string item in a FitsStore. A NULL pointer is stored in the FitsStore.
* Parameters:
* item
* The address of the pointer within the FitsStore which locates the
* arrays of values for the required keyword (eg &(store->ctype) ).
* The array located by the supplied pointer contains a vector of
* pointers. Each of these pointers is associated with a particular
* co-ordinate version (s), and locates an array of pointers for that
* co-ordinate version. Each such array of pointers has an element
* for each intermediate axis number (j), and the pointer locates an
* array of axis keyword values. These arrays of keyword values have
* one element (a char pointyer) for every pixel axis (i) or
* projection parameter (m).
* status
* Pointer to the inherited status variable.
* Notes:
* - This function attempts to execute even if an error has occurred.
*/
/* Local Variables: */
int si; /* Integer co-ordinate version index */
int i; /* Intermediate co-ordinate axis index */
int jm; /* Pixel co-ordinate axis or parameter index */
int oldstatus; /* Old error status value */
int oldreport; /* Old error reporting value */
/* Other initialisation to avoid compiler warnings. */
oldreport = 0;
/* Check the supplied pointer */
if( item && *item ){
/* Start a new error reporting context. */
oldstatus = astStatus;
if( !astOK ) {
oldreport = astReporting( 0 );
astClearStatus;
}
/* Loop round each coordinate version. */
for( si = 0; si < astSizeOf( (void *) *item )/sizeof(char ***);
si++ ){
/* Check the pointer stored for this co-ordinate version is not null. */
if( (*item)[si] ) {
/* Loop round the intermediate axes. */
for( i = 0; i < astSizeOf( (void *) (*item)[si] )/sizeof(char **);
i++ ){
/* Check the pointer stored for this intermediate axis is not null. */
if( (*item)[si][i] ) {
/* Loop round the pixel axes or parameter values. */
for( jm = 0; jm < astSizeOf( (void *) (*item)[si][i] )/sizeof(char *);
jm++ ){
/* Free the string. */
(*item)[si][i][jm] = (char *) astFree( (void *) (*item)[si][i][jm] );
}
/* Free the pixel axes/parameter pointer */
(*item)[si][i] = (char **) astFree( (void *) (*item)[si][i] );
}
}
/* Free the intermediate axes pointer */
(*item)[si] = (char ***) astFree( (void *) (*item)[si] );
}
}
/* Free the co-ordinate versions pointer */
*item = (char ****) astFree( (void *) *item );
/* If there was an error status on entry to this function, re-instate it.
Otherwise, allow any new error status to remain. */
if( oldstatus ){
if( !astOK ) astClearStatus;
astSetStatus( oldstatus );
astReporting( oldreport );
}
}
}
static FitsStore *FreeStore( FitsStore *store, int *status ){
/*
* Name:
* FreeStore
* Purpose:
* Free dynamic arrays stored in a FitsStore structure.
* Type:
* Private function.
* Synopsis:
* FitsStore *FreeStore( FitsStore *store, int *status )
* Class Membership:
* FitsChan
* Description:
* This function frees all dynamically allocated arrays stored in the
* supplied FitsStore structure, and returns a NULL pointer.
* Parameters:
* store
* Pointer to the structure to clean.
* status
* Pointer to the inherited status variable.
* Notes:
* - This function attempts to execute even if an error exists on entry.
*/
/* Return if no FitsStore was supplied. */
if( !store ) return NULL;
/* Free each of the dynamic arrays stored in the FitsStore. */
FreeItemC( &(store->cname), status );
FreeItemC( &(store->ctype), status );
FreeItemC( &(store->ctype_com), status );
FreeItemC( &(store->cunit), status );
FreeItemC( &(store->radesys), status );
FreeItemC( &(store->wcsname), status );
FreeItemC( &(store->specsys), status );
FreeItemC( &(store->ssyssrc), status );
FreeItemC( &(store->ps), status );
FreeItemC( &(store->timesys), status );
FreeItem( &(store->pc), status );
FreeItem( &(store->cdelt), status );
FreeItem( &(store->crpix), status );
FreeItem( &(store->crval), status );
FreeItem( &(store->equinox), status );
FreeItem( &(store->latpole), status );
FreeItem( &(store->lonpole), status );
FreeItem( &(store->mjdobs), status );
FreeItem( &(store->dut1), status );
FreeItem( &(store->mjdavg), status );
FreeItem( &(store->pv), status );
FreeItem( &(store->wcsaxes), status );
FreeItem( &(store->obsgeox), status );
FreeItem( &(store->obsgeoy), status );
FreeItem( &(store->obsgeoz), status );
FreeItem( &(store->restfrq), status );
FreeItem( &(store->restwav), status );
FreeItem( &(store->zsource), status );
FreeItem( &(store->velosys), status );
FreeItem( &(store->asip), status );
FreeItem( &(store->bsip), status );
FreeItem( &(store->apsip), status );
FreeItem( &(store->bpsip), status );
FreeItem( &(store->imagfreq), status );
FreeItem( &(store->axref), status );
store->tables = astAnnul( store->tables );
FreeItem( &(store->skyref), status );
FreeItem( &(store->skyrefp), status );
FreeItemC( &(store->skyrefis), status );
return (FitsStore *) astFree( (void *) store );
}
static char *FormatKey( const char *key, int c1, int c2, char s, int *status ){
/*
* Name:
* FormatKey
* Purpose:
* Format a keyword name with indices and co-ordinate version character.
* Type:
* Private function.
* Synopsis:
* char *FormatKey( const char *key, int c1, int c2, char s, int *status )
* Class Membership:
* FitsChan
* Description:
* This function formats a keyword name by including the supplied
* axis/parameter indices and co-ordinate version character.
* Parameters:
* key
* The base name of the keyword (e.g. "CD", "CRVAL", etc).
* c1
* An integer value to append to the end of the keyword. Ignored if
* less than zero.
* c2
* A second integer value to append to the end of the keyword. Ignored if
* less than zero. This second integer is preceded by an underscore.
* s
* The co-ordinate version character to append to the end of the
* final string. Ignored if blank.
* status
* Pointer to the inherited status variable.
* Returned Value;
* A pointer to a static character buffer containing the final string.
* NULL if an error occurs.
*/
/* Local Variables: */
astDECLARE_GLOBALS
char *ret;
int len;
int nc;
/* Initialise */
ret = NULL;
/* Check inherited status */
if( !astOK ) return ret;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(NULL);
/* No characters stored yet. A value of -1 is used to indicate that an
error has occurred. */
len = 0;
/* Store the supplied keyword base name. */
if( len >= 0 && ( nc = sprintf( formatkey_buff + len, "%s", key ) ) >= 0 ){
len += nc;
} else {
len = -1;
}
/* If index c1 has been supplied, append it to the end of the string. */
if( c1 >= 0 ) {
if( len >= 0 && ( nc = sprintf( formatkey_buff + len, "%d", c1 ) ) >= 0 ){
len += nc;
} else {
len = -1;
}
/* If index c2 has been supplied, append it to the end of the string,
preceded by an underscore. */
if( c2 >= 0 ) {
if( len >= 0 && ( nc = sprintf( formatkey_buff + len, "_%d", c2 ) ) >= 0 ){
len += nc;
} else {
len = -1;
}
}
}
/* If a co-ordinate version character has been supplied, append it to the end
of the string. */
if( s != ' ' ) {
if( len >= 0 && ( nc = sprintf( formatkey_buff + len, "%c", s ) ) >= 0 ){
len += nc;
} else {
len = -1;
}
}
/* Report an error if necessary */
if( len < 0 && astOK ) {
astError( AST__INTER, "FormatKey(fitschan): AST internal error; failed "
"to format the keyword %s with indices %d and %d, and "
"co-ordinate version %c.", status, key, c1, c2, s );
ret = NULL;
} else {
ret = formatkey_buff;
}
return formatkey_buff;
}
static AstObject *FsetFromStore( AstFitsChan *this, FitsStore *store,
const char *method, const char *class, int *status ){
/*
* Name:
* FsetFromStore
* Purpose:
* Create a FrameSet using the the information previously stored in
* the suppllied FitsStore structure.
* Type:
* Private function.
* Synopsis:
* AstObject *FsetFromStore( AstFitsChan *this, FitsStore *store,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* A FitsStore is a structure containing a generalised represention of
* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
* from a set of FITS header cards (using a specified encoding), or
* an AST FrameSet. In other words, a FitsStore is an encoding-
* independant intermediary staging post between a FITS header and
* an AST FrameSet.
*
* This function creates a new FrameSet containing WCS information
* stored in the supplied FitsStore. A null pointer is returned and no
* error is reported if this is not possible.
* Parameters:
* this
* The FitsChan from which the keywords were read. Warning messages
* are added to this FitsChan if the celestial co-ordinate system is
* not recognized.
* store
* Pointer to the FitsStore.
* 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:
* A pointer to the new FrameSet, or a null pointer if no FrameSet
* could be constructed.
* Notes:
* - The pixel Frame is given a title of "Pixel Coordinates", and
* each axis in the pixel Frame is given a label of the form "Pixel
* axis ", where is the axis index (starting at one).
* - The FITS CTYPE keyword values are used to set the labels for any
* non-celestial axes in the physical coordinate Frames, and the FITS
* CUNIT keywords are used to set the corresponding units strings.
* - On exit, the pixel Frame is the base Frame, and the physical
* Frame derived from the primary axis descriptions is the current Frame.
* - Extra Frames are added to hold any secondary axis descriptions. All
* axes within such a Frame refer to the same coordinate version ('A',
* 'B', etc).
*/
/* Local Variables: */
AstFrame *frame; /* Pointer to pixel Frame */
AstFrameSet *ret; /* Pointer to returned FrameSet */
char buff[ 20 ]; /* Buffer for axis label */
char s; /* Co-ordinate version character */
int i; /* Pixel axis index */
int physical; /* Index of primary physical co-ordinate Frame */
int pixel; /* Index of pixel Frame in returned FrameSet */
int use; /* Has this co-ordinate version been used? */
/* Initialise */
ret = NULL;
/* Check the inherited status. */
if( !astOK ) return (AstObject *) ret;
/* Only proceed if there are some axes. */
if( store->naxis ) {
/* Create a Frame describing the pixel coordinate system. Give it the Domain
GRID. */
frame = astFrame( store->naxis, "Title=Pixel Coordinates,Domain=GRID", status );
/* Store labels for each pixel axis. */
if( astOK ){
for( i = 0; i < store->naxis; i++ ){
sprintf( buff, "Pixel axis %d", i + 1 );
astSetLabel( frame, i, buff );
}
}
/* Create the FrameSet initially holding just the pixel coordinate frame
(this becomes the base Frame). */
ret = astFrameSet( frame, "", status );
/* Annul the pointer to the pixel coordinate Frame. */
frame = astAnnul( frame );
/* Get the index of the pixel Frame in the FrameSet. */
pixel = astGetCurrent( ret );
/* Produce the Frame describing the primary axis descriptions, and add it
into the FrameSet. */
AddFrame( this, ret, pixel, store->naxis, store, ' ', method, class, status );
/* Get the index of the primary physical co-ordinate Frame in the FrameSet. */
physical = astGetCurrent( ret );
/* Loop, producing secondary axis Frames for each of the co-ordinate
versions stored in the FitsStore. */
for( s = 'A'; s <= GetMaxS( &(store->crval), status ) && astOK; s++ ){
/* Only use this co-ordinate version character if any of the required
keywords (for any axis) are stored in the FitsStore. */
use = 0;
for( i = 0; i < store->naxis; i++ ){
if( GetItem( &(store->crval), i, 0, s, NULL, method, class, status ) != AST__BAD ||
GetItem( &(store->crpix), 0, i, s, NULL, method, class, status ) != AST__BAD ||
GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status ) != NULL ){
use = 1;
break;
}
}
/* If this co-ordinate version has been used, add a Frame to the returned
FrameSet holding this co-ordinate version. */
if( use ) AddFrame( this, ret, pixel, store->naxis, store, s, method, class, status );
}
/* Ensure the pixel Frame is the Base Frame and the primary physical
Frame is the Current Frame. */
astSetBase( ret, pixel );
astSetCurrent( ret, physical );
/* Remove any unneeded Frames that hold a FITS representation of offset
coordinates. */
TidyOffsets( ret, status );
/* If an error has occurred, free the returned FrameSet and return a null
pointer. */
if( !astOK ) ret = astAnnul( ret );
}
/* Return the answer. */
return (AstObject *) ret;
}
static FitsStore *FsetToStore( AstFitsChan *this, AstFrameSet *fset, int naxis,
double *dim, int encoding, const char *class,
const char *method, int *status ){
/*
* Name:
* FsetToStore
* Purpose:
* Fill a FitsStore structure with a description of the supplied
* FrameSet.
* Type:
* Private function.
* Synopsis:
* FitsStore *FsetToStore( AstFitsChan *this, AstFrameSet *fset, int naxis,
* double *dim, int encoding, const char *class,
* const char *method, int *status )
* Class Membership:
* FitsChan
* Description:
* A FitsStore is a structure containing a generalised represention of
* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
* from a set of FITS header cards (using a specified encoding), or
* an AST FrameSet. In other words, a FitsStore is an encoding-
* independant intermediary staging post between a FITS header and
* an AST FrameSet.
*
* This function creates a new FitsStore containing WCS information
* read from the supplied FitsChan using the specified encoding. An
* error is reported and a null pointer returned if the FitsChan does
* not contain usable WCS information with the specified encoding.
* Parameters:
* this
* Pointer to the FitsChan.
* fset
* Pointer to the FrameSet.
* naxis
* The number of axes in the Base Frame of the supplied FrameSet.
* dim
* Pointer to an array of pixel axis dimensions. Individual elements
* will be AST__BAD if dimensions are not known.
* encoding
* The encoding being used.
* 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:
* A pointer to a new FitsStore, or NULL if an error has occurred. The
* FitsStore should be released using FreeStore function when it is no
* longer needed.
* Notes:
* - A NULL pointer will be returned if this function is invoked
* with the AST error status set, or if it should fail for any
* reason.
* - The Base Frame in the FrameSet is used as the pixel Frame, and
* the Current Frame is used to create the primary axis descriptions.
* Attempts are made to create secondary axis descriptions for any
* other Frames in the FrameSet (up to a total of 26).
*/
/* Local Variables: */
AstFrame *frame; /* A Frame */
const char *id; /* Frame Ident string */
int nfrm; /* Number of Frames in FrameSet */
char *sid; /* Pointer to array of version letters */
int frms[ 'Z' + 1 ]; /* Array of Frame indices */
FitsStore *ret; /* Returned FitsStore */
char s; /* Next available co-ordinate version character */
char s0; /* Co-ordinate version character */
int ibase; /* Base Frame index */
int icurr; /* Current Frame index */
int ifrm; /* Next Frame index */
int isoff; /* Is the Frame an offset SkyFrame? */
int primok; /* Primary Frame stored succesfully? */
/* Initialise */
ret = NULL;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Allocate memory for the new FitsStore, and store NULL pointers in it. */
ret = (FitsStore *) astMalloc( sizeof(FitsStore) );
if( astOK ) {
ret->cname = NULL;
ret->ctype = NULL;
ret->ctype_com = NULL;
ret->cunit = NULL;
ret->ps = NULL;
ret->radesys = NULL;
ret->wcsname = NULL;
ret->wcsaxes = NULL;
ret->pc = NULL;
ret->cdelt = NULL;
ret->crpix = NULL;
ret->crval = NULL;
ret->equinox = NULL;
ret->latpole = NULL;
ret->lonpole = NULL;
ret->dut1 = NULL;
ret->mjdobs = NULL;
ret->mjdavg = NULL;
ret->pv = NULL;
ret->specsys = NULL;
ret->ssyssrc = NULL;
ret->obsgeox = NULL;
ret->obsgeoy = NULL;
ret->obsgeoz = NULL;
ret->restfrq = NULL;
ret->restwav = NULL;
ret->zsource = NULL;
ret->velosys = NULL;
ret->asip = NULL;
ret->bsip = NULL;
ret->apsip = NULL;
ret->bpsip = NULL;
ret->imagfreq = NULL;
ret->axref = NULL;
ret->naxis = naxis;
ret->timesys = NULL;
ret->tables = astKeyMap( " ", status );
ret->skyref = NULL;
ret->skyrefp = NULL;
ret->skyrefis = NULL;
/* Obtain the index of the Base Frame (i.e. the pixel frame ). */
ibase = astGetBase( fset );
/* Obtain the index of the Current Frame (i.e. the Frame to use as the
primary physical coordinate frame). */
icurr = astGetCurrent( fset );
/* Does the current Frame contain a SkyFrame that describes offset
coordinates? */
isoff = IsSkyOff( fset, icurr, status );
/* Add a description of the primary axes to the FitsStore, based on the
Current Frame in the FrameSet. */
primok = AddVersion( this, fset, ibase, icurr, ret, dim, ' ',
encoding, isoff, method, class, status );
/* Do not add any alternate axis descriptions if the primary axis
descriptions could not be produced. */
if( primok && astOK ) {
/* Get the number of Frames in the FrameSet. */
nfrm = astGetNframe( fset );
/* We now need to allocate a version letter to each Frame. Allocate
memory to hold the version letter assigned to each Frame. */
sid = (char *) astMalloc( ( nfrm + 1 )*sizeof( char ) );
/* The frms array has an entry for each of the 26 possible version
letters (starting at A and ending at Z). Each entry holds the index of
the Frame which has been assigned that version character. Initialise
this array to indicate that no version letters have yet been assigned. */
for( s = 'A'; s <= 'Z'; s++ ) {
frms[ (int) s ] = 0;
}
/* Loop round all frames (excluding the current and base and IWC Frames which
do not need version letters). If the Frame has an Ident attribute consisting
of a single upper case letter, use it as its version letter unless that
letter has already been given to an earlier frame. IWC Frames are not
written out - identify them by giving them a "sid" value of 1 (an
illegal FITS axis description character). */
for( ifrm = 1; ifrm <= nfrm; ifrm++ ){
sid[ ifrm ] = 0;
if( ifrm != icurr && ifrm != ibase ) {
frame = astGetFrame( fset, ifrm );
if( astChrMatchN( astGetDomain( frame ), "IWC", 3 ) ) {
sid[ ifrm ] = 1;
} else {
id = astGetIdent( frame );
if( strlen( id ) == 1 && isupper( id[ 0 ] ) ) {
if( frms[ (int) id[ 0 ] ] == 0 ) {
frms[ (int) id[ 0 ] ] = ifrm;
sid[ ifrm ] = id[ 0 ];
}
}
}
(void) astAnnul( frame );
}
}
/* Now go round all the Frames again, looking for Frames which did not
get a version letter assigned to it on the previous loop. Assign them
letters now, selected them from the letters not already assigned
(lowest to highest). */
s = 'A' - 1;
for( ifrm = 1; ifrm <= nfrm; ifrm++ ){
if( ifrm != icurr && ifrm != ibase && sid[ ifrm ] != 1 ) {
if( sid[ ifrm ] == 0 ){
while( frms[ (int) ++s ] != 0 );
if( s <= 'Z' ) {
sid[ ifrm ] = s;
frms[ (int) s ] = ifrm;
}
}
}
}
/* If the primary headers describe offset coordinates, create an alternate
axis description for the correspondsing absolute coordinate system. */
if( isoff && ++s <= 'Z' ) {
(void) AddVersion( this, fset, ibase, icurr, ret, dim,
s, encoding, -1, method, class, status );
}
/* Now go through all the other Frames in the FrameSet, attempting to
create alternate axis descriptions for each one. */
for( ifrm = 1; ifrm <= nfrm; ifrm++ ){
s0 = sid[ ifrm ];
if( s0 != 0 && s0 != 1 ) {
/* Does it contain an offset sky frame? */
isoff = IsSkyOff( fset, ifrm, status );
/* Write out the Frame - offset if it is offset, absolute otherwise. */
(void) AddVersion( this, fset, ibase, ifrm, ret, dim,
s0, encoding, isoff, method, class, status );
/* If the Frame is offset, create an extra alternate axis description for
the correspondsing absolute coordinate system. */
if( isoff && ++s <= 'Z' ) {
(void) AddVersion( this, fset, ibase, ifrm, ret, dim,
s, encoding, -1, method, class, status );
}
}
}
/* Free memory holding version letters */
sid = (char *) astFree( (void *) sid );
}
/* If an error has occurred, or if the primary Frame could not be cerated,
free the returned FitsStore, and return a null pointer. */
if( !astOK || !primok ) ret = FreeStore( ret, status );
}
/* Return the answer. */
return ret;
}
static int GetClean( AstFitsChan *this, int *status ) {
/*
* Name:
* GetClean
* Purpose:
* Return the value of the Clean attribute.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int GetClean( AstFitsChan *this, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function returns the value of the Clean attribute. Since this
* attribute controls the behaviour of the FitsChan in the event of an
* error condition, it is is necessary to ignore any inherited error
* condition when getting the attribute value. This is why the
* astMAKE_GET macro is not used.
* Parameters:
* this
* Pointer to the FitsChan.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The Clean value to use.
*/
/* Return if no FitsChan pointer was supplied. */
if ( !this ) return 0;
/* Return the attribute value, supplying a default value of 0 (false). */
return ( this->clean == -1 ) ? 0 : (this->clean ? 1 : 0 );
}
static int GetObjSize( AstObject *this_object, int *status ) {
/*
* Name:
* GetObjSize
* Purpose:
* Return the in-memory size of an Object.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int GetObjSize( AstObject *this, int *status )
* Class Membership:
* FitsChan member function (over-rides the astGetObjSize protected
* method inherited from the parent class).
* Description:
* This function returns the in-memory size of the supplied FitsChan,
* in bytes.
* Parameters:
* this
* Pointer to the FitsChan.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The Object size, in bytes.
* 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: */
AstFitsChan *this; /* Pointer to FitsChan structure */
FitsCard *card; /* Pointer to next FitsCard */
int result; /* Result value to return */
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain a pointers to the FitsChan structure. */
this = (AstFitsChan *) this_object;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Invoke the GetObjSize method inherited from the parent class, and then
add on any components of the class structure defined by thsi class
which are stored in dynamically allocated memory. */
result = (*parent_getobjsize)( this_object, status );
result += astTSizeOf( this->warnings );
result += astGetObjSize( this->keyseq );
result += astGetObjSize( this->keywords );
result += astGetObjSize( this->tables );
card = (FitsCard *) ( this->head );
while( card ) {
result += astTSizeOf( card );
result += card->size;
result += astTSizeOf( card->comment );
card = GetLink( card, NEXT, "astGetObjSize", "FitsChan", status );
if( (void *) card == this->head ) break;
}
/* If an error occurred, clear the result value. */
if ( !astOK ) result = 0;
/* Return the result, */
return result;
}
static int GetCDMatrix( AstFitsChan *this, int *status ){
/*
* Name:
* GetCDMatrix
* Purpose:
* Get the value of the CDMatrix attribute.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int GetCDMatrix( AstFitsChan *this, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* If the CDMatrix attribute has been set, then its value is returned.
* Otherwise, the supplied FitsChan is searched for keywords of the
* form CDi_j. If any are found a non-zero value is returned. Otherwise
* a zero value is returned.
* Parameters:
* this
* Pointer to the FitsChan.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The attribute value to use.
* Notes:
* - A value of zero is returned if an error has already occurred
* or if an error occurs for any reason within this function.
*/
/* Local Variables... */
int ret; /* Returned value */
int icard; /* Index of current card on entry */
/* Check the global status. */
if( !astOK ) return 0;
/* If a value has been supplied for the CDMatrix attribute, use it. */
if( astTestCDMatrix( this ) ) {
ret = this->cdmatrix;
/* Otherwise, check for the existence of CDi_j keywords... */
} else {
/* Save the current card index, and rewind the FitsChan. */
icard = astGetCard( this );
astClearCard( this );
/* If the FitsChan contains any keywords with the format "CDi_j" then return
1. Otherwise return zero. */
ret = astKeyFields( this, "CD%1d_%1d", 0, NULL, NULL ) ? 1 : 0;
/* Reinstate the original current card index. */
astSetCard( this, icard );
}
/* Return the result. */
return astOK ? ret : 0;
}
static int GetEncoding( AstFitsChan *this, int *status ){
/*
* Name:
* GetEncoding
* Purpose:
* Get the value of the Encoding attribute.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int GetEncoding( AstFitsChan *this, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* If the Encoding attribute has been set, then its value is returned.
* Otherwise, an attempt is made to determine the encoding scheme by
* looking for selected keywords within the FitsChan. Checks are made
* for the following keywords in the order specified, and the
* corresponding encoding is adopted when the first one is found ( where
* i, j and m are integers and s is a single upper case character):
*
* 1) Any keywords starting with "BEGAST" = Native encoding
* 2) DELTAV and VELO-xxx (or VLSR) keywords = FITS-CLASS.
* 3) Any AIPS spectral CTYPE values:
* Any of CDi_j, PROJP, LONPOLE, LATPOLE = FITS-AIPS++ encoding:
* None of the above = FITS-AIPS encoding.
* 4) Any keywords matching PCiiijjj = FITS-PC encoding
* 5) Any keywords matching CDiiijjj = FITS-IRAF encoding
* 6) Any keywords matching CDi_j, AND at least one of RADECSYS, PROJPi
* or CmVALi = FITS-IRAF encoding
* 7) Any keywords RADECSYS, PROJPi or CmVALi, and no CDi_j or PCi_j
* keywords, = FITS-PC encoding
* 8) Any keywords matching CROTAi = FITS-AIPS encoding
* 9) Keywords matching CRVALi = FITS-WCS encoding
* 10) The PLTRAH keyword = DSS encoding
* 11) If none of the above keywords are found, Native encoding is assumed.
*
* For cases 2) to 9), a check is also made that the header contains
* at least one of each keyword CTYPE, CRPIX and CRVAL. If not, then
* the checking process continues to the next case. This goes some way
* towards ensuring that the critical keywords used to determine the
* encoding are part of a genuine WCS description and have not just been
* left in the header by accident.
* Parameters:
* this
* Pointer to the FitsChan.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The encoding scheme identifier.
* Notes:
* - The function returns UNKNOWN_ENCODING if an error has already occurred
* or if an error occurs for any reason within this function.
*/
/* Local Variables... */
int hascd; /* Any CDi_j keywords found? */
int haspc; /* Any PCi_j keywords found? */
int haswcs; /* Any CRVAL, CTYPE and CRPIX found? */
int icard; /* Index of current card on entry */
int ret; /* Returned value */
/* Check the global status. */
if( !astOK ) return UNKNOWN_ENCODING;
/* If a value has been supplied for the Encoding attribute, use it. */
if( astTestEncoding( this ) ) {
ret = this->encoding;
/* Otherwise, check for the existence of certain critcal keywords... */
} else {
/* See if the header contains some CTYPE, CRPIX and CRVAL keywords. */
haswcs = astKeyFields( this, "CTYPE%d", 0, NULL, NULL ) &&
astKeyFields( this, "CRPIX%d", 0, NULL, NULL ) &&
astKeyFields( this, "CRVAL%d", 0, NULL, NULL );
/* See if there are any CDi_j keywords. */
hascd = astKeyFields( this, "CD%1d_%1d", 0, NULL, NULL );
/* See if there are any PCi_j keywords. */
haspc = astKeyFields( this, "PC%1d_%1d", 0, NULL, NULL );
/* Save the current card index, and rewind the FitsChan. */
icard = astGetCard( this );
astClearCard( this );
/* If the FitsChan contains any keywords starting with "BEGAST", then return
"Native" encoding. */
if( astKeyFields( this, "BEGAST%2f", 0, NULL, NULL ) ){
ret = NATIVE_ENCODING;
/* Otherwise, look for a FITS-CLASS signature... */
} else if( haswcs && LooksLikeClass( this, "astGetEncoding", "AstFitsChan", status ) ){
ret = FITSCLASS_ENCODING;
/* Otherwise, if the FitsChan contains any CTYPE keywords which have the
peculiar form used by AIPS, then use "FITS-AIPS" or "FITS-AIPS++" encoding. */
} else if( haswcs && HasAIPSSpecAxis( this, "astGetEncoding", "AstFitsChan", status ) ){
if( hascd ||
astKeyFields( this, "PROJP%d", 0, NULL, NULL ) ||
astKeyFields( this, "LONPOLE", 0, NULL, NULL ) ||
astKeyFields( this, "LATPOLE", 0, NULL, NULL ) ) {
ret = FITSAIPSPP_ENCODING;
} else {
ret = FITSAIPS_ENCODING;
}
/* Otherwise, if the FitsChan contains the "PLTRAH" keywords, use "DSS"
encoding. */
} else if( astKeyFields( this, "PLTRAH", 0, NULL, NULL ) ){
ret = DSS_ENCODING;
/* Otherwise, if the FitsChan contains any keywords with the format
"PCiiijjj" then return "FITS-PC" encoding. */
} else if( haswcs && astKeyFields( this, "PC%3d%3d", 0, NULL, NULL ) ){
ret = FITSPC_ENCODING;
/* Otherwise, if the FitsChan contains any keywords with the format
"CDiiijjj" then return "FITS-IRAF" encoding. */
} else if( haswcs && astKeyFields( this, "CD%3d%3d", 0, NULL, NULL ) ){
ret = FITSIRAF_ENCODING;
/* Otherwise, if the FitsChan contains any keywords with the format
"CDi_j" AND there is a RADECSYS. PROJPi or CmVALi keyword, then return
"FITS-IRAF" encoding. If "CDi_j" is present but none of the others
are, return "FITS-WCS" encoding. */
} else if( haswcs && hascd ) {
if( ( astKeyFields( this, "RADECSYS", 0, NULL, NULL ) &&
!astKeyFields( this, "RADESYS", 0, NULL, NULL ) ) ||
( astKeyFields( this, "PROJP%d", 0, NULL, NULL ) &&
!astKeyFields( this, "PV%d_%d", 0, NULL, NULL ) ) ||
( astKeyFields( this, "C%1dVAL%d", 0, NULL, NULL )) ){
ret = FITSIRAF_ENCODING;
} else {
ret = FITSWCS_ENCODING;
}
/* Otherwise, if the FitsChan contains any keywords with the format
RADECSYS. PROJPi or CmVALi keyword, then return "FITS-PC" encoding,
so long as there are no FITS-WCS equivalent keywords. */
} else if( haswcs && !haspc && !hascd && (
( astKeyFields( this, "RADECSYS", 0, NULL, NULL ) &&
!astKeyFields( this, "RADESYS", 0, NULL, NULL ) ) ||
( astKeyFields( this, "PROJP%d", 0, NULL, NULL ) &&
!astKeyFields( this, "PV%d_%d", 0, NULL, NULL ) ) ||
astKeyFields( this, "C%1dVAL%d", 0, NULL, NULL ) ) ) {
ret = FITSPC_ENCODING;
/* Otherwise, if the FitsChan contains any keywords with the format
"CROTAi" then return "FITS-AIPS" encoding. */
} else if( haswcs && astKeyFields( this, "CROTA%d", 0, NULL, NULL ) ){
ret = FITSAIPS_ENCODING;
/* Otherwise, if the FitsChan contains any keywords with the format
"CRVALi" then return "FITS-WCS" encoding. */
} else if( haswcs && astKeyFields( this, "CRVAL%d", 0, NULL, NULL ) ){
ret = FITSWCS_ENCODING;
/* If none of these conditions is met, assume Native encoding. */
} else {
ret = NATIVE_ENCODING;
}
/* Reinstate the original current card index. */
astSetCard( this, icard );
}
/* Return the encoding scheme. */
return astOK ? ret : UNKNOWN_ENCODING;
}
static void GetFiducialNSC( AstWcsMap *map, double *phi, double *theta, int *status ){
/*
* Name:
* GetFiducialNSC
* Purpose:
* Return the Native Spherical Coordinates at the fiducial point of a
* WcsMap projection.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void GetFiducialNSC( AstWcsMap *map, double *phi, double *theta, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function returns the native spherical coords corresponding at
* the fiducial point of a WcsMap.
*
* The values of parameters 1 and 2 on the longitude axis of the WcsMap
* are usually used as the native spherical coordinates of the
* fiducial point. The default values for these parameters are equal
* to the native spherical coordinates of the projection reference point.
* The exception is that a TPN projection always uses the default
* values, since the projection parameters are used to store polynomial
* coefficients.
* Parameters:
* map
* Pointer to the WcsMap.
* phi
* Address of a location at which to return the native spherical
* longitude at the fiducial point (radians).
* theta
* Address of a location at which to return the native spherical
* latitude at the fiducial point (radians).
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
int axlon; /* Index of longitude axis */
/* Initialise */
*phi = AST__BAD;
*theta = AST__BAD;
/* Check the inherited status. */
if( !astOK ) return;
/* If this is not a TPN projection get he value of the required
projection parameters (the default values for these are equal to the
fixed native shperical coordinates at the projection reference point). */
if( astGetWcsType( map ) != AST__TPN ) {
axlon = astGetWcsAxis( map, 0 );
if( astGetPV( map, axlon, 0 ) != 0.0 ) {
*phi = AST__DD2R*astGetPV( map, axlon, 1 );
*theta = AST__DD2R*astGetPV( map, axlon, 2 );
} else {
*phi = astGetNatLon( map );
*theta = astGetNatLat( map );
}
/* If this is a TPN projection, the returned values are always the fixed
native shperical coordinates at the projection reference point). */
} else {
*phi = astGetNatLon( map );
*theta = astGetNatLat( map );
}
}
static void GetFiducialPPC( AstWcsMap *map, double *x0, double *y0, int *status ){
/*
* Name:
* GetFiducialPPC
* Purpose:
* Return the IWC at the fiducial point of a WcsMap projection.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void GetFiducialPPC( AstWcsMap *map, double *x0, double *y0, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function returns the projection plane coords corresponding to
* the native spherical coords of the fiducial point of a FITS-WCS
* header. Note, projection plane coordinates (PPC) are equal to
* Intermediate World Coordinates (IWC) except for cases where the
* fiducial point does not correspond to the projection reference point.
* In these cases, IWC and PPC will be connected by a translation
* which ensures that the fiducial point corresponds to the origin of
* IWC.
*
* The values of parameters 1 and 2 on the longitude axis of
* the WcsMap are used as the native spherical coordinates of the
* fiducial point. The default values for these parameters are equal
* to the native spherical coordinates of the projection reference point.
* Parameters:
* map
* Pointer to the WcsMap.
* x0
* Address of a location at which to return the PPC X axis value at
* the fiducial point (radians).
* y0
* Address of a location at which to return the PPC Y axis value at
* the fiducial point (radians).
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
AstPointSet *pset1; /* Pointer to the native spherical PointSet */
AstPointSet *pset2; /* Pointer to the intermediate world PointSet */
double **ptr1; /* Pointer to pset1 data */
double **ptr2; /* Pointer to pset2 data */
int axlat; /* Index of latitude axis */
int axlon; /* Index of longitude axis */
int i; /* Loop count */
int naxes; /* Number of axes */
/* Initialise */
*x0 = AST__BAD;
*y0 = AST__BAD;
/* Check the inherited status. */
if( !astOK ) return;
/* Save number of axes in the WcsMap. */
naxes = astGetNin( map );
/* Allocate resources. */
pset1 = astPointSet( 1, naxes, "", status );
ptr1 = astGetPoints( pset1 );
pset2 = astPointSet( 1, naxes, "", status );
ptr2 = astGetPoints( pset2 );
/* Check pointers can be used safely. */
if( astOK ) {
/* Get the indices of the longitude and latitude axes in WcsMap. */
axlon = astGetWcsAxis( map, 0 );
axlat = astGetWcsAxis( map, 1 );
/* Use zero on all non-celestial axes. */
for( i = 0; i < naxes; i++ ) ptr1[ i ][ 0 ] = 0.0;
/* Get the native spherical coords at the fiducial point. */
GetFiducialNSC( map, ptr1[ axlon ], ptr1[ axlat ], status );
/* Use the inverse WcsMap to convert the native longitude and latitude of
the fiducial point into PPC (x,y). */
(void) astTransform( map, pset1, 0, pset2 );
/* Return the calculated PPC coords. */
*x0 = ptr2[ axlon ][ 0 ];
*y0 = ptr2[ axlat ][ 0 ];
}
/* Free resources. */
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
}
static int GetFiducialWCS( AstWcsMap *wcsmap, AstMapping *map2, int colon,
int colat, double *fidlon, double *fidlat, int *status ){
/*
* Name:
* GetFiducialWCS
* Purpose:
* Decide on the celestial coordinates of the fiducial point.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int GetFiducialWCS( AstWcsMap wcsmap, AstMapping map2, int colon,
* int colat, double *fidlon, double *fidlat, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function returns the celestial longitude and latitude values
* to use for the fiducial point. These are the values stored in FITS
* keywords CRVALi.
* Parameters:
* wcsmap
* The WcsMap which converts Projection Plane Coordinates into
* native spherical coordinates. The number of outputs from this
* Mapping should match the number of inputs to "map2".
* map2
* The Mapping which converts native spherical coordinates into WCS
* coordinates.
* colon
* The index of the celestial longitude output from "map2".
* colat
* The index of the celestial latitude output from "map2".
* fidlon
* Pointer to a location at which to return the celestial longitude
* value at the fiducial point. The value is returned in radians.
* fidlat
* Pointer to a location at which to return the celestial latitude
* value at the fiducial point. The value is returned in radians.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Zero if the fiducial point longitude or latitude could not be
* determined. One otherwise.
*/
/* Local Variables: */
AstPointSet *pset1; /* Pointer to the native spherical PointSet */
AstPointSet *pset2; /* Pointer to the WCS PointSet */
double **ptr1; /* Pointer to pset1 data */
double **ptr2; /* Pointer to pset2 data */
int axlat; /* Index of latitude axis */
int axlon; /* Index of longitude axis */
int iax; /* Axis index */
int naxin; /* Number of IWC axes */
int naxout; /* Number of WCS axes */
int ret; /* The returned FrameSet */
/* Initialise */
ret = 0;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Allocate resources. */
naxin = astGetNin( map2 );
naxout = astGetNout( map2 );
pset1 = astPointSet( 1, naxin, "", status );
ptr1 = astGetPoints( pset1 );
pset2 = astPointSet( 1, naxout, "", status );
ptr2 = astGetPoints( pset2 );
if( astOK ) {
/* Get the indices of the latitude and longitude outputs in the WcsMap.
These are not necessarily the same as "colat" and "colon" because "map2"
may contain a PermMap. */
axlon = astGetWcsAxis( wcsmap, 0 );
axlat = astGetWcsAxis( wcsmap, 1 );
/* Use zero on all non-celestial axes. */
for( iax = 0; iax < naxin; iax++ ) ptr1[ iax ][ 0 ] = 0.0;
/* Get the native spherical coords at the fiducial point. */
GetFiducialNSC( wcsmap, ptr1[ axlon ], ptr1[ axlat ], status );
/* The fiducial point in the celestial coordinate system is found by
transforming the fiducial point in native spherical co-ordinates
into absolute physical coordinates using map2. */
(void) astTransform( map2, pset1, 1, pset2 );
/* Store the returned WCS values. */
*fidlon = ptr2[ colon ][ 0 ];
*fidlat = ptr2[ colat ][ 0 ];
/* Indicate if we have been succesfull. */
if( astOK && *fidlon != AST__BAD && *fidlat != AST__BAD ) ret = 1;
}
/* Free resources. */
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
/* Return the result. */
return ret;
}
static const char *GetFitsSor( const char *string, int *status ) {
/*
* Name:
* GetFitsSor
* Purpose:
* Get the string used to represent an AST spectral standard of rest.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* const char *GetFitsSor( const char *string, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function returns a pointer to a static string which is the
* FITS equivalent to a given SpecFrame StdOfRest value.
* Parameters:
* string
* Pointer to a constant null-terminated string containing the
* SpecFrame StdOfRest value.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to a static null-terminated string containing the FITS
* equivalent to the supplied string. NULL is returned if the supplied
* string has no FITS equivalent.
* Notes:
* - A NULL pointer value will be returned if this function is
* invoked wth the global error status set, or if it should fail
* for any reason.
*/
/* Local Variables: */
const char *result; /* Pointer value to return */
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Compare the supplied string with SpecFrame value for which there is a
known FITS equivalent. */
if( !strcmp( string, "Topocentric" ) ){
result = "TOPOCENT";
} else if( !strcmp( string, "Geocentric" )){
result = "GEOCENTR";
} else if( !strcmp( string, "Barycentric" )){
result = "BARYCENT";
} else if( !strcmp( string, "Heliocentric" )){
result = "HELIOCEN";
} else if( !strcmp( string, "LSRK" )){
result = "LSRK";
} else if( !strcmp( string, "LSRD" )){
result = "LSRD";
} else if( !strcmp( string, "Galactic" )){
result = "GALACTOC";
} else if( !strcmp( string, "Local_group" )){
result = "LOCALGRP";
} else if( !strcmp( string, "Source" )){
result = "SOURCE";
} else {
result = NULL;
}
/* Return the answer. */
return result;
}
static double GetItem( double ****item, int i, int jm, char s, char *name,
const char *method, const char *class, int *status ){
/*
* Name:
* GetItem
* Purpose:
* Retrieve a value for a axis keyword value from a FitStore structure.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* double GetItem( double ****item, int i, int jm, char s, char *name,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* The requested keyword value is retrieved from the specified array,
* at a position indicated by the axis and co-ordinate version.
* AST__BAD is returned if the array does not contain the requested
* value.
* Parameters:
* item
* The address of the pointer within the FitsStore which locates the
* arrays of values for the required keyword (eg &(store->crval) ).
* The array located by the supplied pointer contains a vector of
* pointers. Each of these pointers is associated with a particular
* co-ordinate version (s), and locates an array of pointers for that
* co-ordinate version. Each such array of pointers has an element
* for each intermediate axis number (i), and the pointer locates an
* array of axis keyword values. These arrays of keyword values have
* one element for every pixel axis (j) or projection parameter (m).
* i
* The zero based intermediate axis index in the range 0 to 98. Set
* this to zero for keywords (e.g. CRPIX) which are not indexed by
* intermediate axis number.
* jm
* The zero based pixel axis index (in the range 0 to 98) or parameter
* index (in the range 0 to WCSLIB_MXPAR-1). Set this to zero for
* keywords (e.g. CRVAL) which are not indexed by either pixel axis or
* parameter number.
* s
* The co-ordinate version character (A to Z, or space), case
* insensitive
* name
* A string holding a name for the item of information. A NULL
* pointer may be supplied, in which case it is ignored. If a
* non-NULL pointer is supplied, an error is reported if the item
* of information has not been stored, and the supplied name is
* used to identify the information within the error message.
* 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 required keyword value, or AST__BAD if no value has previously
* been stored for the keyword (or if an error has occurred).
*/
/* Local Variables: */
double ret; /* Returned keyword value */
int si; /* Integer co-ordinate version index */
/* Initialise */
ret = AST__BAD;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Convert the character co-ordinate version into an integer index, and
check it is within range. The primary axis description (s=' ') is
given index zero. 'A' is 1, 'B' is 2, etc. */
if( s == ' ' ) {
si = 0;
} else if( islower(s) ){
si = (int) ( s - 'a' ) + 1;
} else {
si = (int) ( s - 'A' ) + 1;
}
if( si < 0 || si > 26 ) {
astError( AST__INTER, "GetItem(fitschan): AST internal error; "
"co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s );
/* Check the intermediate axis index is within range. */
} else if( i < 0 || i > 98 ) {
astError( AST__INTER, "GetItem(fitschan): AST internal error; "
"intermediate axis index %d is invalid.", status, i );
/* Check the pixel axis or parameter index is within range. */
} else if( jm < 0 || jm > 99 ) {
astError( AST__INTER, "GetItem(fitschan): AST internal error; "
"pixel axis or parameter index %d is invalid.", status, jm );
/* Otherwise, if the array holding the required keyword is not null,
proceed... */
} else if( *item ){
/* Find the number of coordinate versions in the supplied array.
Only proceed if it encompasses the requested co-ordinate
version. */
if( astSizeOf( (void *) *item )/sizeof(double **) > si ){
/* Find the number of intermediate axes in the supplied array.
Only proceed if it encompasses the requested intermediate axis. */
if( astSizeOf( (void *) (*item)[si] )/sizeof(double *) > i ){
/* Find the number of pixel axes or parameters in the supplied array.
Only proceed if it encompasses the requested index. */
if( astSizeOf( (void *) (*item)[si][i] )/sizeof(double) > jm ){
/* Return the required keyword value. */
ret = (*item)[si][i][jm];
}
}
}
}
/* If required, report an error if the requested item of information has
not been stored. */
if( ret == AST__BAD && name && astOK ){
astError( AST__NOFTS, "%s(%s): No value can be found for %s.", status,
method, class, name );
}
return ret;
}
static int GetMaxJM( double ****item, char s, int *status ){
/*
* Name:
* GetMaxJM
* Purpose:
* Return the largest pixel axis or parameter index stored for an
* numerical axis keyword value in a FitStore structure.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int GetMaxJM( double ****item, char s, int *status)
* Class Membership:
* FitsChan member function.
* Description:
* The number of pixel axis numbers or projection parameters stored for
* a specified axis keyword is found and returned.
* Parameters:
* item
* The address of the pointer within the FitsStore which locates the
* arrays of values for the required keyword (eg &(store->crpix) ).
* The array located by the supplied pointer contains a vector of
* pointers. Each of these pointers is associated with a particular
* co-ordinate version (s), and locates an array of pointers for that
* co-ordinate version. Each such array of pointers has an element
* for each intermediate axis number (i), and the pointer locates an
* array of axis keyword values. These arrays of keyword values have
* one element for every pixel axis (j) or projection parameter (m).
* s
* The co-ordinate version character (A to Z, or space), case
* insensitive
* status
* Pointer to the inherited status variable.
* Returned Value:
* The maximum pixel axis number or projection parameter index (zero
* based).
*/
/* Local Variables: */
int jm; /* Number of parameters/pixel axes */
int i; /* Intermediate axis index */
int ret; /* Returned axis index */
int si; /* Integer co-ordinate version index */
/* Initialise */
ret = -1;
/* Check the inherited status. */
if( !astOK ) return ret;
/* If the array holding the required keyword is not null, proceed... */
if( *item ){
/* Convert the character co-ordinate version into an integer index, and
check it is within range. The primary axis description (s=' ') is
given index zero. 'A' is 1, 'B' is 2, etc. */
if( s == ' ' ) {
si = 0;
} else if( islower(s) ){
si = (int) ( s - 'a' ) + 1;
} else {
si = (int) ( s - 'A' ) + 1;
}
if( si < 0 || si > 26 ) {
astError( AST__INTER, "GetMaxJM(fitschan): AST internal error; "
"co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s );
return ret;
}
/* Find the number of coordinate versions in the supplied array.
Only proceed if it encompasses the requested co-ordinate
version. */
if( astSizeOf( (void *) *item )/sizeof(double **) > si ){
/* Check that the pointer to the array of intermediate axis values is not null. */
if( (*item)[si] ){
/* Loop round each used element in this array. */
for( i = 0; i < astSizeOf( (void *) (*item)[si] )/sizeof(double *);
i++ ){
if( (*item)[si][i] ){
/* Get the size of the pixel axis/projection parameter array for the
current intermediate axis, and subtract 1 to get the largest index. */
jm = astSizeOf( (void *) (*item)[si][i] )/sizeof(double) - 1;
/* Ignore any trailing unused (AST__BAD) values. */
while( jm >= 0 && (*item)[si][i][jm] == AST__BAD ) jm--;
/* Update the returned value if the current value is larger. */
if( jm > ret ) ret = jm;
}
}
}
}
}
return ret;
}
static int GetMaxJMC( char *****item, char s, int *status ){
/*
* Name:
* GetMaxJMC
* Purpose:
* Return the largest pixel axis or parameter index stored for an
* character-valued axis keyword value in a FitStore structure.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int GetMaxJMC( char *****item, char s, int *status)
* Class Membership:
* FitsChan member function.
* Description:
* The number of pixel axis numbers or projection parameters stored for
* a specified axis keyword is found and returned.
* Parameters:
* item
* The address of the pointer within the FitsStore which locates the
* arrays of values for the required keyword (eg &(store->ctype) ).
* The array located by the supplied pointer contains a vector of
* pointers. Each of these pointers is associated with a particular
* co-ordinate version (s), and locates an array of pointers for that
* co-ordinate version. Each such array of pointers has an element
* for each intermediate axis number (i), and the pointer locates an
* array of axis keyword string pointers. These arrays of keyword
* string pointers have one element for every pixel axis (j) or
* projection parameter (m).
* s
* The co-ordinate version character (A to Z, or space), case
* insensitive
* status
* Pointer to the inherited status variable.
* Returned Value:
* The maximum pixel axis number or projection parameter index (zero
* based).
*/
/* Local Variables: */
int jm; /* Number of parameters/pixel axes */
int i; /* Intermediate axis index */
int ret; /* Returned axis index */
int si; /* Integer co-ordinate version index */
/* Initialise */
ret = -1;
/* Check the inherited status. */
if( !astOK ) return ret;
/* If the array holding the required keyword is not null, proceed... */
if( *item ){
/* Convert the character co-ordinate version into an integer index, and
check it is within range. The primary axis description (s=' ') is
given index zero. 'A' is 1, 'B' is 2, etc. */
if( s == ' ' ) {
si = 0;
} else if( islower(s) ){
si = (int) ( s - 'a' ) + 1;
} else {
si = (int) ( s - 'A' ) + 1;
}
if( si < 0 || si > 26 ) {
astError( AST__INTER, "GetMaxJMC(fitschan): AST internal error; "
"co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s );
return ret;
}
/* Find the number of coordinate versions in the supplied array.
Only proceed if it encompasses the requested co-ordinate
version. */
if( astSizeOf( (void *) *item )/sizeof(char ***) > si ){
/* Check that the pointer to the array of intermediate axis values is not null. */
if( (*item)[si] ){
/* Loop round each used element in this array. */
for( i = 0; i < astSizeOf( (void *) (*item)[si] )/sizeof(char **);
i++ ){
if( (*item)[si][i] ){
/* Get the size of the pixel axis/projection parameter array for the
current intermediate axis, and subtract 1 to get the largest index. */
jm = astSizeOf( (void *) (*item)[si][i] )/sizeof(char *) - 1;
/* Ignore any trailing unused (NULL) values. */
while( jm >= 0 && (*item)[si][i][jm] == NULL ) jm--;
/* Update the returned value if the current value is larger. */
if( jm > ret ) ret = jm;
}
}
}
}
}
return ret;
}
static int GetMaxI( double ****item, char s, int *status ){
/*
* Name:
* GetMaxI
* Purpose:
* Return the largest WCS axis index stored for an axis keyword value in
* a FitStore structure.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int GetMaxJM( double ****item, char s)
* Class Membership:
* FitsChan member function.
* Description:
* The number of Wcs axis numbers stored for a specified axis keyword is
* found and returned.
* Parameters:
* item
* The address of the pointer within the FitsStore which locates the
* arrays of values for the required keyword (eg &(store->crval) ).
* The array located by the supplied pointer contains a vector of
* pointers. Each of these pointers is associated with a particular
* co-ordinate version (s), and locates an array of pointers for that
* co-ordinate version. Each such array of pointers has an element
* for each intermediate axis number (i), and the pointer locates an
* array of axis keyword values. These arrays of keyword values have
* one element for every pixel axis (j) or projection parameter (m).
* s
* The co-ordinate version character (A to Z, or space), case
* insensitive
* Returned Value:
* The maximum WCS axis index (zero based).
*/
/* Local Variables: */
int ret; /* Returned axis index */
int si; /* Integer co-ordinate version index */
/* Initialise */
ret = -1;
/* Check the inherited status. */
if( !astOK ) return ret;
/* If the array holding the required keyword is not null, proceed... */
if( *item ){
/* Convert the character co-ordinate version into an integer index, and
check it is within range. The primary axis description (s=' ') is
given index zero. 'A' is 1, 'B' is 2, etc. */
if( s == ' ' ) {
si = 0;
} else if( islower(s) ){
si = (int) ( s - 'a' ) + 1;
} else {
si = (int) ( s - 'A' ) + 1;
}
if( si < 0 || si > 26 ) {
astError( AST__INTER, "GetMaxI(fitschan): AST internal error; "
"co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s );
return ret;
}
/* Find the number of coordinate versions in the supplied array.
Only proceed if it encompasses the requested co-ordinate
version. */
if( astSizeOf( (void *) *item )/sizeof(double **) > si ){
/* Check that the pointer to the array of intermediate axis values is not null. */
if( (*item)[si] ){
/* Get the size of the intermediate axis array and subtract 1 to get the largest
index. */
ret = astSizeOf( (void *) (*item)[si] )/sizeof(double *) - 1;
/* Ignore any trailing unused (NULL) values. */
while( ret >= 0 && (*item)[si][ret] == NULL ) ret--;
}
}
}
return ret;
}
static char GetMaxS( double ****item, int *status ){
/*
* Name:
* GetMaxS
* Purpose:
* Return the largest (i.e. closest to Z) coordinate version character
* stored for a axis keyword value in a FitStore structure.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* char GetMaxS( double ****item, int *status)
* Class Membership:
* FitsChan member function.
* Description:
* The largest (i.e. closest to Z) coordinate version character
* stored for a axis keyword value in a FitStore structure is found
* and returned.
* Parameters:
* item
* The address of the pointer within the FitsStore which locates the
* arrays of values for the required keyword (eg &(store->crval) ).
* The array located by the supplied pointer contains a vector of
* pointers. Each of these pointers is associated with a particular
* co-ordinate version (s), and locates an array of pointers for that
* co-ordinate version. Each such array of pointers has an element
* for each intermediate axis number (i), and the pointer locates an
* array of axis keyword values. These arrays of keyword values have
* one element for every pixel axis (j) or projection parameter (m).
* status
* Pointer to the inherited status variable.
* Returned Value:
* The highest coordinate version character.
*/
/* Local Variables: */
char ret; /* Returned axis index */
int si; /* Integer index into alphabet */
/* Initialise */
ret = ' ';
/* Check the inherited status. */
if( !astOK ) return ret;
/* If the array holding the required keyword is not null, proceed... */
if( *item ){
/* Find the length of this array, and subtract 1 to get the largest index
in the array. */
si = astSizeOf( (void *) *item )/sizeof(double **) - 1;
/* Ignore any trailing null (i.e. unused) values. */
while( si >= 0 && !(*item)[si] ) si--;
/* Store the corresponding character */
if( si == 0 ) {
ret = ' ';
} else {
ret = 'A' + si - 1;
}
}
return ret;
}
static char *GetItemC( char *****item, int i, int jm, char s, char *name,
const char *method, const char *class, int *status ){
/*
* Name:
* GetItemC
* Purpose:
* Retrieve a string value for a axis keyword value from a FitStore
* structure.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* char *GetItemC( char *****item, int i, int jm, char s, char *name,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* The requested keyword string value is retrieved from the specified
* array, at a position indicated by the axis and co-ordinate version.
* NULL is returned if the array does not contain the requested
* value.
* Parameters:
* item
* The address of the pointer within the FitsStore which locates the
* arrays of values for the required keyword (eg &(store->ctype) ).
* The array located by the supplied pointer contains a vector of
* pointers. Each of these pointers is associated with a particular
* co-ordinate version (s), and locates an array of pointers for that
* co-ordinate version. Each such array of pointers has an element
* for each intermediate axis number (i), and the pointer locates an
* array of axis keyword string pointers. These arrays of keyword
* string pointers have one element for every pixel axis (j) or
* projection parameter (m).
* i
* The zero based intermediate axis index in the range 0 to 98. Set
* this to zero for keywords (e.g. CRPIX) which are not indexed by
* intermediate axis number.
* jm
* The zero based pixel axis index (in the range 0 to 98) or parameter
* index (in the range 0 to WCSLIB__MXPAR-1). Set this to zero for
* keywords (e.g. CTYPE) which are not indexed by either pixel axis or
* parameter number.
* s
* The co-ordinate version character (A to Z, or space), case
* insensitive
* name
* A string holding a name for the item of information. A NULL
* pointer may be supplied, in which case it is ignored. If a
* non-NULL pointer is supplied, an error is reported if the item
* of information has not been stored, and the supplied name is
* used to identify the information within the error message.
* 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:
* A pointer to the required keyword string value, or NULL if no value
* has previously been stored for the keyword (or if an error has
* occurred).
*/
/* Local Variables: */
char *ret; /* Returned keyword value */
int si; /* Integer co-ordinate version index */
/* Initialise */
ret = NULL;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Convert the character co-ordinate version into an integer index, and
check it is within range. The primary axis description (s=' ') is
given index zero. 'A' is 1, 'B' is 2, etc. */
if( s == ' ' ) {
si = 0;
} else if( islower(s) ){
si = (int) ( s - 'a' ) + 1;
} else {
si = (int) ( s - 'A' ) + 1;
}
if( si < 0 || si > 26 ) {
astError( AST__INTER, "GetItemC(fitschan): AST internal error; "
"co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s );
/* Check the intermediate axis index is within range. */
} else if( i < 0 || i > 98 ) {
astError( AST__INTER, "GetItemC(fitschan): AST internal error; "
"intermediate axis index %d is invalid.", status, i );
/* Check the pixel axis or parameter index is within range. */
} else if( jm < 0 || jm > 99 ) {
astError( AST__INTER, "GetItem(fitschan): AST internal error; "
"pixel axis or parameter index %d is invalid.", status, jm );
/* Otherwise, if the array holding the required keyword is not null,
proceed... */
} else if( *item ){
/* Find the number of coordinate versions in the supplied array.
Only proceed if it encompasses the requested co-ordinate
version. */
if( astSizeOf( (void *) *item )/sizeof(char ***) > si ){
/* Find the number of intermediate axes in the supplied array.
Only proceed if it encompasses the requested intermediate axis. */
if( astSizeOf( (void *) (*item)[si] )/sizeof(char **) > i ){
/* Find the number of pixel axes or parameters in the supplied array.
Only proceed if it encompasses the requested index. */
if( astSizeOf( (void *) (*item)[si][i] )/sizeof(char *) > jm ){
/* Return the required keyword value. */
ret = (*item)[si][i][jm];
}
}
}
}
/* If required, report an error if the requested item of information has
not been stored. */
if( !ret && name && astOK ){
astError( AST__NOFTS, "%s(%s): No value can be found for %s.", status,
method, class, name );
}
return ret;
}
static AstFitsTable *GetNamedTable( AstFitsChan *this, const char *extname,
int extver, int extlevel, int report,
const char *method, int *status ){
/*
* Name:
* GetNamedTable
* Purpose:
* Return a FitsTable holding the contents of a named FITS binary table.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstFitsTable *GetNamedTable( AstFitsChan *this, const char *extname,
* int extver, int extlevel, int report,
* const char *method, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* If a table source function has been registered with FitsChan (using
* astTableSource), invoke it to read the required table from the external
* FITS file. If the extension is available in the FITS file, this will
* put a FitsTable into the "tables" KeyMap in the FitsChan structure,
* using the FITS extension name as the key - this will replace any
* FitsTable already present in the KeyMap with the same key. Finally,
* return a pointer to the FitsTable stored in the KeyMap - if any.
*
* This strategy allows the astPutTables or astPutTable method to be used
* as an alternative to registering a table source function with the
* FitsChan. Note, any table read using the source function is used
* in preference to any table stored in the FitsChan by an earlier call
* to astPutTables/astPutTable.
* Parameters:
* this
* Pointer to the FitsChan.
* extname
* The key associated with the required table - should be the name
* of the FITS extension containing the binary table.
* extver
* The FITS "EXTVER" value for the required table.
* extlevel
* The FITS "EXTLEVEL" value for the required table.
* report
* If non-zero, report an error if the named table is not available.
* method
* Pointer to a string holding the name of the calling method.
* This is only for use in constructing error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the FitsTable, or NULL if the table is not avalable.
*/
/* Local Variables: */
AstFitsTable *ret;
/* Initialise */
ret = NULL;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Fitrst attempt to read the required table from the external FITS file.
Only proceed if table source function and wrapper have been supplied
using astTableSource. */
if( this->tabsource && this->tabsource_wrap ){
/* Invoke the table source function asking it to place the required FITS
table in the FitsChan. This is an externally supplied function which may
not be thread-safe, so lock a mutex first. Note, a cloned FitsChan pointer
is sent to the table source function since the table source function will
annul the supplied FitsChan pointer. Also store the channel data
pointer in a global variable so that it can be accessed in the source
function using macro astChannelData. */
astStoreChannelData( this );
LOCK_MUTEX2;
( *this->tabsource_wrap )( this->tabsource, astClone( this ), extname,
extver, extlevel, status );
UNLOCK_MUTEX2;
}
/* Now get a pointer to the required FitsTable, stored as an entry in the
"tables" KeyMap. Report an error if required. */
if( ! (this->tables) || !astMapGet0A( this->tables, extname, &ret ) ){
if( report && astOK ) {
astError( AST__NOTAB, "%s(%s): Failed to read FITS binary table "
"from extension '%s' (extver=%d, extlevel=%d).", status,
method, astGetClass( this ), extname, extver, extlevel );
}
}
/* Return the result. */
return ret;
}
static AstKeyMap *GetTables( AstFitsChan *this, int *status ) {
/*
*++
* Name:
c astGetTables
f AST_GETTABLES
* Purpose:
* Retrieve any FitsTables currently in a FitsChan.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c AstKeyMap *astGetTables( AstFitsChan *this )
f RESULT = AST_GETTABLES( THIS, STATUS )
* Class Membership:
* FitsChan method.
* Description:
* If the supplied FitsChan currently contains any tables, then this
* function returns a pointer to a KeyMap. Each entry in the KeyMap
* is a pointer to a FitsTable holding the data for a FITS binary
* table. The key used to access each entry is the FITS extension
* name in which the table should be stored.
*
* Tables can be present in a FitsChan as a result either of using the
c astPutTable (or astPutTables)
f AST_PUTTABLE (or AST_PUTTABLES)
* method to store existing tables in the FitsChan, or of using the
c astWrite
f AST_WRITE
* method to write a FrameSet to the FitsChan. For the later case, if
* the FitsChan "TabOK" attribute is positive and the FrameSet requires
* a look-up table to describe one or more axes, then the "-TAB"
* algorithm code described in FITS-WCS paper III is used and the table
* values are stored in the FitsChan in the form of a FitsTable object
* (see the documentation for the "TabOK" attribute).
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Returned Value:
c astGetTables()
f AST_GETTABLES = INTEGER
* A pointer to a deep copy of the KeyMap holding the tables currently
* in the FitsChan, or
c NULL
f AST__NULL
* if the FitsChan does not contain any tables. The returned
* pointer should be annulled using
c astAnnul
f AST_ANNUL
* when no longer needed.
* Notes:
* - A null Object pointer (AST__NULL) will be returned if this
c function is invoked with the AST error status set, or if it
f function is invoked with STATUS set to an error value, or if it
* should fail for any reason.
*--
*/
/* Local Variables: */
AstKeyMap *result; /* Pointer value to return */
/* Initialise. */
result = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* If the FitsChan contains any tables, return a pointer to a copy of
the KeyMap containing them. Otherwise, return a NULL pointer. */
if( this->tables && astMapSize( this->tables ) > 0 ) {
result = astCopy( this->tables );
}
/* Return the result. */
return result;
}
static int GetUsedPolyTan( AstFitsChan *this, AstFitsChan *out, int latax,
int lonax, char s, const char *method,
const char *class, int *status ){
/*
* Name:
* GetUsedPolyTan
* Purpose:
* Get the value to use for the PolyTan attribute.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int GetUsedPolyTan( AstFitsChan *this, AstFitsChan *out, int latax,
* int lonax, char s, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* If the PolyTan attribute is zero or positive, then its value is
* returned. If it is negative, the supplied FitsChan is searched for
* keywords of the form PVi_m. If any are found on the latitude axis,
* or if any are found on the longitude axis with "m" > 4, +1 is
* returned (meaning "use the distorted TAN conventio"). Otherwise 0
* is returned (meaning "use the standard TAN convention").
*
* If all the PVi_m values for m > 0 on either axis are zero, a warning is
* issued and zero is returned.
* Parameters:
* this
* Pointer to the FitsChan.
* out
* Pointer to a secondary FitsChan. If the PV values in "this" are
* found to be unusable, they will be marked as used in both "this"
* and "out".
* latax
* The one-based index of the latitude axis within the FITS header.
* lonax
* The one-based index of the longitude axis within the FITS header.
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The attribute value to use.
* Notes:
* - A value of zero is returned if an error has already occurred
* or if an error occurs for any reason within this function.
*/
/* Local Variables... */
char template[ 20 ];
double pval;
int lbnd_lat;
int lbnd_lon;
int m;
int nfound1;
int nfound2;
int ok;
int ret;
int ubnd_lat;
int ubnd_lon;
/* Check the global status. */
if( !astOK ) return 0;
/* Get the value of the PolyTan attribute. */
ret = astGetPolyTan( this );
/* If it is negative, we examine the FitsChan to see which convention to
use. */
if( ret < 0 ) {
ret = 0;
/* Search the FitsChan for latitude PV cards. */
if( s != ' ' ) {
sprintf( template, "PV%d_%%d%c", latax, s );
} else {
sprintf( template, "PV%d_%%d", latax );
}
nfound1 = astKeyFields( this, template, 1, &ubnd_lat, &lbnd_lat );
/* Search the FitsChan for longitude PV cards. */
if( s != ' ' ) {
sprintf( template, "PV%d_%%d%c", lonax, s );
} else {
sprintf( template, "PV%d_%%d", lonax );
}
nfound2 = astKeyFields( this, template, 1, &ubnd_lon, &lbnd_lon );
/* If any were found with "m" value greater than 4, assume the distorted
TAN convention is in use. Otherwise assume the stdanrd TAN convention is
in use. */
if( nfound1 || ( nfound2 && ubnd_lon > 4 ) ) ret = 1;
/* If the distorted TAN convention is to be used, check that at least one
of the PVi_m values is non-zero on each axis. We ignore the PVi_0
(constant) terms in this check. */
if( ret > 0 ) {
/* Do the latitude axis first, skipping the first (constant) term. Assume
that all latitude pV values are zero until we find one that is not. */
ok = 0;
for( m = 1; m <= ubnd_lat && !ok; m++ ) {
/* Form the PVi_m keyword name. */
if( s != ' ' ) {
sprintf( template, "PV%d_%d%c", latax, m, s );
} else {
sprintf( template, "PV%d_%d", latax, m );
}
/* Get it's value. */
if( ! GetValue( this, template, AST__FLOAT, &pval, 0, 0,
method, class, status ) ) {
/* If the PVi_m header is not present in the FitsChan, use a default value. */
pval = ( m == 1 ) ? 1.0 : 0.0;
}
/* If the PVi_m header has a non-zero value, we can leave the loop. */
if( pval != 0.0 ) ok = 1;
}
/* If all the latitude PVi_m values are zero, issue a warning and return
zero, indicating that a simple undistorted TAN projection should be used. */
if( !ok ) {
Warn( this, "badpv", "This FITS header describes a distorted TAN "
"projection, but all the distortion coefficients (the "
"PVi_m headers) on the latitude axis are zero.", method,
class, status );
ret = 0;
/* Also, delete the PV keywords so that no attempt is made to use them. */
for( m = 1; m <= ubnd_lat; m++ ) {
if( s != ' ' ) {
sprintf( template, "PV%d_%d%c", latax, m, s );
} else {
sprintf( template, "PV%d_%d", latax, m );
}
astClearCard( this );
if( FindKeyCard( this, template, method, class, status ) ) {
DeleteCard( this, method, class, status );
}
}
/* Otherwise, do the same check for the longitude axis. */
} else {
ok = 0;
for( m = 1; m <= ubnd_lon && !ok; m++ ) {
if( s != ' ' ) {
sprintf( template, "PV%d_%d%c", lonax, m, s );
} else {
sprintf( template, "PV%d_%d", lonax, m );
}
if( ! GetValue( this, template, AST__FLOAT, &pval, 0, 0,
method, class, status ) ) {
pval = ( m == 1 ) ? 1.0 : 0.0;
}
if( pval != 0.0 ) ok = 1;
}
if( !ok ) {
Warn( this, "badpv", "This FITS header describes a distorted TAN "
"projection, but all the distortion coefficients (the "
"PVi_m headers) on the longitude axis are zero.", method,
class, status );
ret = 0;
for( m = 1; m <= ubnd_lon; m++ ) {
if( s != ' ' ) {
sprintf( template, "PV%d_%d%c", lonax, m, s );
} else {
sprintf( template, "PV%d_%d", lonax, m );
}
astClearCard( this );
if( FindKeyCard( this, template, method, class, status ) ) {
DeleteCard( this, method, class, status );
}
}
}
}
}
}
/* Return the result. */
return astOK ? ret : 0;
}
static int GoodWarns( const char *value, int *status ){
/*
* Name:
* GoodWarns
* Purpose:
* Checks a string to ensure it is a legal list of warning conditions.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int GoodWarns( const char *value, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function checks the supplied string to ensure it contains a space
* separated list of zero or more recognised warning conditions. An
* error is reported if it does not.
* Parameters:
* value
* The string to check.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Zero is returned if the supplied string is not a legal list of
* conditions, or if an error has already occurred. One is returned
* otherwise.
*/
/* Local Variables: */
char *b; /* Pointer to next buffer element */
const char *c ; /* Pointer to next character */
char buf[100]; /* Buffer for condition name */
int inword; /* Are we in a word? */
int n; /* Number of conditions supplied */
int ret; /* Returned value */
/* Initialise */
ret = 0;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Report an error and return if the pointer is null. */
if( !value ){
astError( AST__ATTIN, "astSetWarnings(fitschan): Null pointer "
"supplied for the Warnings attribute." , status);
return ret;
}
/* Initialise things */
inword = 0;
buf[ 0 ] = ' ';
b = buf + 1;
n = 0;
ret = 1;
/* Loop round each character in the supplied string. */
for( c = value ; c < value + strlen( value ) + 1; c++ ){
/* Have we found the first space or null following a word? */
if( ( !(*c) || isspace( *c ) ) && inword ){
/* Add a space to the end of the buffer and terminate it. */
*(b++) = ' ';
*b = 0;
/* Check the word is legal by searching for it in the string of all
conditions, which should be lower case and have spaces at start and end.
The word in the buffer is delimited by spaces and so it will not match
a substring within a condition. If it is legal increment the number of
conditions found. */
if( strstr( ALLWARNINGS, buf ) ){
n++;
/* Otherwise, report an error and break. */
} else {
ret = 0;
*(--b) = 0;
astError( AST__ATTIN, "astSetWarnings(fitschan): Unknown "
"condition '%s' specified when setting the Warnings "
"attribute.", status, buf + 1 );
break;
}
/* Reset the pointer to the next character in the buffer, retaining the
initial space in the buffer. */
b = buf + 1;
/* Indicate we are no longer in a word. */
inword = 0;
/* Have we found the first non-space, non-null character following a space? */
} else if( *c && !isspace( *c ) && !inword ){
/* Note we are now in a word. */
inword = 1;
}
/* If we are in a word, copy the lowercase character to the buffer. */
if( inword ) *(b++) = tolower( *c );
}
return ret;
}
static AstMapping *GrismSpecWcs( char *algcode, FitsStore *store, int i,
char s, AstSpecFrame *specfrm,
const char *method, const char *class, int *status ) {
/*
* Name:
* GrismSpecWcs
* Purpose:
* Create a Mapping describing a FITS-WCS grism-dispersion algorithm
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstMapping *GrismSpecWcs( char *algcode, FitsStore *store, int i, char s,
* AstSpecFrame *specfrm, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function uses the contents of the supplied FitsStore to create
* a Mapping which goes from Intermediate World Coordinate (known as "w"
* in the context of FITS-WCS paper III) to the spectral system
* described by the supplied SpecFrame.
*
* The returned Mapping implements the grism "GRA" and "GRI" algorithms
* described in FITS-WCS paper III.
* Parameters:
* algcode
* Pointer to a string holding the code for the required algorithm
* ("-GRA" or "-GRI").
* store
* Pointer to the FitsStore structure holding the values to use for
* the WCS keywords.
* i
* The zero-based index of the spectral axis within the FITS header
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* specfrm
* Pointer to the SpecFrame. This specifies the "S" system - the
* system in which the CRVAL kewyords (etc) are specified.
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to a Mapping, or NULL if an error occurs.
*/
/* Local Variables: */
AstFrameSet *fs;
AstMapping *gmap;
AstMapping *map1;
AstMapping *map2;
AstMapping *map2a;
AstMapping *map2b;
AstMapping *ret;
AstMapping *smap;
AstSpecFrame *wfrm;
double crv;
double dg;
double gcrv;
double pv;
double wcrv;
/* Check the global status. */
ret = NULL;
if( !astOK ) return ret;
/* The returned Mapping will be a CmpMap including a GrismMap. This
GrismMap will produced wavelength as output. We also need the Mapping
from wavelength to the system represented by the supplied SpecFrame.
To get this, we first create a copy of the supplied SpecFrame (in order
to inherit the standard of rest, epoch, etc), and set its System to
wavlength in vacuum (for "-GRI") or air (for "-GRA"), and then use
astConvert to get the Mapping from the SpecFrame system to relevant
form of wavelength. */
wfrm = astCopy( specfrm );
astSetSystem( wfrm, strcmp( algcode, "-GRI" )?AST__AIRWAVE:AST__WAVELEN );
astSetUnit( wfrm, 0, "m" );
fs = astConvert( specfrm, wfrm, "" );
if( fs ) {
smap = astGetMapping( fs, AST__BASE, AST__CURRENT );
fs = astAnnul( fs );
/* Get the CRVAL value for the spectral axis (this will be in the S system). */
crv = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
if( crv == AST__BAD ) crv = 0.0;
/* Convert it to the wavelength system (vacuum or air) in metres. */
astTran1( smap, 1, &crv, 1, &wcrv );
/* Create a GrismMap, and then use the projection parameters stored in
the FitsStore to set its attributes (convert degrees values to radians
and supply the defaults specified in FITS-WCS paper III). The FITS
paper specifies units in which these parameters should be stored in a
FITS header - distances are in metres and angles in degrees. */
gmap = (AstMapping *) astGrismMap( "", status );
pv = GetItem( &(store->pv), i, 0, s, NULL, method, class, status );
astSetGrismG( gmap, ( pv != AST__BAD )?pv:0.0 );
pv = GetItem( &(store->pv), i, 1, s, NULL, method, class, status );
astSetGrismM( gmap, ( pv != AST__BAD )?(int) ( pv + 0.5 ):0);
pv = GetItem( &(store->pv), i, 2, s, NULL, method, class, status );
astSetGrismAlpha( gmap, ( pv != AST__BAD )?pv*AST__DD2R:0.0 );
pv = GetItem( &(store->pv), i, 3, s, NULL, method, class, status );
astSetGrismNR( gmap, ( pv != AST__BAD )?pv:1.0 );
pv = GetItem( &(store->pv), i, 4, s, NULL, method, class, status );
astSetGrismNRP( gmap, ( pv != AST__BAD )?pv:0.0 );
pv = GetItem( &(store->pv), i, 5, s, NULL, method, class, status );
astSetGrismEps( gmap, ( pv != AST__BAD )?pv*AST__DD2R:0.0 );
pv = GetItem( &(store->pv), i, 6, s, NULL, method, class, status );
astSetGrismTheta( gmap, ( pv != AST__BAD )?pv*AST__DD2R:0.0 );
/* Store the reference wavelength found above as an attribute of the
GrismMap. */
astSetGrismWaveR( gmap, wcrv );
/* Invert the GrismMap to get the (Wavelength -> grism parameter) Mapping, and
then combine it with the (S -> Wavelength) Mapping to get the (S -> grism
parameter) Mapping. */
astInvert( gmap );
map1 = (AstMapping *) astCmpMap( smap, gmap, 1, "", status );
/* Convert the reference point value from wavelength to grism parameter. */
astTran1( gmap, 1, &wcrv, 1, &gcrv );
/* Find the rate of change of grism parameter with respect to the S
system at the reference point, dg/dS. */
dg = astRate( map1, &crv, 0, 0 );
if( dg != AST__BAD && dg != 0.0 ) {
/* FITS-WCS paper II requires headers to be constructed so that dS/dw = 1.0
at the reference point. Therefore dg/dw = dg/dS. Create a WinMap which
scales and shifts the "w" value to get the grism parameter value. */
map2a = (AstMapping *) astZoomMap( 1, dg, "", status );
map2b = (AstMapping *) astShiftMap( 1, &gcrv, "", status );
map2 = (AstMapping *) astCmpMap( map2a, map2b, 1, "", status );
map2a = astAnnul( map2a );
map2b = astAnnul( map2b );
/* The Mapping to be returned is the concatenation of the above Mapping
(from w to g) with the Mapping from g to S. */
astInvert( map1 );
ret = (AstMapping *) astCmpMap( map2, map1, 1, "", status );
map2 = astAnnul( map2 );
}
map1 = astAnnul( map1 );
smap = astAnnul( smap );
gmap = astAnnul( gmap );
}
wfrm = astAnnul( wfrm );
/* Return the result */
return ret;
}
static int KeyFields( AstFitsChan *this, const char *filter, int maxfld,
int *ubnd, int *lbnd, int *status ){
/*
*+
* Name:
* astKeyFields
* Purpose:
* Find the ranges taken by integer fields within the keyword names
* in a FitsChan.
* Type:
* Protected virtual function.
* Synopsis:
* #include "fitschan.h"
* int astKeyFields( AstFitsChan *this, const char *filter, int maxfld,
* int *ubnd, int *lbnd )
* Class Membership:
* FitsChan method.
* Description:
* This function returns the number of cards within a FitsChan which
* refer to keywords which match the supplied filter template. If the
* filter contains any integer field specifiers (e.g. "%d", "%3d", etc),
* it also returns the upper and lower bounds found for the integer
* fields.
* Parameters:
* this
* Pointer to the FitsChan.
* filter
* The filter string.
* maxfld
* The size of the "ubnd" and "lbnd" arrays.
* ubnd
* A pointer to an integer array in which to return the
* upper bound found for each integer field in the filter.
* They are stored in the order in which they occur in the filter.
* If the filter contains too many fields to fit in the supplied
* array, the excess trailing fields are ignored.
* lbnd
* A pointer to an integer array in which to return the
* lower bound found for each integer field in the filter.
* Returned Value:
* astKeyFields()
* The total number of cards matching the supplied filter in the
* FitsChan.
* Filter Syntax:
* - The criteria for a keyword name to match a filter template are
* as follows:
* - All characters in the template other than "%" (and the field width
* and type specifiers which follow a "%") must be matched by an
* identical character in the test string.
- If a "%" occurs in the template, then the next character in the
* template should be a single digit specifying a field width. If it is
* zero, then the test string may contain zero or more matching characters.
* Otherwise, the test string must contain exactly the specified number
* of matching characters (i.e. 1 to 9). The field width digit may be
* omitted, in which case the test string must contain one or more matching
* characters. The next character in the template specifies the type of
* matching characters and must be one of "d", "c" or "f". Decimal digits
* are matched by "d", all upper (but not lower) case alphabetical
* characters are matched by "c", and all characters which may legally be
* found within a FITS keyword name are matched by "f".
* Examples:
* - The filter "CRVAL1" accepts the single keyword CRVAL1.
* - The filter "CRVAL%1d" accepts the single keyword CRVAL0, CRVAL1,
* CRVAL2, up to CRVAL9.
* - The filter "CRVAL%d" accepts any keyword consisting of the string
* "CRVAL" followed by any integer value.
* - The filter "CR%0s1" accepts any keyword starting with the string "CR"
* and ending with the character "1" (including CR1).
* Notes:
* - The entire FitsChan is searched, irrespective of the setting of
* the Card attribute.
* - If "maxfld" is supplied as zero, "ubnd" and "lbnd" are ignored,
* but the number of matching cards is still returned as the function value.
* - If no matching cards are found in the FitsChan, or if there are no
* integer fields in the filter, then the lower and upper bounds are
* returned as zero and -1 (i.e. reversed).
* - If an error has already occured, or if this function should fail
* for any reason, a value of zero is returned for the function value,
* and the lower and upper bounds are set to zero and -1.
*-
*/
/* Local Variables: */
const char *class; /* Object class */
const char *method; /* Method name */
int *fields; /* Pointer to array of field values */
int i; /* Field index */
int icard; /* Index of current card on entry */
int nmatch; /* No. of matching cards */
int nf; /* No. of integer fields in the filter */
int nfld; /* No. of integer fields in current keyword name */
/* Initialise the returned values. */
nmatch = 0;
for( i = 0; i < maxfld; i++ ){
lbnd[ i ] = 0;
ubnd[ i ] = -1;
}
nf = 0;
/* Check the global error status. */
if ( !astOK || !filter ) return nf;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Store the method name and object class for use in error messages. */
method = "astKeyFields";
class = astGetClass( this );
/* Count the number of integer fields in the filter string. */
nf = CountFields( filter, 'd', method, class, status );
/* If this is larger than the supplied arrays, use the size of the arrays
instead. */
if( nf > maxfld ) nf = maxfld;
/* Allocate memory to hold the integer field values extracted from
each matching keyword. */
fields = (int *) astMalloc( sizeof( int )*(size_t) nf );
/* Save the current card index, and rewind the FitsChan. */
icard = astGetCard( this );
astClearCard( this );
/* Check that the FitsChan is not empty and the pointer can be used. */
if( !astFitsEof( this ) && astOK ){
/* Initialise the returned bounds. Any excess elements in the array are left
at the previously initialised values. */
for( i = 0; i < nf; i++ ){
lbnd[ i ] = INT_MAX;
ubnd[ i ] = -INT_MAX;
}
/* Initialise the number of matching keywords. */
nmatch = 0;
/* Loop round all the cards in the FitsChan. */
while( !astFitsEof( this ) && astOK ){
/* If the current keyword name matches the filter, update the returned
bounds and increment the number of matches. */
if( Match( CardName( this, status ), filter, nf, fields, &nfld,
method, class, status ) ){
for( i = 0; i < nf; i++ ){
if( fields[ i ] > ubnd[ i ] ) ubnd[ i ] = fields[ i ];
if( fields[ i ] < lbnd[ i ] ) lbnd[ i ] = fields[ i ];
}
nmatch++;
}
/* Move on to the next card. */
MoveCard( this, 1, method, class, status );
}
/* If bounds were not found, returned 0 and -1. */
for( i = 0; i < nf; i++ ){
if( lbnd[ i ] == INT_MAX ){
lbnd[ i ] = 0;
ubnd[ i ] = -1;
}
}
}
/* Reinstate the original current card index. */
astSetCard( this, icard );
/* Free the memory used to hold the integer field values extracted from
each matching keyword. */
fields = (int *) astFree( (void *) fields );
/* If an error has occurred, returned no matches and reversed bounds. */
if( !astOK ){
nmatch = 0;
for( i = 0; i < maxfld; i++ ){
lbnd[ i ] = 0;
ubnd[ i ] = -1;
}
}
/* Returned the answer. */
return nmatch;
}
static int FindFits( AstFitsChan *this, const char *name,
char card[ AST__FITSCHAN_FITSCARDLEN + 1 ], int inc, int *status ){
/*
*++
* Name:
c astFindFits
f AST_FINDFITS
* Purpose:
* Find a FITS card in a FitsChan by keyword.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c int astFindFits( AstFitsChan *this, const char *name, char card[ 81 ],
c int inc )
f RESULT = AST_FINDFITS( THIS, NAME, CARD, INC, STATUS )
* Class Membership:
* FitsChan member function.
* Description:
* This function searches for a card in a FitsChan by keyword. The
* search commences at the current card (identified by the Card
* attribute) and ends when a card is found whose FITS keyword
* matches the template supplied, or when the last card in the
* FitsChan has been searched.
*
* If the search is successful (i.e. a card is found which matches
c the template), the contents of the card are (optionally)
f the template), the contents of the card are
* returned and the Card attribute is adjusted to identify the card
* found or, if required, the one following it. If the search is
c not successful, the function returns zero and the Card attribute
f not successful, the function returns .FALSE. and the Card attribute
* is set to the "end-of-file".
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
c name
f NAME = CHARACTER * ( * ) (Given)
c Pointer to a null-terminated character string containing a
f A character string containing a
* template for the keyword to be found. In the simplest case,
* this should simply be the keyword name (the search is case
* insensitive and trailing spaces are ignored). However, this
* template may also contain "field specifiers" which are
* capable of matching a range of characters (see the "Keyword
* Templates" section for details). In this case, the first card
* with a keyword which matches the template will be found. To
* find the next FITS card regardless of its keyword, you should
* use the template "%f".
c card
f CARD = CHARACTER * ( 80 ) (Returned)
c An array of at least 81 characters (to allow room for a
c terminating null)
f A character variable with at least 80 characters
* in which the FITS card which is found will be returned. If
c the search is not successful (or a NULL pointer is given), a
f the search is not successful, a
* card will not be returned.
c inc
f INC = LOGICAL (Given)
c If this value is zero (and the search is successful), the
f If this value is .FALSE. (and the search is successful), the
* FitsChan's Card attribute will be set to the index of the card
c that was found. If it is non-zero, however, the Card
f that was found. If it is .TRUE., however, the Card
* attribute will be incremented to identify the card which
* follows the one found.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Returned Value:
c astFindFits()
f AST_FINDFITS = LOGICAL
c One if the search was successful, otherwise zero.
f .TRUE. if the search was successful, otherwise .FALSE..
* Notes:
* - The search always starts with the current card, as identified
* by the Card attribute. To ensure you search the entire contents
* of a FitsChan, you should first clear the Card attribute (using
c astClear). This effectively "rewinds" the FitsChan.
f AST_CLEAR). This effectively "rewinds" the FitsChan.
* - If a search is unsuccessful, the Card attribute is set to the
* "end-of-file" (i.e. to one more than the number of cards in the
* FitsChan). No error occurs.
c - A value of zero will be returned if this function is invoked
f - A value of .FALSE. will be returned if this function is invoked
* with the AST error status set, or if it should fail for any
* reason.
* Examples:
c result = astFindFits( fitschan, "%f", card, 1 );
f RESULT = AST_FINDFITS( FITSCHAN, '%f', CARD, .TRUE., STATUS )
* Returns the current card in a FitsChan and advances the Card
* attribute to identify the card that follows (the "%f"
* template matches any keyword).
c result = astFindFits( fitschan, "BITPIX", card, 1 );
f RESULT = AST_FINDFITS( FITSCHAN, 'BITPIX', CARD, .TRUE., STATUS )
* Searches a FitsChan for a FITS card with the "BITPIX" keyword
* and returns that card. The Card attribute is then incremented
* to identify the card that follows it.
c result = astFindFits( fitschan, "COMMENT", NULL, 0 );
f RESULT = AST_FINDFITS( FITSCHAN, 'COMMENT', CARD, .FALSE., STATUS )
* Sets the Card attribute of a FitsChan to identify the next
c COMMENT card (if any). The card itself is not returned.
f COMMENT card (if any) and returns that card.
c result = astFindFits( fitschan, "CRVAL%1d", card, 1 );
f RESULT = AST_FINDFITS( FITSCHAN, 'CRVAL%1d', CARD, .TRUE., STATUS )
* Searches a FitsChan for the next card with a keyword of the
* form "CRVALi" (for example, any of the keywords "CRVAL1",
* "CRVAL2" or "CRVAL3" would be matched). The card found (if
* any) is returned, and the Card attribute is then incremented
* to identify the following card (ready to search for another
* keyword with the same form, perhaps).
* Keyword Templates:
* The templates used to match FITS keywords are normally composed
* of literal characters, which must match the keyword exactly
* (apart from case). However, a template may also contain "field
* specifiers" which can match a range of possible characters. This
* allows you to search for keywords that contain (for example)
* numbers, where the digits comprising the number are not known in
* advance.
*
* A field specifier starts with a "%" character. This is followed
* by an optional single digit (0 to 9) specifying a field
* width. Finally, there is a single character which specifies the
* type of character to be matched, as follows:
*
* - "c": matches all upper case letters,
* - "d": matches all decimal digits,
* - "f": matches all characters which are permitted within a FITS
* keyword (upper case letters, digits, underscores and hyphens).
*
* If the field width is omitted, the field specifier matches one
* or more characters. If the field width is zero, it matches zero
* or more characters. Otherwise, it matches exactly the number of
* characters specified. In addition to this:
*
* - The template "%f" will match a blank FITS keyword consisting
* of 8 spaces (as well as matching all other keywords).
* - A template consisting of 8 spaces will match a blank keyword
* (only).
*
* For example:
*
* - The template "BitPix" will match the keyword "BITPIX" only.
* - The template "crpix%1d" will match keywords consisting of
* "CRPIX" followed by one decimal digit.
* - The template "P%c" will match any keyword starting with "P"
* and followed by one or more letters.
* - The template "E%0f" will match any keyword beginning with "E".
* - The template "%f" will match any keyword at all (including a
* blank one).
*--
*/
/* Local Variables: */
char *c; /* Pointer to next character to check */
char *lname; /* Pointer to copy of name without trailing spaces */
const char *class; /* Object class */
const char *method; /* Calling method */
int ret; /* Was a card found? */
/* Check the global status, and supplied keyword name. */
if( !astOK ) return 0;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Store the calling method and object class. */
method = "astFindFits";
class = astGetClass( this );
/* Get a local copy of the keyword template. */
lname = (char *) astStore( NULL, (void *) name, strlen(name) + 1 );
/* Terminate it to exclude trailing spaces. */
c = lname + strlen(lname) - 1;
while( *c == ' ' && c >= lname ) *(c--) = 0;
/* Use the private FindKeyCard function to find the card and make it the
current card. Always use the supplied current card (if any) if the
template is "%f" or "%0f". */
if ( !strcmp( lname, "%f" ) || !strcmp( lname, "%0f" ) ){
ret = astFitsEof( this ) ? 0 : 1;
} else {
ret = FindKeyCard( this, lname, method, class, status );
}
/* Only proceed if the card was found. */
if( ret && astOK ){
/* Format the current card if a destination string was supplied. */
if( card ) FormatCard( this, card, method, status );
/* Increment the current card pointer if required. */
if( inc ) MoveCard( this, 1, method, class, status );
/* Indicate that a card has been formatted. */
ret = 1;
}
/* Free the memory holding the local copy of the keyword template. */
lname = (char *) astFree( (void *) lname );
/* If an errror has occurred, return zero. */
if( !astOK ) ret = 0;
/* Return the answer. */
return ret;
}
static int FindKeyCard( AstFitsChan *this, const char *name,
const char *method, const char *class, int *status ){
/*
* Name:
* FindKeyCard
* Purpose:
* Find the next card refering to given keyword.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int FindKeyCard( AstFitsChan *this, const char *name,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Finds the next card which refers to the supplied keyword and makes
* it the current card. The search starts with the current card and ends
* when it reaches the last card.
* Parameters:
* this
* Pointer to the FitsChan.
* name
* Pointer to a string holding the keyword template (using the
* syntax expected by the Match function).
* method
* Pointer to string holding name of calling method.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A value of 1 is returned if a card was found refering to the given
* keyword. Otherwise zero is returned.
* Notes:
* - If a NULL pointer is supplied for "name" then the current card
* is left unchanged.
* - The current card is set to NULL (end-of-file) if no card can be
* found for the supplied keyword.
*/
/* Local Variables: */
int nfld; /* Number of fields in keyword template */
int ret; /* Was a card found? */
/* Check the global status, and supplied keyword name. */
if( !astOK || !name ) return 0;
/* Indicate that no card has been found yet. */
ret = 0;
/* Search forward through the list until all cards have been checked. */
while( !astFitsEof( this ) && astOK ){
/* Break out of the loop if the keyword name from the current card matches
the supplied keyword name. */
if( Match( CardName( this, status ), name, 0, NULL, &nfld, method, class, status ) ){
ret = 1;
break;
/* Otherwise, move the current card on to the next card. */
} else {
MoveCard( this, 1, method, class, status );
}
}
/* Return. */
return ret;
}
static double *FitLine( AstMapping *map, double *g, double *g0, double *w0,
double dim, double *tol, int *status ){
/*
* Name:
* FitLine
* Purpose:
* Check a Mapping for linearity.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* double *FitLine( AstMapping *map, double *g, double *g0, double *w0,
* double dim, double *tol, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function applies the supplied Mapping to a set of points along
* a straight line in the input space. It checks to see if the transformed
* positions also lie on a straight line (in the output space). If so,
* it returns the vector along this line in the output space which
* corresponds to a unit vector along the line in the input space. If
* not, a NULL pointer is returned.
*
* The returned vector is found by doing a least squares fit.
* Parameters:
* map
* A pointer to the Mapping to test. The number of outputs must be
* greater than or equal to the number of inputs.
* g
* A pointer to an array holding a unit vector within the input space
* defining the straight line to be checked. The number of elements
* within this array should equal the number of inputs for "map".
* g0
* A pointer to an array holding a position within the input space
* giving the central position of the vector "g". The number of elements
* within this array should equal the number of inputs for "map".
* w0
* A pointer to an array holding a vector within the output space
* which corresponds to "g0". The number of elements within this array
* should equal the number of outputs for "map".
* dim
* The length of the pixel axis, or AST__BAD if unknown.
* tol
* Pointer to an array holding the tolerance for equality on each
* output axis.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to dynamically allocated memory holding the required vector
* in the output space. The number of elements in this vector will equal
* the number of outputs for "map". The memory should be freed using
* astFree when no longer needed. If the Mapping is not linear, NULL
* is returned.
* Notes:
* - NULL is returned if an error occurs.
*/
/* Local Constants: */
#define NPO2 50
#define NP (2*NPO2+1)
/* Local Variables: */
AstPointSet *pset1;
AstPointSet *pset2;
double **ptr1;
double **ptr2;
double *offset;
double *pax;
double *ret;
double *voffset;
double dax;
double denom;
double gap;
double sd2;
double sd;
double sdw;
double sw;
double wmax;
double wmin;
int i;
int j;
int n;
int nin;
int nout;
int ok;
/* Initialise */
ret = NULL;
/* Check the inherited status and supplied axis size. */
if( !astOK || dim == 0.0 ) return ret;
/* Get the number of inputs and outputs for the Mapping. Return if the
number of outputs is smaller than the number of inputs. */
nin = astGetNin( map );
nout = astGetNout( map );
if( nout < nin ) return ret;
/* Check the supplied position is good on all axes. */
for( j = 0; j < nout; j++ ) {
if( w0[ j ] == AST__BAD ) return ret;
}
/* We use NP points in the fit. If a value for "dim" has been supplied,
we use points evenly distributed over the whole axis. If not, we use
a gap of 1.0 (corresponds to an axis length of 100 pixels).
Choose the gap. */
gap = ( dim != AST__BAD ) ? dim/NP : 1.0;
/* Create PointSets to hold the input and output positions. */
pset1 = astPointSet( NP, nin, "", status );
ptr1 = astGetPoints( pset1 );
pset2 = astPointSet( NP, nout, "", status );
ptr2 = astGetPoints( pset2 );
/* Allocate the returned array. */
ret = astMalloc( sizeof( double )*(size_t) nout );
/* Allocate workspace to hold the constant offsets of the fit. */
offset = astMalloc( sizeof( double )*(size_t) nout );
voffset = astMalloc( sizeof( double )*(size_t) nout );
/* Indicate we have not yet got a usable returned vector. */
ok = 0;
/* Check we can use the pointers safely. */
if( astOK ) {
/* Set up the input positions: NP evenly spaced points along a line with
unit direction vector given by "g", centred at position given by "g0". */
for( j = 0; j < nin; j++ ) {
pax = ptr1[ j ];
dax = g[ j ]*gap;
for( i = -NPO2; i <= NPO2; i++ ) *(pax++) = g0[ j ] + dax*i;
}
/* Transform these positions into the output space. */
(void) astTransform( map, pset1, 1, pset2 );
/* Loop over all output axes, finding the component of the returned vector. */
ok = 1;
for( j = 0; j < nout; j++ ) {
pax = ptr2[ j ];
/* Now loop over all the transformed points to form the other required
sums. We also form the sums needed to estimate the variance in the
calculated offset. */
sdw = 0.0;
sw = 0.0;
sd = 0.0;
sd2 = 0.0;
n = 0;
wmax = -DBL_MAX;
wmin = DBL_MAX;
for( i = -NPO2; i <= NPO2; i++, pax++ ) {
if( *pax != AST__BAD ) {
/* Increment the required sums. */
sdw += i*(*pax);
sw += (*pax);
sd += i;
sd2 += i*i;
n++;
if( *pax > wmax ) wmax = *pax;
if( *pax < wmin ) wmin = *pax;
}
}
/* If a reasonable number of good points were found, find the component of
the returned vector (excluding a scale factor of 1/gap). */
denom = sd2*n - sd*sd;
if( n > NP/4 && denom != 0.0 ) {
/* Find the constant scale factor to return for this axis. If the axis
value is constant, return zero. */
if( wmax > wmin ) {
ret[ j ] = (sdw*n - sw*sd)/denom;
} else {
ret[ j ] = 0.0;
}
/* Now find the constant offset for this axis. */
offset[ j ] = (sw*sd2 - sdw*sd)/denom;
} else {
ok = 0;
break;
}
}
/* Now check that the fit is good enough. Each axis is checked separately.
All axes must be good. */
if( ok ) {
for( j = 0; j < nout; j++ ) {
/* Store the axis values implied by the linear fit in the now un-needed ptr1[0]
array. */
pax = ptr1[ 0 ];
for( i = -NPO2; i <= NPO2; i++, pax++ ) {
*pax = i*ret[ j ] + offset[ j ];
}
/* Test the fit to see if we beleive that the mapping is linear. If
it is, scale the returned value from units of "per gap" to units of
"per pixel". Otherwise,indicate that he returned vector is unusable. */
if( FitOK( NP, ptr2[ j ], ptr1[ 0 ], tol[ j ], status ) ) {
ret[ j ] /= gap;
} else {
ok = 0;
break;
}
}
}
}
/* Annul the PointSets. */
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
/* Free memory. */
offset = astFree( offset );
voffset = astFree( voffset );
/* If an error has occurred, or if the returned vector is unusable,
free any returned memory */
if( !astOK || !ok ) ret = astFree( ret );
/* Return the answer. */
return ret;
/* Undefine local constants: */
#undef NP
#undef NPO2
}
static int FitsEof( AstFitsChan *this, int *status ){
/*
*+
* Name:
* astFitsEof
* Purpose:
* See if the FitsChan is at "end-of-file".
* Type:
* Protected virtual function.
* Synopsis:
* #include "fitschan.h"
* int astFitsEof( AstFitsChan *this )
* Class Membership:
* FitsChan method.
* Description:
* A value of zero is returned if any more cards remain to be read from the
* FitsChan. Otherwise a value of 1 is returned. Thus, it is
* equivalent to testing the FitsChan for an "end-of-file" condition.
* Parameters:
* this
* Pointer to the FitsChan.
* Returned Value:
* One if no more cards remain to be read, otherwise zero.
* Notes:
* - This function attempts to execute even if an error has already
* occurred.
*-
*/
/* Check the supplied object. */
if( !this ) return 1;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* If no more cards remain to be read, the current card pointer in the
FitsChan will be NULL. Return an appropriate integer value. */
return this->card ? 0 : 1;
}
static int FitsSof( AstFitsChan *this, int *status ){
/*
*+
* Name:
* FitsSof
* Purpose:
* See if the FitsChan is at "start-of-file".
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int FitsSof( AstFitsChan *this, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* A value of 1 is returned if the current card is the first card in
* the FitsChan. Otherwise a value of zero is returned. This function
* is much more efficient than "astGetCard(this) <= 1" .
* Parameters:
* this
* Pointer to the FitsChan.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Zero if the current card is the first card.
* Notes:
* - This function attempts to execute even if an error has already
* occurred.
* - A non-zero value is returned if the FitsChan is empty.
*-
*/
/* Return if no FitsChan was supplied, or if the FitsChan is empty. */
if ( !this || !this->head ) return 1;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* If the current card is at the head of the linked list, it is the first
card. */
return this->card == this->head;
}
/*
*++
* Name:
c astGetFits
f AST_GETFITS
* Purpose:
* Get a named keyword value from a FitsChan.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c int astGetFits( AstFitsChan *this, const char *name, type *value )
f RESULT = AST_GETFITS( THIS, NAME, VALUE, STATUS )
* Class Membership:
* FitsChan method.
* Description:
* This is a family of functions which gets a value for a named keyword,
* or the value of the current card, from a FitsChan using one of several
* different data types. The data type of the returned value is selected
* by replacing in the function name by one of the following strings
* representing the recognised FITS data types:
*
* - CF - Complex floating point values.
* - CI - Complex integer values.
* - F - Floating point values.
* - I - Integer values.
* - L - Logical (i.e. boolean) values.
* - S - String values.
* - CN - A "CONTINUE" value, these are treated like string values, but
* are encoded without an equals sign.
*
* The data type of the "value"
c parameter
f argument
* depends on as follows:
*
c - CF - "double *" (a pointer to a 2 element array to hold the real and
c imaginary parts of the complex value).
c - CI - "int *" (a pointer to a 2 element array to hold the real and
c imaginary parts of the complex value).
c - F - "double *".
c - I - "int *".
c - L - "int *".
c - S - "char **" (a pointer to a static "char" array is returned at the
c location given by the "value" parameter, Note, the stored string
c may change on subsequent invocations of astGetFitsS so a
c permanent copy should be taken of the string if necessary).
c - CN - Like"S".
f - CF - DOUBLE PRECISION(2) (a 2 element array to hold the real and
f imaginary parts of the complex value).
f - CI - INTEGER(2) (a 2 element array to hold the real and imaginary
f parts of the complex value).
f - F - DOUBLE PRECISION.
f - I - INTEGER
f - L - LOGICAL
f - S - CHARACTER
f - CN - CHARACTER
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
c name
f NAME = CHARACTER * ( * ) (Given)
c Pointer to a null-terminated character string
f A character string
* containing the FITS keyword name. This may be a complete FITS
* header card, in which case the keyword to use is extracted from
* it. No more than 80 characters are read from this string. If
c NULL
f a single dot '.'
* is supplied, the value of the current card is returned.
c value
f VALUE = type (Returned)
c A pointer to a
f A
* buffer to receive the keyword value. The data type depends on
* as described above. The conents of the buffer on entry are left
* unchanged if the keyword is not found.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Returned Value:
c astGetFits()
f AST_GETFITS = LOGICAL
c A value of zero
f .FALSE.
* is returned if the keyword was not found in the FitsChan (no error
* is reported). Otherwise, a value of
c one
f .TRUE.
* is returned.
* Notes:
* - If a name is supplied, the card following the current card is
* checked first. If this is not the required card, then the rest of the
* FitsChan is searched, starting with the first card added to the
* FitsChan. Therefore cards should be accessed in the order they are
* stored in the FitsChan (if possible) as this will minimise the time
* spent searching for cards.
* - If the requested card is found, it becomes the current card,
* otherwise the current card is left pointing at the "end-of-file".
* - If the stored keyword value is not of the requested type, it is
* converted into the requested type.
* - If the keyword is found in the FitsChan, but has no associated
* value, an error is reported. If necessary, the
c astTestFits
f AST_TESTFITS
* function can be used to determine if the keyword has a defined
* value in the FitsChan prior to calling this function.
* - An error will be reported if the keyword name does not conform
* to FITS requirements.
c - Zero
* - .FALSE.
* is returned as the function value if an error has already occurred,
* or if this function should fail for any reason.
* - The FITS standard says that string keyword values should be
* padded with trailing spaces if they are shorter than 8 characters.
* For this reason, trailing spaces are removed from the string
* returned by
c astGetFitsS
f AST_GETFITSS
* if the original string (including any trailing spaces) contains 8
* or fewer characters. Trailing spaces are not removed from longer
* strings.
*--
*/
/* Define a macro which expands to the implementation of the astGetFits
routine for a given data type. */
#define MAKE_FGET(code,ctype,ftype) \
static int GetFits##code( AstFitsChan *this, const char *name, ctype value, int *status ){ \
\
/* Local Variables: */ \
const char *class; /* Object class */ \
const char *method; /* Calling method */ \
char *lcom; /* Supplied keyword comment */ \
char *lname; /* Supplied keyword name */ \
char *lvalue; /* Supplied keyword value */ \
char *string; /* Pointer to returned string value */ \
char *c; /* Pointer to next character */ \
int cl; /* Length of string value */ \
int ret; /* The returned value */ \
\
/* Check the global error status. */ \
if ( !astOK ) return 0; \
\
/* Ensure the source function has been called */ \
ReadFromSource( this, status ); \
\
/* Store the calling method and object class. */ \
method = "astGetFits"#code; \
class = astGetClass( this ); \
\
/* Initialise the returned value. */ \
ret = 0; \
\
/* Extract the keyword name from the supplied string. */ \
if( name ) { \
(void) Split( this, name, &lname, &lvalue, &lcom, method, class, status ); \
} else { \
lname = NULL; \
lvalue = NULL; \
lcom = NULL; \
} \
\
/* Attempt to find a card in the FitsChan refering to this keyword, \
and make it the current card. Only proceed if a card was found. No \
need to do the search if the value of the current card is required. */ \
if( !lname || SearchCard( this, lname, method, class, status ) ){ \
\
/* Convert the stored data value to the requested type, and store it in \
the supplied buffer. */ \
if( !CnvValue( this, ftype, 0, value, method, status ) && astOK ) { \
astError( AST__FTCNV, "%s(%s): Cannot convert FITS keyword " \
"'%s' to %s.", status, method, class, \
CardName( this, status ), type_names[ ftype ] ); \
} \
\
/* If the returned value is a string containing 8 or fewer characters, \
replace trailing spaces with null characters. */ \
if( astOK ) { \
if( ftype == AST__STRING ) { \
string = *( (char **) value ); \
if( string ) { \
cl =strlen( string ); \
if( cl <= 8 ) { \
c = string + cl - 1; \
while( *c == ' ' && c > string ) { \
*c = 0; \
c--; \
} \
} \
} \
} \
\
/* Indicate that a value is available. */ \
ret = 1; \
} \
\
} \
\
/* Context error message. */ \
if( !astOK && lname && *lname ) { \
astError( astStatus, "%s(%s): Cannot get value for FITS keyword " \
"'%s'.", status, method, class, lname ); \
} \
\
/* Release the memory used to hold keyword name, value and comment strings. */ \
lname = (char *) astFree( (void *) lname ); \
lvalue = (char *) astFree( (void *) lvalue ); \
lcom = (char *) astFree( (void *) lcom ); \
\
/* Return the answer. */ \
return ret; \
\
}
/* Use the above macro to give defintions for the astGetFits method
for each FITS data type. */
MAKE_FGET(CF,double *,AST__COMPLEXF)
MAKE_FGET(CI,int *,AST__COMPLEXI)
MAKE_FGET(F,double *,AST__FLOAT)
MAKE_FGET(I,int *,AST__INT)
MAKE_FGET(L,int *,AST__LOGICAL)
MAKE_FGET(S,char **,AST__STRING)
MAKE_FGET(CN,char **,AST__CONTINUE)
#undef MAKE_FGET
static int FitsGetCom( AstFitsChan *this, const char *name,
char **comment, int *status ){
/*
*+
* Name:
* astFitsGetCom
* Purpose:
* Get a keyword comment from a FitsChan.
* Type:
* Protected virtual function.
* Synopsis:
* #include "fitschan.h"
* int astFitsGetCom( AstFitsChan *this, const char *name,
* char **comment )
* Class Membership:
* FitsChan method.
* Description:
* This function gets the comment associated with the next occurrence of
* a named keyword in a FitsChan.
* Parameters:
* this
* Pointer to the FitsChan.
* name
* A pointer to a
* string holding the keyword name. This may be a complete FITS
* header card, in which case the keyword to use is extracted from
* it. No more than 80 characters are read from this string.
* comment
* A pointer to a location at which to return a pointer to a string
* holding the keyword comment. Note, the stored string will change on
* subsequent invocations of astFitsGetCom so a permanent copy
* should be taken of the string if necessary.
* Returned Value:
* astFitsGetCom()
* A value of zero is returned if the keyword was not found before
* the end of the FitsChan was reached (no error is reported).
* Otherwise, a value of one is returned.
* Notes:
* - If a NULL pointer is supplied for "name" then the comment from
* the current card is returned.
* - The returned value is obtained from the next card refering to
* the required keyword, starting the search with the current card.
* Any cards occuring before the current card are not seached. If
* the entire contents of the FitsChan must be searched, then ensure
* the current card is the first card in the FitsChan by clearing the Card
* attribute. This effectively "rewinds" the FitsChan.
* - The current card is updated to become the card following the one
* read by this function. If the card read by this function is the
* last one in the FitsChan, then the current card is left pointing at the
* "end-of-file".
* - An error will be reported if the keyword name does not conform
* to FITS requirements.
* - A NULL pointer is returned for the comment string if the keyword
* has no comment.
* - Zero is returned as the function value if an error has already
* occurred, or if this function should fail for any reason.
*-
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
const char *method; /* Calling method */
const char *class; /* Object class */
char *lcom; /* Supplied keyword comment */
char *lname; /* Supplied keyword name */
char *lvalue; /* Supplied keyword value */
int ret; /* The returned value */
/* Check the global error status. */
if ( !astOK ) return 0;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this);
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Initialise the returned value. */
ret = 0;
/* Store the method name and object class. */
method = "astFitsGetCom";
class = astGetClass( this );
/* Extract the keyword name from the supplied string (if supplied). */
if( name ){
(void) Split( this, name, &lname, &lvalue, &lcom, method, class, status );
} else {
lname = NULL;
lcom = NULL;
lvalue = NULL;
}
/* Find the next card in the FitsChan refering to this keyword. This will
be the current card if no keyword name was supplied. The matching card
is made the current card. Only proceed if a card was found. */
if( FindKeyCard( this, lname, method, class, status ) ){
/* Copy the comment into a static buffer, and return a pointer to it. */
if( CardComm( this, status ) ){
(void) strncpy( fitsgetcom_sval, CardComm( this, status ), AST__FITSCHAN_FITSCARDLEN );
fitsgetcom_sval[ AST__FITSCHAN_FITSCARDLEN ] = 0;
if( comment ) *comment = fitsgetcom_sval;
} else {
if( comment ) *comment = NULL;
}
/* Move on to the next card. */
MoveCard( this, 1, method, class, status );
/* Indicate that a value is available. */
if( astOK ) ret = 1;
}
/* Release the memory used to hold keyword name, value and comment strings. */
lname = (char *) astFree( (void *) lname );
lvalue = (char *) astFree( (void *) lvalue );
lcom = (char *) astFree( (void *) lcom );
/* Return the answer. */
return ret;
}
static int SetFits( AstFitsChan *this, const char *keyname, void *value,
int type, const char *comment, int overwrite, int *status ){
/*
* Name:
* SetFits
* Purpose:
* Store a keyword value of any type in a FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int SetFits( AstFitsChan *this, const char *keyname, void *value,
* int type, const char *comment, int overwrite, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function stores the supplied value for the supplied keyword
* in the supplied FitsChan, assuming it is of the supplied data type.
* Parameters:
* this
* Pointer to the FitsChan.
* name
* A pointer to a string holding the keyword name.
* value
* A pointer to a buffer holding the keyword value. For strings,
* the buffer should hold the address of a pointer to the character
* string.
* type
* The keyword type.
* comment
* A pointer to a string holding a comment to associated with the
* keyword. If a NULL pointer or a blank string is supplied, then
* any comment included in the string supplied for the "name" parameter
* is used instead. If "name" contains no comment, then any existing
* comment in the card being over-written is retained, or a NULL
* pointer is stored if a new card is being inserted. If the data
* value being stored for the card is the same as the card being
* over-written, then any existing comment is retained.
* overwrite
* If non-zero, the new card formed from the supplied keyword name,
* value and comment string over-writes the current card, and the
* current card is incremented to refer to the next card. If zero, the
* new card is inserted in front of the current card and the current
* card is left unchanged. In either case, if the current card on
* entry points to the "end-of-file", the new card is appended to the
* end of the list.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A value of 0 is returned if the value could not be stored for any
* reason. A value of 1 is returned otherwise.
* Notes:
* - Nothing is stored in the FitsChan and a value of zero is returned
* (but no error is reported) if an AST__FLOAT value is supplied equal
* to AST__BAD.
*/
/* Local Variables: */
const char *cval;
const char *ecval;
double dval;
double ecdval[ 2 ];
double edval;
int ecival[ 2 ];
int eival;
int ival;
int ret;
/* Check the global status, and the supplied pointer. */
if( !astOK || !value ) return 0;
/* Initialise the returned value to indicate that the supplied name was
stored. */
ret = 1;
/* Check each data type in turn. */
if( type == AST__FLOAT ){
dval = *( (double *) value );
if( dval != AST__BAD ) {
/* If the data value has not changed, and the card has a coment,
set the comment pointer NULL so that the existing comment will be
retained. */
if( overwrite && CnvValue( this, type, 0, &edval, "SetFits",
status ) &&
CardComm( this, status ) ) {
if( EQUAL( edval, dval ) ) comment = NULL;
}
astSetFitsF( this, keyname, dval, comment, overwrite );
} else {
ret = 0;
}
} else if( type == AST__STRING ){
cval = *( (char **) value);
if( cval ){
/* If the data value has not changed, retain the original comment. */
if( overwrite && CnvValue( this, type, 0, &ecval, "SetFits",
status ) &&
CardComm( this, status ) ) {
if( Similar( ecval, cval, status ) ) comment = NULL;
}
/* Ignore comments if they are identical to the keyword value. */
if( comment && !strcmp( cval, comment ) ) comment = NULL;
astSetFitsS( this, keyname, cval, comment, overwrite );
} else {
ret = 0;
}
} else if( type == AST__CONTINUE ){
cval = *( (char **) value);
if( cval ){
astSetFitsCN( this, keyname, cval, comment, overwrite );
} else {
ret = 0;
}
} else if( type == AST__COMMENT ){
astSetFitsCom( this, keyname, comment, overwrite );
} else if( type == AST__INT ){
ival = *( (int *) value );
/* If the data value has not changed, retain the original comment. */
if( overwrite && CnvValue( this, type, 0, &eival, "SetFits",
status ) &&
CardComm( this, status ) ) {
if( eival == ival ) comment = NULL;
}
astSetFitsI( this, keyname, ival, comment, overwrite );
} else if( type == AST__COMPLEXF ){
if( ( (double *) value )[0] != AST__BAD &&
( (double *) value )[1] != AST__BAD ) {
/* If the data value has not changed, retain the original comment. */
if( overwrite && CnvValue( this, type, 0, ecdval, "SetFits",
status ) &&
CardComm( this, status ) ) {
if( EQUAL( ecdval[ 0 ], ( (double *) value )[ 0 ] ) &&
EQUAL( ecdval[ 1 ], ( (double *) value )[ 1 ] ) ) comment = NULL;
}
astSetFitsCF( this, keyname, (double *) value, comment, overwrite );
} else {
ret = 0;
}
} else if( type == AST__COMPLEXI ){
/* If the data value has not changed, retain the original comment. */
if( overwrite && CnvValue( this, type, 0, ecival, "SetFits",
status ) &&
CardComm( this, status ) ) {
if( ecival[ 0 ] == ( (int *) value )[ 0 ] &&
ecival[ 1 ] == ( (int *) value )[ 1 ] ) comment = NULL;
}
astSetFitsCI( this, keyname, (int *) value, comment, overwrite );
} else if( type == AST__LOGICAL ){
ival = ( *( (int *) value ) != 0 );
/* If the data value has not changed, retain the original comment. */
if( overwrite && CnvValue( this, type, 0, &eival, "SetFits",
status ) &&
CardComm( this, status ) ) {
if( eival == ival ) comment = NULL;
}
astSetFitsL( this, keyname, ival, comment, overwrite );
} else if( type == AST__UNDEF ){
if( overwrite && CardType( this, status ) == AST__UNDEF && CardComm( this, status ) ) {
comment = NULL;
}
astSetFitsU( this, keyname, comment, overwrite );
}
return ret;
}
/*
*++
* Name:
c astSetFits
f AST_SETFITS
* Purpose:
* Store a keyword value in a FitsChan.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c void astSetFits( AstFitsChan *this, const char *name, type value,
c const char *comment, int overwrite )
f CALL AST_SETFITS( THIS, NAME, VALUE, COMMENT, OVERWRITE, STATUS )
* Class Membership:
* FitsChan method.
* Description:
c This is a family of functions which store values for named keywords
f This is a family of routines which store values for named keywords
* within a FitsChan at the current card position. The supplied keyword
* value can either over-write an existing keyword value, or can be
* inserted as a new header card into the FitsChan.
*
c The keyword data type is selected by replacing in the function name
f The keyword data type is selected by replacing in the routine name
* by one of the following strings representing the recognised FITS data
* types:
*
* - CF - Complex floating point values.
* - CI - Complex integer values.
* - F - Floating point values.
* - I - Integer values.
* - L - Logical (i.e. boolean) values.
* - S - String values.
* - CN - A "CONTINUE" value, these are treated like string values, but
* are encoded without an equals sign.
*
* The data type of the "value" parameter depends on as follows:
*
c - CF - "double *" (a pointer to a 2 element array holding the real and
c imaginary parts of the complex value).
c - CI - "int *" (a pointer to a 2 element array holding the real and
c imaginary parts of the complex value).
c - F - "double".
c - I - "int".
c - L - "int".
c - S - "const char *".
c - CN - "const char *".
*
f - CF - DOUBLE PRECISION(2) (a 2 element array holding the real and
f imaginary parts of the complex value).
f - CI - INTEGER(2) (a 2 element array holding the real and imaginary
f parts of the complex value).
f - F - DOUBLE PRECISION.
f - I - INTEGER
f - L - LOGICAL
f - S - CHARACTER
f - CN - CHARACTER
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
c name
f NAME = CHARACTER * ( * ) (Given)
c Pointer to a null-terminated character string
f A character string
* containing the FITS keyword name. This may be a complete FITS
* header card, in which case the keyword to use is extracted from
* it. No more than 80 characters are read from this string.
c value
f VALUE = type (Given)
* The keyword value to store with the named keyword. The data type
* of this parameter depends on as described above.
c comment
f COMMENT = CHARACTER * ( * ) (Given)
c A pointer to a null terminated string
f A string
* holding a comment to associated with the keyword.
c If a NULL pointer or
f If
* a blank string is supplied, then any comment included in the string
* supplied for the
c "name" parameter is used instead. If "name"
f NAME parameter is used instead. If NAME
* contains no comment, then any existing comment in the card being
* over-written is retained. Otherwise, no comment is stored with
* the card.
c overwrite
f OVERWRITE = LOGICAL (Given)
c If non-zero,
f If .TRUE.,
* the new card formed from the supplied keyword name, value and comment
* string over-writes the current card, and the current card is
* incremented to refer to the next card (see the "Card" attribute). If
c zero,
f .FALSE.,
* the new card is inserted in front of the current card and the current
* card is left unchanged. In either case, if the current card on entry
* points to the "end-of-file", the new card is appended to the end of
* the list.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Notes:
* - The
c function astSetFitsU
f routine AST_SETFITSU
* can be used to indicate that no value is associated with a keyword.
* - The
c function astSetFitsCM
f routine AST_SETFITSCM
* can be used to store a pure comment card (i.e. a card with a blank
* keyword).
* - To assign a new value for an existing keyword within a FitsChan,
c first find the card describing the keyword using astFindFits, and
c then use one of the astSetFits family to over-write the old value.
f first find the card describing the keyword using AST_FINDFITS, and
f then use one of the AST_SETFITS family to over-write the old value.
* - If, on exit, there are no cards following the card written by
c this function, then the current card is left pointing at the
f this routine, then the current card is left pointing at the
* "end-of-file".
* - An error will be reported if the keyword name does not conform
* to FITS requirements.
*--
*/
/* Define a macro which expands to the implementation of the astSetFits
routine for a given data type. */
#define MAKE_FSET(code,ctype,ftype,valexp) \
static void SetFits##code( AstFitsChan *this, const char *name, ctype value, const char *comment, int overwrite, int *status ) { \
\
/* Local variables: */ \
const char *class; /* Object class */ \
const char *method; /* Calling method */ \
const char *com; /* Comment to use */ \
char *lcom; /* Supplied keyword comment */ \
char *lname; /* Supplied keyword name */ \
char *lvalue; /* Supplied keyword value */ \
int free_com; /* Should com be freed before returned? */ \
\
/* Check the global error status. */ \
if ( !astOK ) return; \
\
/* Ensure the source function has been called */ \
ReadFromSource( this, status ); \
\
/* Store the object clas and calling method. */ \
class = astGetClass( this ); \
method = "astSetFits"#code; \
\
/* Extract the keyword name from the supplied string. */ \
(void) Split( this, name, &lname, &lvalue, &lcom, method, class, status ); \
\
/* Initialise a pointer to the comment to be stored. If the supplied \
comment is blank, use the comment given with "name". */ \
com = ChrLen( comment, status ) ? comment : lcom; \
\
/* If the comment is still blank, use the existing comment if we are \
over-writing, or a NULL pointer otherwise. */ \
free_com = 0; \
if( !ChrLen( com, status ) ) { \
com = NULL; \
if( overwrite ) { \
if( CardComm( this, status ) ){ \
com = (const char *) astStore( NULL, (void *) CardComm( this, status ), \
strlen( CardComm( this, status ) ) + 1 ); \
free_com = 1; \
} \
} \
} \
\
/* Insert the new card. */ \
InsCard( this, overwrite, lname, ftype, valexp, com, method, class, status ); \
\
/* Release the memory used to hold keyword name, value and comment strings. */ \
lname = (char *) astFree( (void *) lname ); \
lvalue = (char *) astFree( (void *) lvalue ); \
lcom = (char *) astFree( (void *) lcom ); \
\
/* Release the memory holding the stored comment string, so long as it was \
allocated within this function. */ \
if( free_com ) com = (const char *) astFree( (void *) com ); \
\
}
/* Use the above macro to give defintions for the astSetFits method
for each FITS data type. */
MAKE_FSET(I,int,AST__INT,(void *)&value)
MAKE_FSET(F,double,AST__FLOAT,(void *)&value)
MAKE_FSET(S,const char *,AST__STRING,(void *)value)
MAKE_FSET(CN,const char *,AST__CONTINUE,(void *)value)
MAKE_FSET(CF,double *,AST__COMPLEXF,(void *)value)
MAKE_FSET(CI,int *,AST__COMPLEXI,(void *)value)
MAKE_FSET(L,int,AST__LOGICAL,(void *)&value)
#undef MAKE_FSET
static void SetFitsU( AstFitsChan *this, const char *name, const char *comment,
int overwrite, int *status ) {
/*
*++
* Name:
c astSetFitsU
f AST_SETFITSU
* Purpose:
* Store an undefined keyword value in a FitsChan.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c void astSetFitsU( AstFitsChan *this, const char *name,
c const char *comment, int overwrite )
f CALL AST_SETFITSU( THIS, NAME, COMMENT, OVERWRITE, STATUS )
* Description:
* This
c function
f routine
* stores an undefined value for a named keyword within
* a FitsChan at the current card position. The new undefined value
* can either over-write an existing keyword value, or can be inserted
* as a new header card into the FitsChan.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
c name
f NAME = CHARACTER * ( * ) (Given)
c Pointer to a null-terminated character string
f A character string
* containing the FITS keyword name. This may be a complete FITS
* header card, in which case the keyword to use is extracted from
* it. No more than 80 characters are read from this string.
c comment
f COMMENT = CHARACTER * ( * ) (Given)
c A pointer to a null terminated string
f A string
* holding a comment to associated with the keyword.
c If a NULL pointer or
f If
* a blank string is supplied, then any comment included in the string
* supplied for the
c "name" parameter is used instead. If "name"
f NAME parameter is used instead. If NAME
* contains no comment, then any existing comment in the card being
* over-written is retained. Otherwise, no comment is stored with
* the card.
c overwrite
f OVERWRITE = LOGICAL (Given)
c If non-zero,
f If .TRUE.,
* the new card formed from the supplied keyword name and comment
* string over-writes the current card, and the current card is
* incremented to refer to the next card (see the "Card" attribute). If
c zero,
f .FALSE.,
* the new card is inserted in front of the current card and the current
* card is left unchanged. In either case, if the current card on entry
* points to the "end-of-file", the new card is appended to the end of
* the list.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Notes:
* - If, on exit, there are no cards following the card written by
* this function, then the current card is left pointing at the
* "end-of-file".
* - An error will be reported if the keyword name does not conform
* to FITS requirements.
*--
*/
/* Local variables: */
const char *class; /* Object class */
const char *method; /* Calling method */
const char *com; /* Comment to use */
char *lcom; /* Supplied keyword comment */
char *lname; /* Supplied keyword name */
char *lvalue; /* Supplied keyword value */
int free_com; /* Should com be freed before returned? */
/* Check the global error status. */
if ( !astOK ) return;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Store the object clas and calling method. */
class = astGetClass( this );
method = "astSetFitsU";
/* Extract the keyword name from the supplied string. */
(void) Split( this, name, &lname, &lvalue, &lcom, method, class, status );
/* Initialise a pointer to the comment to be stored. If the supplied
comment is blank, use the comment given with "name". */
com = ChrLen( comment, status ) ? comment : lcom;
/* If the comment is still blank, use the existing comment if we are
over-writing, or a NULL pointer otherwise. */
free_com = 0;
if( !ChrLen( com, status ) ) {
com = NULL;
if( overwrite ) {
if( CardComm( this, status ) ){
com = (const char *) astStore( NULL, (void *) CardComm( this, status ),
strlen( CardComm( this, status ) ) + 1 );
free_com = 1;
}
}
}
/* Insert the new card. */
InsCard( this, overwrite, lname, AST__UNDEF, NULL, com, method, class,
status );
/* Release the memory used to hold keyword name, value and comment strings. */
lname = (char *) astFree( (void *) lname );
lvalue = (char *) astFree( (void *) lvalue );
lcom = (char *) astFree( (void *) lcom );
/* Release the memory holding the stored comment string, so long as it was
allocated within this function. */
if( free_com ) com = (const char *) astFree( (void *) com );
}
static void SetFitsCM( AstFitsChan *this, const char *comment,
int overwrite, int *status ) {
/*
*++
* Name:
c astSetFitsCM
f AST_SETFITSCM
* Purpose:
* Store a comment card in a FitsChan.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c void astSetFitsCM( AstFitsChan *this, const char *comment,
c int overwrite )
f CALL AST_SETFITSCM( THIS, COMMENT, OVERWRITE, STATUS )
* Description:
* This
c function
f routine
* stores a comment card ( i.e. a card with no keyword name or equals
* sign) within a FitsChan at the current card position. The new card
* can either over-write an existing card, or can be inserted as a new
* card into the FitsChan.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
c comment
f COMMENT = CHARACTER * ( * ) (Given)
c A pointer to a null terminated string
f A string
* holding the text of the comment card.
c If a NULL pointer or
f If
* a blank string is supplied, then a totally blank card is produced.
c overwrite
f OVERWRITE = LOGICAL (Given)
c If non-zero,
f If .TRUE.,
* the new card over-writes the current card, and the current card is
* incremented to refer to the next card (see the "Card" attribute). If
c zero,
f .FALSE.,
* the new card is inserted in front of the current card and the current
* card is left unchanged. In either case, if the current card on entry
* points to the "end-of-file", the new card is appended to the end of
* the list.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Notes:
* - If, on exit, there are no cards following the card written by
* this function, then the current card is left pointing at the
* "end-of-file".
*--
*/
/* Just call astSetFitsCom with a blank keyword name. */
astSetFitsCom( this, "", comment, overwrite );
}
static void SetFitsCom( AstFitsChan *this, const char *name,
const char *comment, int overwrite, int *status ){
/*
*+
* Name:
* astSetFitsCom
* Purpose:
* Store a comment for a keyword in a FitsChan.
* Type:
* Protected virtual function.
* Synopsis:
* #include "fitschan.h"
* void astSetFitsCom( AstFitsChan *this, const char *name,
* const char *comment, int overwrite )
* Class Membership:
* FitsChan method.
* Description:
* This function replaces the comment within an existing card, or
* stores a new comment card within a FitsChan.
* Parameters:
* this
* Pointer to the FitsChan.
* name
* A pointer to a
* string holding the keyword name. This may be a complete FITS
* header card, in which case the keyword to use is extracted from
* it. No more than 80 characters are read from this string.
* comment
* A pointer to a
* string holding a comment to associated with the keyword.
* If a NULL or
* blank string is supplied, any existing comment associated with
* the keyword is removed.
* overwrite
* If non-zero, the new comment replaces the comment in the current
* card, and the current card is then incremented to refer to the next
* card. If zero, a new comment card is inserted in front of the current
* card and the current card is left unchanged. In either case, if the
* current card on entry points to the "end-of-file", the new card is
* appended to the end of the list.
* Notes:
* - When replacing an existing comment, any existing keyword value is
* retained only if the supplied keyword name is the same as the keyword
* name in the current card. If the keyword names are different, then
* the new name replaces the old name, and any existing keyword data value
* is deleted. The card thus becomes a comment card with the supplied
* keyword name and comment, but no data value.
* - If, on exit, there are no cards following the card written by
* this function, then the current card is left pointing at the
* "end-of-file".
* - The current card can be set explicitly before calling this function
* either by assigning a value to the Card attribute (if the index of the
* required card is already known), or using astFindFits (if only the
* keyword name is known).
* - An error will be reported if the keyword name does not conform
* to FITS requirements.
*-
*/
/* Local variables: */
const char *class; /* Pointer to object class string */
const char *method; /* Pointer to calling method string */
const char *cname; /* The existing keyword name */
const char *com; /* The comment to use */
char *lcom; /* Supplied keyword comment */
char *lname; /* Supplied keyword name */
char *lvalue; /* Supplied keyword value */
void *old_data; /* Pointer to the old data value */
void *data; /* Pointer to data value to be stored */
size_t size; /* The size of the data value */
/* Check the global error status. */
if ( !astOK ) return;
/* Initialisation */
size = 0;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Store the calling method and object class. */
method = "astSetFitsCom";
class = astGetClass( this );
/* Extract the keyword name, etc, from the supplied string. */
(void) Split( this, name, &lname, &lvalue, &lcom, method, class, status );
/* If a blank comment has been supplied, use NULL instead. */
com = ChrLen( comment, status )? comment : NULL;
/* If we are inserting a new card, or over-writing an old card with a
different name, create and store a comment card with the given keyword
name and comment, but no data value. */
cname = CardName( this, status );
if( !overwrite || !cname || strcmp( lname, cname ) ){
InsCard( this, overwrite, lname, AST__COMMENT, NULL, com, method, class, status );
/* If we are overwriting an existing keyword comment, use the data type
and value from the existing current card. Note, we have to take a copy
of the old data value because InsCard over-writes by deleting the old
card and then inserting a new one. */
} else {
old_data = CardData( this, &size, status );
data = astStore( NULL, old_data, size );
InsCard( this, 1, lname, CardType( this, status ), data, com, method, class, status );
data = astFree( data );
}
/* Release the memory used to hold keyword name, value and comment strings. */
lname = (char *) astFree( (void *) lname );
lvalue = (char *) astFree( (void *) lvalue );
lcom = (char *) astFree( (void *) lcom );
}
static void FixNew( AstFitsChan *this, int flag, int remove,
const char *method, const char *class, int *status ){
/*
*
* Name:
* FixNew
* Purpose:
* Remove "new" flags from the whole FitsChan, and optionally remove
* "new" cards.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void FixNew( AstFitsChan *this, int flag, int remove,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan method.
* Description:
* This function searches the entire FitsChan for cards which are
* marked as new using the supplied flag (NEW1 or NEW2). If "remove"
* is non-zero, these cards are completely removed from the FitsChan
* (not just marked as used). If "remove" is zero, they are retained
* and the specified flag is cleared.
* Parameters:
* this
* Pointer to the FitsChan.
* flag
* The flag to use; NEW1 or NEW2.
* remove
* Remove flagged cards from the FitsChan?
* 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.
* Notes:
* - This function attempts to execute even if an error has occurred.
* - If any cards are removed, the current Card is left at "end-of-file"
* on exit. If no cards are removed, the original current card is
* retained.
*-
*/
/* Local Variables: */
int *flags; /* Pointer to flags mask for the current card */
int icard; /* Index of current card on entry */
int ndeleted; /* Number of cards deleted by this call */
/* Return if no FitsChan was supplied, or if the FitsChan is empty. */
if ( !this || !this->head ) return;
/* Save the current card index, and rewind the FitsChan. */
icard = astGetCard( this );
astClearCard( this );
/* Indicate no cards have yet been deleted. */
ndeleted = 0;
/* Loop through the list of FitsCards in the FitsChan until the final
card is reached. */
while( astOK && this->card ){
/* Get a pointer to the flags mask for this card. */
flags = CardFlags( this, status );
/* See if the Card has been marked with the requeste new flag. */
if( flags && ( (*flags) & flag ) ) {
/* If requested, remove the card. This will automatically move the
current card on to the next card. */
if( remove ){
DeleteCard( this, method, class, status );
ndeleted++;
/* Otherwise, clear the flag. */
} else {
*flags = (*flags) & ~flag;
/* Increment the card count and move on to the next card. */
MoveCard( this, 1, method, class, status );
}
/* Move on to the next card if this card is not marked with the requested
new flag. */
} else {
MoveCard( this, 1, method, class, status );
}
}
/* If no cards were removed, we can safely re-instate the original
current card. Otherwise, the current card is left at "end-of-file". */
if( ndeleted == 0 ) astSetCard( this, icard );
/* Return */
return;
}
static void FixUsed( AstFitsChan *this, int reset, int used, int remove,
const char *method, const char *class, int *status ){
/*
*
* Name:
* FixUsed
* Purpose:
* Remove "provisionally used" flags from the whole FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void FixUsed( AstFitsChan *this, int reset, int used, int remove,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan method.
* Description:
* This function searches the entire FitsChan for cards which are
* marked as "provisionally used". The "provisionally used" flag is
* cleared for each such card. In addition, if "used" is non-zero then
* each such card is flagged as having been "definitely used". If
* "remove" is non-zero, then all "provisionally used" cards are deleted
* from the FitsChan.
* Parameters:
* this
* Pointer to the FitsChan.
* reset
* Set all cards so that they are neither provisionally used or
* definitely used. In this case neither the "used" nor the
* "remove" parameter are accssed.
* used
* Have the provisionally used cards definitely been used?
* remove
* Should provisionally used cards be deleted?
* 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.
* Notes:
* - This function attempts to execute even if an error has occurred.
*-
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
FitsCard *card0; /* Pointer to current FitsCard */
int *flags; /* Pointer to flags mask for the current card */
int old_ignore_used; /* Original value of variable ignore_used */
int old_status; /* Original inherited status value */
int rep; /* Original error reporting flag */
/* Return if no FitsChan was supplied, or if the FitsChan is empty. */
if ( !this || !this->head ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this);
/* Temporarily clear any bad status value and supress error reporting in
this function. */
old_status = astStatus;
astClearStatus;
rep = astReporting( 0 );
/* Indicate that we should not skip over cards marked as having been
read. */
old_ignore_used = ignore_used;
ignore_used = 0;
/* Save a pointer to the current card, and the reset the current card to
be the first card. */
card0 = this->card;
astClearCard( this );
/* Loop through the list of FitsCards in the FitsChan until the final
card is reached. */
while( this->card ){
/* Get a pointer to the flags mask for this card. */
flags = CardFlags( this, status );
/* Reset both used flags if required. */
if( reset ) {
*flags = (*flags) & ~PROVISIONALLY_USED;
*flags = (*flags) & ~USED;
MoveCard( this, 1, method, class, status );
/* Otherwise perform the actions indicated by parameters "used" and
"remove". */
} else {
/* See if the Card has been provisionally used. */
if( flags && ( (*flags) & PROVISIONALLY_USED ) ) {
/* Clear the provisionally used flag. */
*flags = (*flags) & ~PROVISIONALLY_USED;
/* If required, set the definitely used flag. */
if( used ) *flags = (*flags) | USED;
/* If required, delete the card. The next card is made current. If we are
about to delete the original current card, we need to update the
pointer to the card to be made current at the end of this function.
If we end up back at the head of the chain, indicate that we have
reached the end of file by setting card0 NULL. */
if( remove ) {
if( card0 == this->card && card0 ) {
card0 = ( (FitsCard *) this->card )->next;
if( (void *) card0 == this->head ) card0 = NULL;
}
DeleteCard( this, method, class, status );
/* Otherwise, just move on to the next card. */
} else {
MoveCard( this, 1, method, class, status );
}
/* If this card has not bee provisionally used, move on to the next card. */
} else {
MoveCard( this, 1, method, class, status );
}
}
}
/* Re-instate the original current card. */
this->card = card0;
/* If this card is now flagged as definitely used, move forward to the
next un-used card. */
flags = CardFlags( this, status );
if( flags && (*flags & USED ) ) {
ignore_used = 1;
MoveCard( this, 1, method, class, status );
}
/* Re-instate the original flag indicating if cards marked as having been
read should be skipped over. */
ignore_used = old_ignore_used;
/* Re-instate the original status value and error reporting condition. */
astReporting( rep );
astSetStatus( old_status );
}
static void FormatCard( AstFitsChan *this, char *buf, const char *method, int *status ){
/*
*
* Name:
* FormatCard
* Purpose:
* Formats the current card.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void FormatCard( AstFitsChan *this, char *buf, const char *method, int *status )
* Class Membership:
* FitsChan method.
* Description:
* This function write the current card into the supplied character
* buffer as a complete FITS header card.
* Parameters:
* this
* Pointer to the FitsChan.
* buf
* A character string into which the header card is written. This
* should be at least 81 characters long. The returned string is
* padded with spaces upto column 80. A terminating null character
* is added.
* method
* Pointer to a string holding the name of the calling method.
* This is only for use in constructing error messages.
* status
* Pointer to the inherited status variable.
* Notes:
* - An error is reported if the requested header card does not conform to
* FITS standards.
*
*/
/* Local Variables: */
const char *com; /* Pointer to comment string to use */
int comlen; /* Length of comment string */
int comstart; /* Column in which to start comment */
int i; /* Loop counter for characters */
int len; /* Output string length */
int digits; /* No. of digits to use when formatting floating point values */
int type; /* Card data type */
/* Check the global error status, and check the current card is defined. */
if ( !astOK || astFitsEof( this ) ) return;
/* Get a pointer to the comment to use and determine its length. */
com = CardComm( this, status );
comlen = ChrLen( com, status );
/* Copy the keyword name to the start of the output buffer, and store
its length. */
len = (int) strlen( strcpy( buf, CardName( this, status ) ) );
/* Pad the name with spaces up to column 8. */
while ( len < FITSNAMLEN ) buf[ len++ ] = ' ';
/* If the card contains a keyword value... */
type = CardType( this, status );
if( type != AST__COMMENT ){
/* Get the number of digits to use when formatting floating point values. */
digits = astGetFitsDigits( this );
/* Put an equals sign in column 9 (or a space if the keyword is a CONTINUE
card), followed by a space in column 10. */
buf[ len++ ] = ( type == AST__CONTINUE ) ? ' ' : '=';
buf[ len++ ] = ' ';
/* Format and store the keyword value, starting at column 11 and update the
output string length. */
len += EncodeValue( this, buf + len, FITSNAMLEN + 3, digits,
method, status );
/* If there is a comment, determine which column it should start in so that
it ends in column 80. */
if( com ){
comstart = AST__FITSCHAN_FITSCARDLEN - ( comlen - 2 ) + 1;
/* Adjust the starting column to 32 if possible, avoiding over-writing
the value, or running off the end of the card unless this is
unavoidable. */
if ( comstart > FITSCOMCOL ) comstart = FITSCOMCOL;
if ( comstart < len + 2 ) comstart = len + 2;
/* Pad the output buffer with spaces up to the start of the comment. */
while ( len < comstart - 1 ) buf[ len++ ] = ' ';
/* Then append "/ " to introduce the comment, truncating if the card
length forces this. */
for ( i = 0; ( i < 2 ) && ( len < AST__FITSCHAN_FITSCARDLEN ); i++ ) {
buf[ len++ ] = "/ "[ i ];
}
}
}
/* Append any comment, truncating it if the card length forces
this. */
if ( com ) {
for ( i = 0; com[ i ] && ( len < AST__FITSCHAN_FITSCARDLEN ); i++ ) {
buf[ len++ ] = com[ i ];
}
}
/* Pad with spaces up to the end of the card. */
while ( len < AST__FITSCHAN_FITSCARDLEN ) buf[ len++ ] = ' ';
/* Terminate it. */
buf[ AST__FITSCHAN_FITSCARDLEN ] = 0;
}
static int FullForm( const char *list, const char *test, int abbrev, int *status ){
/*
* Name:
* FullForm
* Purpose:
* Identify the full form of an option string.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int FullForm( const char *list, const char *test, int abbrev, int *status )
* Class Membership:
* FitsChan method.
* Description:
* This function identifies a supplied test option within a supplied
* list of valid options, and returns the index of the option within
* the list. The test option may be abbreviated, and case is
* insignificant.
* Parameters:
* list
* A list of space separated option strings.
* test
* A candidate option string.
* abbrev
* 1 if abbreviations are to be accepted. Zero otherwise.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The index of the identified option within the supplied list, starting
* at zero. -1 is returned if the option is not recognised, and (if
* abbrev is 1 ) -2 if the option is ambiguous (no errors are reported
* in these cases). If abbrev is zero, the returned index will be the
* index of the first matching string.
*
* Notes:
* - A value of -1 is returned if an error has already occurred, or
* if this function should fail for any reason.
*/
/* Local Variables: */
char *context; /* Context used by strtok_r */
char *llist; /* Pointer to a local copy of the options list */
char *option; /* Pointer to the start of the next option */
int i; /* Current option index */
int len; /* Length of supplied option */
int nmatch; /* Number of matching options */
int ret; /* The returned index */
/* Initialise the answer to indicate that the option has not been
identified. */
ret = -1;
/* Avoid compiler warnings. */
context = NULL;
/* Check global status. */
if( !astOK ) return ret;
/* Take a local copy of the supplied options list. This is necessary since
"strtok" modified the string by inserting null characters. */
llist = (char *) astStore( NULL, (void *) list, strlen(list) + 1 );
if( astOK ){
/* Save the number of characters in the supplied test option (excluding
trailing spaces). */
len = ChrLen( test, status );
/* Compare the supplied test option against each of the known options in
turn. Count the number of matches. */
nmatch = 0;
#if HAVE_STRTOK_R
option = strtok_r( llist, " ", &context );
#else
option = strtok( llist, " " );
#endif
i = 0;
while( option ){
/* If every character in the supplied label matches the corresponding
character in the current test label we have a match. Increment the
number of matches and save the current item index. If abbreviation is
not allowed ensure that the lengths of the strings are equal. */
if( !Ustrncmp( test, option, len, status ) && ( abbrev ||
len == ChrLen( option, status ) ) ) {
nmatch++;
ret = i;
if( !abbrev ) break;
}
/* Get a pointer to the next option. */
#if HAVE_STRTOK_R
option = strtok_r( NULL, " ", &context );
#else
option = strtok( NULL, " " );
#endif
i++;
}
/* Return -1 if no match was found. */
if( !nmatch ){
ret = -1;
/* Return -2 if the option was ambiguous. */
} else if( abbrev && nmatch > 1 ){
ret = -2;
}
/* Free the local copy of the options list. */
llist = (char *) astFree( (void *) llist );
}
/* Return the answer. */
return ret;
}
static const char *GetAllWarnings( AstFitsChan *this, int *status ){
/*
*+
* Name:
* astGetAllWarnings
* Purpose:
* Return a list of all condition names.
* Type:
* Protected virtual function.
* Synopsis:
* #include "fitschan.h"
* const char *GetAllWarnings( AstFitsChan *this )
* Class Membership:
* FitsChan method.
* Description:
* This function returns a space separated lits of the condition names
* currently recognized by the Warnings attribute.
* Parameters:
* this
* Pointer to the FitsChan.
* Returned Value:
* A pointer to a static string holding the condition names.
* Notes:
* - This routine does not check the inherited status.
*-
*/
/* Return the result. */
return ALLWARNINGS;
}
const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
* Name:
* GetAttrib
* Purpose:
* Get the value of a specified attribute for a FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* const char *GetAttrib( AstObject *this, const char *attrib, int *status )
* Class Membership:
* FitsChan 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 FitsChan, formatted as a character string.
* Parameters:
* this
* Pointer to the FitsChan.
* 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 FitsChan, 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 FitsChan. 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 */
AstFitsChan *this; /* Pointer to the FitsChan 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 FitsChan structure. */
this = (AstFitsChan *) this_object;
/* Card. */
/* ----- */
if ( !strcmp( attrib, "card" ) ) {
ival = astGetCard( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", ival );
result = getattrib_buff;
}
/* CardComm. */
/* --------- */
} else if ( !strcmp( attrib, "cardcomm" ) ) {
result = astGetCardComm( this );
/* CardName. */
/* --------- */
} else if ( !strcmp( attrib, "cardname" ) ) {
result = astGetCardName( this );
/* CardType. */
/* --------- */
} else if ( !strcmp( attrib, "cardtype" ) ) {
ival = astGetCardType( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", ival );
result = getattrib_buff;
}
/* Encoding. */
/* --------- */
} else if ( !strcmp( attrib, "encoding" ) ) {
ival = astGetEncoding( this );
if ( astOK ) {
if( ival == NATIVE_ENCODING ){
result = NATIVE_STRING;
} else if( ival == FITSPC_ENCODING ){
result = FITSPC_STRING;
} else if( ival == FITSIRAF_ENCODING ){
result = FITSIRAF_STRING;
} else if( ival == FITSAIPS_ENCODING ){
result = FITSAIPS_STRING;
} else if( ival == FITSAIPSPP_ENCODING ){
result = FITSAIPSPP_STRING;
} else if( ival == FITSCLASS_ENCODING ){
result = FITSCLASS_STRING;
} else if( ival == FITSWCS_ENCODING ){
result = FITSWCS_STRING;
} else if( ival == DSS_ENCODING ){
result = DSS_STRING;
} else {
result = UNKNOWN_STRING;
}
}
/* CDMatrix */
/* -------- */
} else if ( !strcmp( attrib, "cdmatrix" ) ) {
ival = astGetCDMatrix( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", ival );
result = getattrib_buff;
}
/* DefB1950 */
/* -------- */
} else if ( !strcmp( attrib, "defb1950" ) ) {
ival = astGetDefB1950( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", ival );
result = getattrib_buff;
}
/* TabOK */
/* ----- */
} else if ( !strcmp( attrib, "tabok" ) ) {
ival = astGetTabOK( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", ival );
result = getattrib_buff;
}
/* CarLin */
/* ------ */
} else if ( !strcmp( attrib, "carlin" ) ) {
ival = astGetCarLin( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", ival );
result = getattrib_buff;
}
/* PolyTan */
/* ------- */
} else if ( !strcmp( attrib, "polytan" ) ) {
ival = astGetPolyTan( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", ival );
result = getattrib_buff;
}
/* Iwc */
/* --- */
} else if ( !strcmp( attrib, "iwc" ) ) {
ival = astGetIwc( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", ival );
result = getattrib_buff;
}
/* Clean */
/* ----- */
} else if ( !strcmp( attrib, "clean" ) ) {
ival = astGetClean( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", ival );
result = getattrib_buff;
}
/* FitsAxisOrder. */
/* -------------- */
} else if ( !strcmp( attrib, "fitsaxisorder" ) ) {
result = astGetFitsAxisOrder( this );
/* FitsDigits. */
/* ----------- */
} else if ( !strcmp( attrib, "fitsdigits" ) ) {
ival = astGetFitsDigits( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", ival );
result = getattrib_buff;
}
/* Ncard. */
/* ------ */
} else if ( !strcmp( attrib, "ncard" ) ) {
ival = astGetNcard( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", ival );
result = getattrib_buff;
}
/* Nkey. */
/* ----- */
} else if ( !strcmp( attrib, "nkey" ) ) {
ival = astGetNkey( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", ival );
result = getattrib_buff;
}
/* AllWarnings */
/* ----------- */
} else if ( !strcmp( attrib, "allwarnings" ) ) {
result = astGetAllWarnings( this );
/* Warnings. */
/* -------- */
} else if ( !strcmp( attrib, "warnings" ) ) {
result = astGetWarnings( 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 GetCard( AstFitsChan *this, int *status ){
/*
*+
* Name:
* astGetCard
* Purpose:
* Get the value of the Card attribute.
* Type:
* Protected virtual function.
* Synopsis:
* #include "fitschan.h"
* int astGetCard( AstFitsChan *this )
* Class Membership:
* FitsChan method.
* Description:
* This function returns the value of the Card attribute for the supplied
* FitsChan. This is the index of the next card to be read from the
* FitsChan. The index of the first card is 1. If there are no more
* cards to be read, a value one greater than the number of cards in the
* FitsChan is returned.
* Parameters:
* this
* Pointer to the FitsChan.
* Returned Value:
* The index of the next card to be read.
* Notes:
* - A value of zero will be returned if the current card is not defined.
* - This function attempts to execute even if an error has occurred.
*-
*/
/* Local Variables: */
const char *class; /* Pointer to class string */
const char *method; /* Pointer to method string */
FitsCard *card0; /* Pointer to current FitsCard */
int index; /* Index of next FitsCard */
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Return if no FitsChan was supplied, or if the FitsChan is empty. */
if ( !this || !this->head ) return 0;
/* Store the method and object class. */
method = "astGetCard";
class = astGetClass( this );
/* Save a pointer to the current card, and the reset the current card to
be the first card. */
card0 = this->card;
astClearCard( this );
/* Count through the list of FitsCards in the FitsChan until the original
current card is reached. If the current card is not found (for instance
if it has been marked as deleted and we are currently skipping such cards),
this->card will be left null (end-of-file). */
index = 1;
while( this->card != card0 && astOK && this->card ){
/* Increment the card count and move on to the next card. */
index++;
MoveCard( this, 1, method, class, status );
}
/* Return the card index. */
return index;
}
static const char *GetCardComm( AstFitsChan *this, int *status ){
/*
*+
* Name:
* GetCardComm
* Purpose:
* Get the value of the CardComm attribute.
* Type:
* Protected virtual function.
* Synopsis:
* #include "fitschan.h"
* const char *astGetCardComm( AstFitsChan *this)
* Class Membership:
* FitsChan method.
* Description:
* This function returns the value of the CardComm attribute for the
* supplied FitsChan. This is the comment for the current card.
* Parameters:
* this
* Pointer to the FitsChan.
* Returned Value:
* A pointer to a static string holding the comment. A zero-length
* string is returned if the card has no comment.
* Notes:
* - A value of NULL will be returned if an error has already
* occurred, or if this function should fail for any reason.
*-
*/
/* Local Variables */
const char *result = NULL;
/* Check inherited status */
if( !astOK ) return result;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Get the comment for the current card. */
result = CardComm( this, status );
/* Return a zero-length string if the card has no comment. */
if( astOK && !result ) result = "";
/* Return the comment. */
return result;
}
static const char *GetCardName( AstFitsChan *this, int *status ){
/*
*+
* Name:
* GetCardName
* Purpose:
* Get the value of the CardName attribute.
* Type:
* Protected virtual function.
* Synopsis:
* #include "fitschan.h"
* const char *astGetCardName( AstFitsChan *this)
* Class Membership:
* FitsChan method.
* Description:
* This function returns the value of the CardName attribute for the
* supplied FitsChan. This is the keyword name for the current card.
* Parameters:
* this
* Pointer to the FitsChan.
* Returned Value:
* A pointer to a static string holding the keyword name.
* Notes:
* - A value of NULL will be returned if an error has already
* occurred, or if this function should fail for any reason.
*-
*/
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Return the keyword name of the current card. */
return CardName( this, status );
}
static int GetCardType( AstFitsChan *this, int *status ){
/*
*+
* Name:
* GetCardType
* Purpose:
* Get the value of the CardType attribute.
* Type:
* Protected virtual function.
* Synopsis:
* #include "fitschan.h"
* int astGetCardType( AstFitsChan *this )
* Class Membership:
* FitsChan method.
* Description:
* This function returns the value of teh CardType attribute for the supplied
* FitsChan. This is the data type of the keyword value for the current card.
* Parameters:
* this
* Pointer to the FitsChan.
* Returned Value:
* An integer representing the data type of the current card.
* Notes:
* - A value of AST__NOTYPE will be returned if an error has already
* occurred, or if this function should fail for any reason.
*-
*/
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Return the data type of the current card. */
return CardType( this, status );
}
static int GetFull( AstChannel *this_channel, int *status ) {
/*
* Name:
* GetFull
* Purpose:
* Obtain the value of the Full attribute for a FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int GetFull( AstChannel *this, int *status )
* Class Membership:
* FitsChan member function (over-rides the protected astGetFull
* method inherited from the Channel class).
* Description:
* This function return the integer value of the Full attribute for
* a FitsChan.
* Parameters:
* this
* Pointer to the FitsChan.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The Full attribute value.
* Notes:
* - This function modifies the default Full value from 0 to -1 for
* the benefit of the FitsChan class. This prevents non-essential
* information being written by the astWrite method unless it is
* requested by explicitlt setting a Full value.
* - 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: */
AstFitsChan *this; /* Pointer to the FitsChan structure */
int result; /* Result value to return */
/* Check the global error status. */
if ( !astOK ) return 0;
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_channel;
/* If the Full attribute us set, obtain its value using the parent class
method. */
if ( astTestFull( this ) ) {
result = (* parent_getfull)( this_channel, status );
/* Otherwise, supply a default value of -1. */
} else {
result = -1;
}
/* Return the result. */
return result;
}
static FitsCard *GetLink( FitsCard *card, int next, const char *method,
const char *class, int *status ){
/*
* Name:
* GetLink
* Purpose:
* Get a pointer to the next or previous card in the list.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* FitsCard *GetLink( FitsCard *card, int next, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Returns the a pointer to either the next or previous FitsCard
* structure in the circular linked list of such structures stored in a
* FitsChan. A check is performed to ensure that the forward and
* backward links from the supplied card are consistent and an error
* is reported if they are not (so long as no previous error has been
* reported). Memory corruption can result in inconsistent links
* which can result in infinite loops if an attempt is made to scan the
* list.
* Parameters:
* card
* The current card.
* next
* If non-zero, a pointer to the "next" card is returned. Otherwise
* a pointer to the "previous" card is returned.
* method
* Pointer to string holding the name of the calling method.
* class
* Pointer to string holding the object class.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the required card, or NULL if an error occurs.
* Notes:
* - This function attempts to execute even if an error has occurred.
*/
/* Local Variables: */
FitsCard *ret; /* Pointer to the returned card */
/* Check that the "next" link from the previous card points back to
the current card, and that the "prev" link from the next card points
back to the current card. */
if( card && ( card->prev->next != card ||
card->next->prev != card ) ){
/* Report an error so long as no previous error has been reported, and
return a NULL pointer. */
if( astOK ){
astError( AST__FCRPT, "%s(%s): A corrupted %s object has been "
"supplied.", status, method, class, class );
}
ret = NULL;
/* If the links are good, return a pointer to the required card. */
} else {
ret = next ? card->next : card->prev;
}
/* Return the result. */
return ret;
}
static int GetNcard( AstFitsChan *this, int *status ){
/*
*+
* Name:
* astGetNcard
* Purpose:
* Get the value of the Ncard attribute.
* Type:
* Protected virtual function.
* Synopsis:
* #include "fitschan.h"
* int astGetNcard( AstFitsChan *this )
* Class Membership:
* FitsChan method.
* Description:
* This function returns the value of the Ncard attribute for the supplied
* FitsChan. This is the number of cards currently in the FitsChan.
* Parameters:
* this
* Pointer to the FitsChan.
* Returned Value:
* The number of cards currently in the FitsChan.
* Notes:
* - A value of zero will be returned if an error has already
* occurred, or if this function should fail for any reason.
*-
*/
/* Local Variables: */
const char *class; /* Pointer to class string */
const char *method; /* Pointer to method string */
FitsCard *card0; /* Pointer to current card on entry */
int ncard; /* Number of cards so far */
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Return zero if an error has already occurred, or no FitsChan was supplied,
or the FitsChan is empty. */
if ( !astOK || !this || !this->head ) return 0;
/* Store the method and object class. */
method = "astGetNcard";
class = astGetClass( this );
/* Save a pointer to the current card, and then reset the current card to
be the first card. */
card0 = this->card;
astClearCard( this );
/* Count through the cards in the FitsChan until the end of file is reached. */
ncard = 0;
while( astOK && this->card ){
/* Increment the card count and move on to the next card. */
ncard++;
MoveCard( this, 1, method, class, status );
}
/* Reset the current card to be the original current card. */
this->card = card0;
/* Return the result. */
return astOK ? ncard : 0;
}
static int GetNkey( AstFitsChan *this, int *status ){
/*
*+
* Name:
* astGetNkey
* Purpose:
* Get the value of the Nkey attribute.
* Type:
* Protected virtual function.
* Synopsis:
* #include "fitschan.h"
* int astGetNkey( AstFitsChan *this )
* Class Membership:
* FitsChan method.
* Description:
* This function returns the value of the Nkey attribute for the supplied
* FitsChan. This is the number of unique keywords currently in the
* FitsChan.
* Parameters:
* this
* Pointer to the FitsChan.
* Returned Value:
* The number of unique keywords currently in the FitsChan.
* Notes:
* - A value of zero will be returned if an error has already
* occurred, or if this function should fail for any reason.
*-
*/
/* Local Variables: */
AstKeyMap *km; /* KeyMap holding unique keyword names */
FitsCard *card0; /* Pointer to current card on entry */
const char *class; /* Pointer to class string */
const char *method; /* Pointer to method string */
int nkey; /* Returned Nkey value */
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Return zero if an error has already occurred, or no FitsChan was supplied,
or the FitsChan is empty. */
if ( !astOK || !this || !this->head ) return 0;
/* Store the method and object class. */
method = "astGetNkey";
class = astGetClass( this );
/* Create an empty KeyMap to hold the unused keyword names */
km = astKeyMap( " ", status );
/* Save a pointer to the current card, and then reset the current card to
be the first card. */
card0 = this->card;
astClearCard( this );
/* Loop through the cards in the FitsChan until the end of file is reached. */
while( astOK && this->card ){
/* Get the keyword name for the current card and add it to the keymap. */
astMapPut0I( km, CardName( this, status ), 0, NULL );
/* Move on to the next unused card. */
MoveCard( this, 1, method, class, status );
}
/* Reset the current card to be the original current card. */
this->card = card0;
/* Get the number of keywords. */
nkey = astMapSize( km );
/* Annull the KeyMap . */
km = astAnnul( km );
/* Return the result. */
return astOK ? nkey : 0;
}
static void GetNextData( AstChannel *this_channel, int skip, char **name,
char **val, int *status ) {
/*
* Name:
* GetNextData
* Purpose:
* Read the next item of data from a data source.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void GetNextData( AstChannel *this, int skip, char **name, char **val )
* Class Membership:
* FitsChan member function (over-rides the protected
* astGetNextData method inherited from the Channel class).
* Description:
* This function reads the next item of input data from a data
* source associated with a FitsChan and returns the result. It
* decodes the data item and returns name/value pairs ready for
* use.
* Parameters:
* this
* Pointer to the FitsChan.
* skip
* A non-zero value indicates that a new Object is to be read,
* and that all input data up to the next "Begin" item are to be
* skipped in order to locate it. This is useful if the data
* source contains AST objects interspersed with other data (but
* note that these other data cannot appear inside AST Objects,
* only between them).
*
* A zero value indicates that all input data are significant
* and the next item will therefore be read and an attempt made
* to interpret it whatever it contains. Any other data
* inter-mixed with AST Objects will then result in an error.
* name
* An address at which to store a pointer to a null-terminated
* dynamically allocated string containing the name of the next
* item in the input data stream. This name will be in lower
* case with no surrounding white space. It is the callers
* responsibilty to free the memory holding this string (using
* astFree) when it is no longer required.
*
* A NULL pointer value will be returned (without error) to
* indicate when there are no further input data items to be
* read.
* val
* An address at which to store a pointer to a null-terminated
* dynamically allocated string containing the value associated
* with the next item in the input data stream. No case
* conversion is performed on this string and all white space is
* potentially significant. It is the callers responsibilty to
* free the memory holding this string (using astFree) when it
* is no longer required.
*
* The returned pointer will be NULL if an Object data item is
* read (see the "Data Representation" section).
* Data Representation:
* The returned data items fall into the following categories:
*
* - Begin: Identified by the name string "begin", this indicates
* the start of an Object definition. The associated value string
* gives the class name of the Object being defined.
*
* - IsA: Identified by the name string "isa", this indicates the
* end of the data associated with a particular class structure
* within the definiton of a larger Object. The associated value
* string gives the name of the class whose data have just been
* read.
*
* - End: Identified by the name string "end", this indicates the
* end of the data associated with a complete Object
* definition. The associated value string gives the class name of
* the Object whose definition is being ended.
*
* - Non-Object: Identified by any other name string plus a
* non-NULL "val" pointer, this gives the value of a non-Object
* structure component (instance variable). The name identifies
* which instance variable it is (within the context of the class
* whose data are being read) and the value is encoded as a string.
*
* - Object: Identified by any other name string plus a NULL "val"
* pointer, this identifies the value of an Object structure
* component (instance variable). The name identifies which
* instance variable it is (within the context of the class whose
* data are being read) and the value is given by subsequent data
* items (so the next item should be a "Begin" item).
* Notes:
* - NULL pointer values will be returned if this function is
* invoked with the global error status set, or if it should fail
* for any reason.
*/
/* Local Constants: */
#define BUFF_LEN 100 /* Length of formatting buffer */
/* Local Variables: */
AstFitsChan *this; /* Pointer to the FitsChan structure */
char *keyword; /* Pointer to current keyword string */
char *newdata; /* Pointer to stripped string value */
char *upq; /* Pointer to unprequoted string */
char buff[ BUFF_LEN + 1 ]; /* Buffer for formatting values */
const char *class; /* Pointer to object class */
const char *method; /* Pointer to method name */
int cont; /* String ends with an ampersand? */
int done; /* Data item found? */
int freedata; /* Should the data pointer be freed? */
int i; /* Loop counter for keyword characters */
int len; /* Length of current keyword */
int nc; /* Number of characters read by "astSscanf" */
int nn; /* No. of characters after UnPreQuoting */
int type; /* Data type code */
void *data; /* Pointer to current data value */
/* Initialise the returned pointer values. */
*name = NULL;
*val = NULL;
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_channel;
/* Store the method name and object class. */
method = "astRead";
class = astGetClass( this );
/* Loop to consider successive cards stored in the FitsChan (starting
at the "current" card) until a valid data item is read or "end of
file" is reached. Also quit the loop if an error occurs. */
done = 0;
newdata = NULL;
while ( !done && !astFitsEof( this ) && astOK ){
/* Obtain the keyword string, data type code and data value pointer
from the current card. */
keyword = CardName( this, status );
type = CardType( this, status );
data = CardData( this, NULL, status );
/* Mark all cards as having been used unless we are skipping over cards which
may not be related to AST. */
if( !skip ) MarkCard( this, status );
/* Ignore comment cards. */
if ( type != AST__COMMENT ) {
/* Native encoding requires trailing white space to be removed from
string values (so that null strings can be distinguished from blank
strings). Do this now. */
freedata = 0;
if ( ( type == AST__STRING || type == AST__CONTINUE ) && data ){
newdata = (char *) astStore( NULL, data, strlen( (char *) data ) + 1 );
if( newdata ){
newdata[ ChrLen( data, status ) ] = 0;
data = (void *) newdata;
freedata = 1;
}
}
/* Obtain the keyword length and test the card to identify the type of
AST data item (if any) that it represents. */
len = (int) strlen( keyword );
/* "Begin" item. */
/* ------------- */
/* This is identified by a string value and a keyword of the form
"BEGASTxx", where "xx" are characters encoding a sequence
number. */
if ( ( type == AST__STRING ) &&
( nc = 0,
( 0 == astSscanf( keyword, "BEGAST"
"%*1[" SEQ_CHARS "]"
"%*1[" SEQ_CHARS "]%n", &nc ) )
&& ( nc >= len ) ) ) {
/* Note we have found a data item. */
done = 1;
/* Set the returned name to "begin" and extract the associated class
name from the string value. Store both of these in dynamically
allocated strings. */
*name = astString( "begin", 5 );
*val = UnPreQuote( (const char *) data, status );
/* Indicate that the current card has been used. */
MarkCard( this, status );
/* The "begin" item will be preceded by a header of COMMENT cards. Mark
them as having been used. */
ComBlock( this, -1, method, class, status );
/* "IsA" item. */
/* ----------- */
/* This is identified by a string value and a keyword of the form
"ISAxx", where "xx" are characters encoding a sequence
number. Don't accept the item if we are skipping over cards looking
for a "Begin" item. */
} else if ( !skip &&
( type == AST__STRING ) &&
( nc = 0,
( 0 == astSscanf( keyword,
"ISA"
"%*1[" SEQ_CHARS "]"
"%*1[" SEQ_CHARS "]%n", &nc ) )
&& ( nc >= len ) ) ) {
/* Note we have found a data item. */
done = 1;
/* Set the returned name to "isa" and extract the associated class
name from the string value. Store both of these in dynamically
allocated strings. */
*name = astString( "isa", 3 );
*val = UnPreQuote( (const char *) data, status );
/* "End" item. */
/* ----------- */
/* This is identified by a string value and a keyword of the form
"ENDASTxx", where "xx" are characters encoding a sequence
number. Don't accept the item if we are skipping over cards looking
for a "Begin" item. */
} else if ( !skip &&
( type == AST__STRING ) &&
( nc = 0,
( 0 == astSscanf( keyword,
"ENDAST"
"%*1[" SEQ_CHARS "]"
"%*1[" SEQ_CHARS "]%n", &nc ) )
&& ( nc >= len ) ) ) {
/* Note we have found a data item. */
done = 1;
/* Set the returned name to "end" and extract the associated class
name from the string value. Store both of these in dynamically
allocated strings. */
*name = astString( "end", 3 );
*val = UnPreQuote( (const char *) data, status );
/* The "end" item eill be followed by a footer of COMMENT cards. Mark
these cards as having been used. */
ComBlock( this, 1, method, class, status );
/* Object or data item. */
/* -------------------- */
/* These are identified by a string, int, or double value, and a
keyword ending in two characters encoding a sequence number. Don't
accept the item if we are skipping over cards looking for a "Begin"
item. */
} else if ( !skip &&
( ( type == AST__STRING ) ||
( type == AST__INT ) ||
( type == AST__FLOAT ) ) &&
( len > 2 ) &&
strchr( SEQ_CHARS, keyword[ len - 1 ] ) &&
strchr( SEQ_CHARS, keyword[ len - 2 ] ) ) {
/* Note we have found a data item. */
done = 1;
/* Set the returned name by removing the last two characters from the
keyword and converting to lower case. Store this in a dynamically
allocated string. */
*name = astString( keyword, len - 2 );
for ( i = 0; ( *name )[ i ]; i++ ) {
( *name )[ i ] = tolower( ( *name )[ i ] );
}
/* Classify the data type. */
switch ( type ) {
/* If the value is a string, test if it is zero-length. If so, this
"null" value indicates an Object data item (whose definition
follows), so leave the returned value pointer as NULL. Otherwise,
we have a string data item, so extract its value and store it in a
dynamically allocated string. */
case AST__STRING:
if ( *( (char *) data ) ) {
/* A long string value may be continued on subsequent CONTINUE cards. See
if the current string may be continued. This is the case if the final
non-blank character (before UnPreQuoting) is an ampersand. */
cont = ( ((char *) data)[ ChrLen( data, status ) - 1 ] == '&' );
/* If the string does not end with an ampersand, just UnPreQUote it and
return a copy. */
if( !cont ) {
*val = UnPreQuote( (const char *) data, status );
/* Otherwise, initialise the returned string to hold a copy of the keyword
value. */
} else {
nc = strlen( (const char *) data );
*val = astStore( NULL, (const char *) data, nc + 1 );
/* Loop round reading any subsequent CONTINUE cards. Leave the loop when
the end-of-file is hit, or an error occurs. */
while( cont && MoveCard( this, 1, method, class, status ) &&
astOK ){
/* See if this is a CONTINUE card. If so, get its data pointer. */
if( CardType( this, status ) == AST__CONTINUE ){
data = CardData( this, NULL, status );
/* See if the CONTINUE card ends with an ampersand (i.e. if there is
a possibility of there being any remaining CONTINUE cards). */
cont = ( ( (char *) data)[ ChrLen( data, status ) - 1 ] == '&' );
/* UnPreQUote it. */
upq = UnPreQuote( (const char *) data, status );
if( !astOK ) break;
/* Expand the memory for the returned string to hold the new string. */
nn = strlen( upq );
*val = astRealloc( *val, nc + nn );
if( !astOK ) break;
/* Copy the new string into the expanded memory, so that the first
character of the new string over-writes the trailing ampersand
currently in the buffer. */
strcpy( *val + nc - 1, upq );
/* Release the memory holding the UnPreQUoted string . */
upq = astFree( upq );
/* Update the current length of the returned string. */
nc += nn - 1;
/* Mark the current card as having been read. */
MarkCard( this, status );
/* Report an error if this is not a CONTINUE card. */
} else {
astError( AST__BADIN, "%s(%s): One or more "
"FITS \"CONTINUE\" cards are missing "
"after the card for keyword \"%s\".", status,
method, class, keyword );
}
}
}
}
break;
/* If the value is an int, format it and store the result in a
dynamically allocated string. */
case AST__INT:
(void) sprintf( buff, "%d", *( (int *) data ) );
*val = astString( buff, (int) strlen( buff ) );
break;
/* If the value is a double, format it and store the result in a
dynamically allocated string. */
case AST__FLOAT:
(void) sprintf( buff, "%.*g", DBL_DIG, *( (double *) data ) );
CheckZero( buff, *( (double *) data ), 0, status );
*val = astString( buff, (int) strlen( buff ) );
break;
}
/* Anything else. */
/* -------------- */
/* If the input line didn't match any of the above and the "skip" flag
is not set, then report an error.. */
} else if ( !skip ) {
astError( AST__BADIN,
"%s(%s): Cannot interpret the input data given by "
"FITS keyword \"%s\".", status, method, class, keyword );
}
/* Free any memory used to hold stripped string data. */
if( freedata ) newdata = (char *) astFree( (void *) newdata );
}
/* Increment the current card. */
MoveCard( this, 1, method, class, status );
}
/* If an error occurred, ensure that any allocated memory is freed and
that NULL pointer values are returned. */
if ( !astOK ) {
*name = astFree( *name );
*val = astFree( *val );
}
/* Undefine macros local to this function. */
#undef BUFF_LEN
}
static int GetSkip( AstChannel *this_channel, int *status ) {
/*
* Name:
* GetSkip
* Purpose:
* Obtain the value of the Skip attribute for a FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int GetSkip( AstChannel *this, int *status )
* Class Membership:
* FitsChan member function (over-rides the protected astGetSkip
* method inherited from the Channel class).
* Description:
* This function return the (boolean) integer value of the Skip
* attribute for a FitsChan.
* Parameters:
* this
* Pointer to the FitsChan.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The Skip attribute value.
* Notes:
* - This function modifies the default Skip value from 0 to 1 for
* the benefit of the FitsChan class. This default value allows the
* astRead method to skip over unrelated FITS keywords when
* searching for the next Object to read.
* - 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: */
AstFitsChan *this; /* Pointer to the FitsChan structure */
int result; /* Result value to return */
/* Check the global error status. */
if ( !astOK ) return 0;
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_channel;
/* If the Skip attribute us set, obtain its value using the parent class
method. */
if ( astTestSkip( this ) ) {
result = (* parent_getskip)( this_channel, status );
/* Otherwise, supply a default value of 1. */
} else {
result = 1;
}
/* Return the result. */
return result;
}
static int GetValue( AstFitsChan *this, const char *keyname, int type,
void *value, int report, int mark, const char *method,
const char *class, int *status ){
/*
* Name:
* GetValue
* Purpose:
* Obtain a FITS keyword value.
* Type:
* Private function.
* Synopsis:
* int GetValue( AstFitsChan *this, const char *keyname, int type, void *value,
* int report, int mark, const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* This function gets a value for the specified keyword from the
* supplied FitsChan, and stores it in the supplied buffer. Optionally,
* the keyword is marked as having been read into an AST object so that
* it is not written out when the FitsChan is deleted.
* Parameters:
* this
* A pointer to the FitsChan containing the keyword values to be
* read.
* keyname
* A pointer to a string holding the keyword name.
* type
* The FITS data type in which to return the keyword value. If the
* stored value is not of the requested type, it is converted if
* possible.
* value
* A pointer to a buffer of suitable size to receive the keyword
* value. The supplied value is left unchanged if the keyword is
* not found.
* report
* Should an error be reported if the keyword cannot be found, or
* cannot be converted to the requested type?
* mark
* Should the card be marked as having been used?
* method
* A string holding the name of the calling method.
* class
* A string holding the object class.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Zero if the keyword does not exist in "this", or cannot be
* converted to the requested type. One is returned otherwise.
* Notes:
* - An error is reported if the keyword value is undefined.
* - A value of zero is returned if an error has already occurred,
* or if an error occurs within this function.
*/
/* Local Variables: */
int icard; /* Current card index */
int ret; /* Returned value */
/* Check the status */
if( !astOK ) return 0;
/* Save the current card index. */
icard = astGetCard( this );
/* Attempt to find the supplied keyword. */
ret = SearchCard( this, keyname, method, class, status );
/* If the keyword was found, convert the current card's data value and copy
it to the supplied buffer. */
if( ret ){
if( CnvValue( this, type, 0, value, method, status ) ) {
/* If required, mark it as having been read into an AST object. */
if( mark ) MarkCard( this, status );
/* If the value is undefined, report an error if "report" is non-zero. */
if( type == AST__UNDEF && report && astOK ) {
ret = 0;
astError( AST__FUNDEF, "%s(%s): FITS keyword \"%s\" has no value.",
status, method, class, keyname );
}
/* If the value could not be converted to the requested data, type report
an error if reporting is enabled. */
} else {
ret = 0;
if( report && astOK ){
astError( AST__FTCNV, "%s(%s): Cannot convert FITS keyword '%s' to %s.",
status, method, class, keyname, type_names[ type ] );
}
}
/* If the keyword was not found, report an error if "report" is non-zero. */
} else if( report && astOK ){
astError( AST__BDFTS, "%s(%s): Unable to find a value for FITS "
"keyword \"%s\".", status, method, class, keyname );
}
/* Reinstate the original current card index. */
astSetCard( this, icard );
/* If an error has occurred, return 0. */
if( !astOK ) ret = 0;
/* Return the result. */
return ret;
}
static int GetValue2( AstFitsChan *this1, AstFitsChan *this2, const char *keyname,
int type, void *value, int report, const char *method,
const char *class, int *status ){
/*
* Name:
* GetValue2
* Purpose:
* Obtain a FITS keyword value from one of two FitsChans.
* Type:
* Private function.
* Synopsis:
* int GetValue2( AstFitsChan *this1, AstFitsChan *this2, const char *keyname,
* int type, void *value, int report, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* This function attempts to get a value for the specified keyword from
* the first supplied FitsChan. If this fails (due to the FitsChan not
* containing a value for the ketword) then an attempt is made to get
* a value for the keyword from the second supplied FitsChan.
* Parameters:
* this1
* A pointer to the first FitsChan to be used.
* this2
* A pointer to the second FitsChan to be used.
* keyname
* A pointer to a string holding the keyword name.
* type
* The FITS data type in which to return the keyword value. If the
* stored value is not of the requested type, it is converted if
* possible.
* value
* A pointer to a buffer of suitable size to receive the keyword
* value. The supplied value is left unchanged if the keyword is
* not found.
* report
* Should an error be reported if the keyword cannot be found, or
* cannot be converted to the requested type?
* method
* A string holding the name of the calling method.
* class
* A string holding the object class.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Zero if the keyword does not exist in either FitsChan, or cannot be
* converted to the requested type. One is returned otherwise.
* Notes:
* - A value of zero is returned if an error has already occurred,
* or if an error occurs within this function.
* - If the card is found in the first FitsChan, it is not marked as
* having been used. If the card is found in the second FitsChan, it is
* marked as having been used.
*/
/* Local Variables: */
int ret; /* Returned value */
/* Check the status */
if( !astOK ) return 0;
/* Try the first FitsChan. If this fails try the second. Do not report
an error if the keyword is not found in the first FitsChan (this will
be done, if required, once the second FitsChan has been searched). */
ret = GetValue( this1, keyname, type, value, 0, 0, method, class, status );
if( ! ret ) {
ret = GetValue( this2, keyname, type, value, report, 1, method, class, status );
}
/* If an error has occurred, return 0. */
if( !astOK ) ret = 0;
/* Return the result. */
return ret;
}
static int HasAIPSSpecAxis( AstFitsChan *this, const char *method,
const char *class, int *status ){
/*
* Name:
* HasAIPSSpecAxis
* Purpose:
* Does the FitsChan contain an AIPS spectral CTYPE keyword?
* Type:
* Private function.
* Synopsis:
* int HasAIPSSpecAxis( AstFitsChan *this, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* This function returns a non-zero value if the FitsCHan contains a
* CTYPE value which conforms to the non-standard system used by AIPS.
* Parameters:
* this
* A pointer to the FitsChan to be used.
* 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:
* Non-zero if an AIPS spectral CTYPE keyword was found.
*/
/* Local Variables: */
char *assys; /* AIPS standard of rest type */
char *astype; /* AIPS spectral type */
char *cval; /* Pointer to character string */
int j; /* Current axis index */
int jhi; /* Highest axis index with a CTYPE */
int jlo; /* Lowest axis index with a CTYPE */
int ret; /* Returned value */
/* Initialise */
ret = 0;
/* Check the status */
if( !astOK ) return ret;
/* If the FitsChan contains any CTYPE values, convert the bounds from
one-based to zero-based, and loop round them all. */
if( astKeyFields( this, "CTYPE%1d", 1, &jhi, &jlo ) ) {
jlo--;
jhi--;
for( j = jlo; j <= jhi; j++ ) {
/* Get the next CTYPE value. If found, see if it is an AIPS spectral
CTYPE value. */
if( GetValue( this, FormatKey( "CTYPE", j + 1, -1, ' ', status ),
AST__STRING, (void *) &cval, 0, 0, method,
class, status ) ){
if( IsAIPSSpectral( cval, &astype, &assys, status ) ) {
ret = 1;
break;
}
}
}
}
/* If an error has occurred, return 0. */
if( !astOK ) ret = 0;
/* Return the result. */
return ret;
}
static int HasCard( AstFitsChan *this, const char *name,
const char *method, const char *class, int *status ){
/*
* Name:
* HasCard
* Purpose:
* Check if the FitsChan contains a specified keyword.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int HasCard( AstFitsChan *this, const char *name,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Returns a non-zero value if the FitsChan contains the given keyword,
* and zero otherwise. The current card is unchanged.
* Parameters:
* this
* Pointer to the FitsChan.
* name
* Pointer to a string holding the keyword name.
* method
* Pointer to string holding name of calling method.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A value of 1 is returned if a card was found refering to the given
* keyword. Otherwise zero is returned.
*/
/* Check the supplied pointers (we can rely on astMapHasKey to check the
inherited status). */
if( !name || !this || !this->keywords ) return 0;
/* Search the KeyMap holding the keywords currently in the FitsChan,
returning non-zero if the keyword was found. A KeyMap is used because
it uses a hashing algorithm to find the entries and is therefore a lot
quicker than searching through the list of linked FitsCards. */
return astMapHasKey( this->keywords, name );
}
void astInitFitsChanVtab_( AstFitsChanVtab *vtab, const char *name, int *status ) {
/*
*+
* Name:
* astInitFitsChanVtab
* Purpose:
* Initialise a virtual function table for a FitsChan.
* Type:
* Protected function.
* Synopsis:
* #include "fitschan.h"
* void astInitFitsChanVtab( AstFitsChanVtab *vtab, const char *name )
* Class Membership:
* FitsChan vtab initialiser.
* Description:
* This function initialises the component of a virtual function
* table which is used by the FitsChan 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 */
char buf[ 100 ]; /* Buffer large enough to store formatted INT_MAX */
/* 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 astIsAFitsChan) 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->PutCards = PutCards;
vtab->PutFits = PutFits;
vtab->DelFits = DelFits;
vtab->GetTables = GetTables;
vtab->PutTables = PutTables;
vtab->PutTable = PutTable;
vtab->TableSource = TableSource;
vtab->SetTableSource = SetTableSource;
vtab->RemoveTables = RemoveTables;
vtab->PurgeWCS = PurgeWCS;
vtab->RetainFits = RetainFits;
vtab->FindFits = FindFits;
vtab->KeyFields = KeyFields;
vtab->ReadFits = ReadFits;
vtab->ShowFits = ShowFits;
vtab->WriteFits = WriteFits;
vtab->EmptyFits = EmptyFits;
vtab->FitsEof = FitsEof;
vtab->GetFitsCF = GetFitsCF;
vtab->GetFitsCI = GetFitsCI;
vtab->GetFitsF = GetFitsF;
vtab->GetFitsI = GetFitsI;
vtab->GetFitsL = GetFitsL;
vtab->TestFits = TestFits;
vtab->GetFitsS = GetFitsS;
vtab->GetFitsCN = GetFitsCN;
vtab->FitsGetCom = FitsGetCom;
vtab->SetFitsCom = SetFitsCom;
vtab->SetFitsCF = SetFitsCF;
vtab->SetFitsCI = SetFitsCI;
vtab->SetFitsF = SetFitsF;
vtab->SetFitsI = SetFitsI;
vtab->SetFitsL = SetFitsL;
vtab->SetFitsU = SetFitsU;
vtab->SetFitsS = SetFitsS;
vtab->SetFitsCN = SetFitsCN;
vtab->SetFitsCM = SetFitsCM;
vtab->ClearCard = ClearCard;
vtab->TestCard = TestCard;
vtab->SetCard = SetCard;
vtab->GetCard = GetCard;
vtab->ClearFitsDigits = ClearFitsDigits;
vtab->TestFitsDigits = TestFitsDigits;
vtab->SetFitsDigits = SetFitsDigits;
vtab->GetFitsDigits = GetFitsDigits;
vtab->ClearFitsAxisOrder = ClearFitsAxisOrder;
vtab->TestFitsAxisOrder = TestFitsAxisOrder;
vtab->SetFitsAxisOrder = SetFitsAxisOrder;
vtab->GetFitsAxisOrder = GetFitsAxisOrder;
vtab->ClearDefB1950 = ClearDefB1950;
vtab->TestDefB1950 = TestDefB1950;
vtab->SetDefB1950 = SetDefB1950;
vtab->GetDefB1950 = GetDefB1950;
vtab->ClearTabOK = ClearTabOK;
vtab->TestTabOK = TestTabOK;
vtab->SetTabOK = SetTabOK;
vtab->GetTabOK = GetTabOK;
vtab->ClearCarLin = ClearCarLin;
vtab->TestCarLin = TestCarLin;
vtab->SetCarLin = SetCarLin;
vtab->GetCarLin = GetCarLin;
vtab->ClearPolyTan = ClearPolyTan;
vtab->TestPolyTan = TestPolyTan;
vtab->SetPolyTan = SetPolyTan;
vtab->GetPolyTan = GetPolyTan;
vtab->ClearIwc = ClearIwc;
vtab->TestIwc = TestIwc;
vtab->SetIwc = SetIwc;
vtab->GetIwc = GetIwc;
vtab->ClearWarnings = ClearWarnings;
vtab->TestWarnings = TestWarnings;
vtab->SetWarnings = SetWarnings;
vtab->GetWarnings = GetWarnings;
vtab->GetCardType = GetCardType;
vtab->GetCardName = GetCardName;
vtab->GetCardComm = GetCardComm;
vtab->GetNcard = GetNcard;
vtab->GetNkey = GetNkey;
vtab->GetAllWarnings = GetAllWarnings;
vtab->ClearEncoding = ClearEncoding;
vtab->TestEncoding = TestEncoding;
vtab->SetEncoding = SetEncoding;
vtab->GetEncoding = GetEncoding;
vtab->ClearClean = ClearClean;
vtab->TestClean = TestClean;
vtab->SetClean = SetClean;
vtab->GetClean = GetClean;
vtab->ClearCDMatrix = ClearCDMatrix;
vtab->TestCDMatrix = TestCDMatrix;
vtab->SetCDMatrix = SetCDMatrix;
vtab->GetCDMatrix = GetCDMatrix;
/* Save the inherited pointers to methods that will be extended, and
replace them with pointers to the new member functions. */
object = (AstObjectVtab *) vtab;
channel = (AstChannelVtab *) vtab;
parent_getobjsize = object->GetObjSize;
object->GetObjSize = GetObjSize;
#if defined(THREAD_SAFE)
parent_managelock = object->ManageLock;
object->ManageLock = ManageLock;
#endif
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;
parent_write = channel->Write;
channel->Write = Write;
parent_read = channel->Read;
channel->Read = Read;
parent_getskip = channel->GetSkip;
channel->GetSkip = GetSkip;
parent_getfull = channel->GetFull;
channel->GetFull = GetFull;
channel->WriteBegin = WriteBegin;
channel->WriteIsA = WriteIsA;
channel->WriteEnd = WriteEnd;
channel->WriteInt = WriteInt;
channel->WriteDouble = WriteDouble;
channel->WriteString = WriteString;
channel->WriteObject = WriteObject;
channel->GetNextData = GetNextData;
parent_setsourcefile = channel->SetSourceFile;
channel->SetSourceFile = SetSourceFile;
/* Declare the class dump, copy and delete functions.*/
astSetDump( vtab, Dump, "FitsChan", "I/O channels to FITS files" );
astSetCopy( (AstObjectVtab *) vtab, Copy );
astSetDelete( (AstObjectVtab *) vtab, Delete );
/* Max number of characters needed to format an int. */
LOCK_MUTEX4
sprintf( buf, "%d", INT_MAX );
int_dig = strlen( buf );
/* Create a pair of MJD TimeFrames which will be used for converting to and
from TDB. */
astBeginPM;
if( !tdbframe ) tdbframe = astTimeFrame( "system=MJD,timescale=TDB", status );
if( !timeframe ) timeframe = astTimeFrame( "system=MJD", status );
astEndPM;
UNLOCK_MUTEX4
/* 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 void InsCard( AstFitsChan *this, int overwrite, const char *name,
int type, void *data, const char *comment,
const char *method, const char *class, int *status ){
/*
* Name:
* InsCard
* Purpose:
* Inserts a card into a FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void InsCard( AstFitsChan *this, int overwrite, const char *name,
* int type, void *data, const char *comment,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Either appends a new card to a FitsChan, or over-writes an existing
* card, holding the supplied keyword name, value and comment.
* Parameters:
* this
* Pointer to the FitsChan containing the filters to apply to the
* keyword name. If a NULL pointer is supplied, no filtering is applied.
* overwrite
* If non-zero, the new card over-writes the current card given by
* the "Card" attribute, and the current card is incremented so
* that it refers to the next card. Otherwise, the new card is
* inserted in front of the current card and the current card is
* left unchanged.
* name
* Pointer to a string holding the keyword name of the new card.
* type
* An integer value representing the data type of the keyword.
* data
* Pointer to the data associated with the keyword.
* comment
* Pointer to a null-terminated string holding a comment.
* 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.
* Notes:
* - An error is reported if an attempt is made to change the data type
* of an existing card.
* - If a type of AST__COMMENT is supplied, then any data value (of any
* type) associated with an existing card is left unchanged.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
int flags; /* Flags to assign to new card */
/* Check the global status. */
if( !astOK ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this);
/* If the current card is to be over-written, delete the current card (the
next card in the list, if any, will become the new current card). */
if( overwrite ) DeleteCard( this, method, class, status );
/* If requested, set both NEW flags for the new card. */
flags = ( mark_new ) ? ( NEW1 | NEW2 ): 0;
/* Insert the new card into the list, just before the current card. */
NewCard( this, name, type, data, comment, flags, status );
}
static int IRAFFromStore( AstFitsChan *this, FitsStore *store,
const char *method, const char *class, int *status ){
/*
* Name:
* IRAFFromStore
* Purpose:
* Store WCS keywords in a FitsChan using FITS-IRAF encoding.
* Type:
* Private function.
* Synopsis:
* int IRAFFromStore( AstFitsChan *this, FitsStore *store,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* A FitsStore is a structure containing a generalised represention of
* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
* from a set of FITS header cards (using a specified encoding), or
* an AST FrameSet. In other words, a FitsStore is an encoding-
* independant intermediary staging post between a FITS header and
* an AST FrameSet.
*
* This function copies the WCS information stored in the supplied
* FitsStore into the supplied FitsChan, using FITS-IRAF encoding.
*
* IRAF encoding is like FITS-WCS encoding but with the following
* restrictions:
*
* 1) The celestial projection must not have any projection parameters
* which are not set to their default values. The one exception to this
* is that SIN projections are acceptable if the associated projection
* parameter PV_1 is zero and PV_2 = cot( reference point
* latitude). This is encoded using the string "-NCP". The SFL projection
* is encoded using the string "-GLS". Note, the original IRAF WCS
* system only recognised a small subset of the currently available
* projections, but some more recent IRAF-like software recognizes some
* of the new projections included in the FITS-WCS encoding.
*
* 2) The celestial axes must be RA/DEC, galactic or ecliptic.
*
* 3) LONPOLE and LATPOLE cannot be used.
*
* 4) Only primary axis descriptions are written out.
*
* 5) RADECSYS is used in place of RADESYS.
*
* 6) PC/CDELT keywords are not allowed (CD must be used)
* Parameters:
* this
* Pointer to the FitsChan.
* store
* Pointer to the FitsStore.
* 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:
* A value of 1 is returned if succesfull, and zero is returned
* otherwise.
*/
/* Local Variables: */
char *comm; /* Pointer to comment string */
char *cval; /* Pointer to string keyword value */
char combuf[80]; /* Buffer for FITS card comment */
char lattype[MXCTYPELEN];/* Latitude axis CTYPE */
char lontype[MXCTYPELEN];/* Longitude axis CTYPE */
char s; /* Co-ordinate version character */
char sign[2]; /* Fraction's sign character */
double cdelt; /* A CDELT value */
double fd; /* Fraction of a day */
double mjd99; /* MJD at start of 1999 */
double p1, p2; /* Projection parameters */
double val; /* General purpose value */
int axlat; /* Index of latitude FITS WCS axis */
int axlon; /* Index of longitude FITS WCS axis */
int axspec; /* Index of spectral FITS WCS axis */
int i; /* Axis index */
int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */
int iymdf[ 4 ]; /* Year, month, date, fractional day */
int j; /* Axis index */
int jj; /* SlaLib status */
int naxis; /* No. of axes */
int ok; /* Is FitsSTore OK for IRAF encoding? */
int prj; /* Projection type */
int ret; /* Returned value. */
/* Initialise */
ret = 0;
/* Check the inherited status. */
if( !astOK ) return ret;
/* First check that the values in the FitsStore conform to the
requirements of the IRAF encoding. Assume they do to begin with. */
ok = 1;
/* Just do primary axes. */
s = ' ';
/* Look for the primary celestial and spectral axes. */
FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status );
/* If both longitude and latitude axes are present and thereis no
spectral axis...*/
if( axlon >= 0 && axlat >= 0 ) {
/* Get the CTYPE values for both axes. */
cval = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status );
if( !cval ) return ret;
strcpy( lontype, cval );
cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status );
if( !cval ) return ret;
strcpy( lattype, cval );
/* Extract the projection type as specified by the last 4 characters
in the CTYPE keyword value. */
prj = astWcsPrjType( lattype + 4 );
/* Check the projection type is OK. Assume not initially. */
ok = 0;
/* FITS-IRAF cannot handle the AST-specific TPN projection. */
if( prj == AST__TPN || prj == AST__WCSBAD ) {
ok = 0;
/* SIN projections are handled later. */
} else if( prj != AST__SIN ){
/* There must be no projection parameters. */
if( GetMaxJM( &(store->pv), ' ', status ) == -1 ) ok = 1;
/* Change the new SFL projection code to to the older equivalent GLS */
if( prj == AST__SFL ){
(void) strcpy( lontype + 4, "-GLS" );
(void) strcpy( lattype + 4, "-GLS" );
}
/* SIN projections are only acceptable if the associated projection
parameters are both zero, or if the first is zero and the second
= cot( reference point latitude ) (the latter case is equivalent to
the old NCP projection). */
} else {
p1 = GetItem( &( store->pv ), axlat, 1, s, NULL, method, class, status );
p2 = GetItem( &( store->pv ), axlat, 2, s, NULL, method, class, status );
if( p1 == AST__BAD ) p1 = 0.0;
if( p2 == AST__BAD ) p2 = 0.0;
val = GetItem( &( store->crval ), axlat, 0, s, NULL, method, class, status );
if( val != AST__BAD ) {
if( p1 == 0.0 ) {
if( p2 == 0.0 ) {
ok = 1;
} else if( fabs( p2 ) >= 1.0E14 && val == 0.0 ){
ok = 1;
(void) strcpy( lontype + 4, "-NCP" );
(void) strcpy( lattype + 4, "-NCP" );
} else if( fabs( p2*tan( AST__DD2R*val ) - 1.0 )
< 0.01 ){
ok = 1;
(void) strcpy( lontype + 4, "-NCP" );
(void) strcpy( lattype + 4, "-NCP" );
}
}
}
}
/* Identify the celestial coordinate system from the first 4 characters of the
longitude CTYPE value. Only RA, galactic longitude, and ecliptic
longitude can be stored using FITS-IRAF. */
if( strncmp( lontype, "RA--", 4 ) &&
strncmp( lontype, "GLON", 4 ) &&
strncmp( lontype, "ELON", 4 ) ) ok = 0;
/* If the physical Frame requires a LONPOLE or LATPOLE keyword, it cannot
be encoded using FITS-IRAF. */
if( GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status )
!= AST__BAD ||
GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status )
!= AST__BAD ) ok = 0;
/* If there are no celestial axes, the physical Frame can be written out
using FITS-IRAF. */
} else {
ok = 1;
}
/* Save the number of axes */
naxis = GetMaxJM( &(store->crpix), ' ', status ) + 1;
/* If this is different to the value of NAXIS abort since this encoding
does not support WCSAXES keyword. */
if( naxis != store->naxis ) ok = 0;
/* Return if the FitsStore does not conform to IRAF encoding. */
if( !ok ) return ret;
/* Get and save CRPIX for all pixel axes. These are required, so return
if they are not available. */
for( i = 0; i < naxis; i++ ){
val = GetItem( &(store->crpix), 0, i, s, NULL, method, class, status );
if( val == AST__BAD ) return ret;
sprintf( combuf, "Reference pixel on axis %d", i + 1 );
SetValue( this, FormatKey( "CRPIX", i + 1, -1, s, status ), &val, AST__FLOAT,
combuf, status );
}
/* Get and save CRVAL for all intermediate axes. These are required, so return
if they are not available. */
for( j = 0; j < naxis; j++ ){
val = GetItem( &(store->crval), j, 0, s, NULL, method, class, status );
if( val == AST__BAD ) return ret;
sprintf( combuf, "Value at ref. pixel on axis %d", j + 1 );
SetValue( this, FormatKey( "CRVAL", j + 1, -1, s, status ), &val, AST__FLOAT,
combuf, status );
}
/* Get and save CTYPE for all intermediate axes. These are required, so return
if they are not available. Use the potentially modified versions saved
above for the celestial axes. */
for( i = 0; i < naxis; i++ ){
if( i == axlat ) {
cval = lattype;
} else if( i == axlon ) {
cval = lontype;
} else {
cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
if( !cval ) return ret;
}
if( strlen(cval) > 4 && !strcmp( cval + 4, "-TAB" ) ) return ret;
comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status );
if( !comm ) {
sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 );
comm = combuf;
}
SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval, AST__STRING,
comm, status );
}
/* CD matrix (the product of the CDELT and PC matrices). */
for( i = 0; i < naxis; i++ ){
cdelt = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
if( cdelt == AST__BAD ) cdelt = 1.0;
for( j = 0; j < naxis; j++ ){
val = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0;
val *= cdelt;
if( val != 0.0 ) {
SetValue( this, FormatKey( "CD", i + 1, j + 1, s, status ), &val,
AST__FLOAT, "Transformation matrix element", status );
}
}
}
/* Get and save CUNIT for all intermediate axes. These are NOT required, so
do not return if they are not available. */
for( i = 0; i < naxis; i++ ){
cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status );
if( cval ) {
sprintf( combuf, "Units for axis %d", i + 1 );
SetValue( this, FormatKey( "CUNIT", i + 1, -1, s, status ), &cval, AST__STRING,
combuf, status );
}
}
/* Get and save RADECSYS. This is NOT required, so do not return if it is
not available. */
cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status );
if( cval ) SetValue( this, "RADECSYS", &cval, AST__STRING,
"Reference frame for RA/DEC values", status );
/* Reference equinox */
val = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, "EQUINOX", &val, AST__FLOAT,
"Epoch of reference equinox", status );
/* Date of observation */
val = GetItem( &(store->mjdobs), 0, 0, ' ', NULL, method, class, status );
if( val != AST__BAD ) {
/* The format used for the DATE-OBS keyword depends on the value of the
keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format.
Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */
palCaldj( 99, 1, 1, &mjd99, &jj );
if( val < mjd99 ) {
palDjcal( 0, val, iymdf, &jj );
sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ],
iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) );
} else {
palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj );
palDd2tf( 3, fd, sign, ihmsf );
sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d",
iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1],
ihmsf[2], ihmsf[3] );
}
/* Now store the formatted string in the FitsChan. */
cval = combuf;
SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING,
"Date of observation", status );
}
/* If we get here we have succeeded. */
ret = 1;
/* Return zero or ret depending on whether an error has occurred. */
return astOK ? ret : 0;
}
static int IsMapLinear( AstMapping *smap, const double lbnd_in[],
const double ubnd_in[], int coord_out, int *status ) {
/*
* Name:
* IsMapLinear
* Purpose:
* See if a specified Mapping output is linearly related to the
* Mapping inputs.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int IsMapLinear( AstMapping *smap, const double lbnd_in[],
* const double ubnd_in[], int coord_out, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Returns a flag indicating if the specified output of the supplied
* Mapping is a linear function of the Mapping inputs. A set of output
* positions are created which are evenly spaced along the specified
* output coordinate. The spacing is chosen so that the entire range
* of the output coordinate is covered in 20 steps. The other output
* coordinates are held fixed at arbitrary values (actually, values
* at which the specified output coordinate achieves its minimum value).
* This set of output positions is transformed into the corresponding
* set of input coordinates using the inverse of the supplied Mapping.
* A least squares linear fit is then made which models each input
* coordinate as a linear function of the specified output coordinate.
* The residual at every point in this fit must be less than some
* small fraction of the total range of the corresponding input
* coordinate for the Mapping to be considered linear.
* Parameters:
* smap
* Pointer to the Mapping.
* lbnd_in
* Pointer to an array of double, with one element for each
* Mapping input coordinate. This should contain the lower bound
* of the input box in each input dimension.
* ubnd_in
* Pointer to an array of double, with one element for each
* Mapping input coordinate. This should contain the upper bound
* of the input box in each input dimension.
* coord_out
* The zero-based index of the Mapping output which is to be checked.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Non-zero if the specified Mapping output is linear. Zero otherwise.
*/
/* Local Constants: */
#define NP 20
/* Local Variables: */
AstMapping *map;
AstPointSet *pset1;
AstPointSet *pset2;
double **ptr1;
double **ptr2;
double *p;
double *s;
double *xl;
double c;
double d;
double delta;
double in_lbnd;
double in_ubnd;
double lbnd_out;
double m;
double p0;
double pv;
double sn;
double sp;
double sps;
double ss2;
double ss;
double sv;
double tol;
double ubnd_out;
int *ins;
int boxok;
int i;
int j;
int nin;
int nout;
int oldrep;
int ret;
/* Initialise */
ret = 0;
/* Check inherited status */
if( !astOK ) return ret;
/* Attempt to split off the required output (in case any of the other
outputs are associated with Mappings that do not have an inverse). */
astInvert( smap );
ins = astMapSplit( smap, 1, &coord_out, &map );
astInvert( smap );
/* If successful, check that the output is fed by only one input. */
if( ins ) {
if( astGetNin( map ) == 1 ) {
/* If so, invert the map so that it goes from pixel to wcs, and then
modify the supplied arguments so that they refer to the single required
axis. */
astInvert( map );
lbnd_in += coord_out;
ubnd_in += coord_out;
coord_out = 0;
/* If the output was fed by more than one input, annul the split mapping
and use the supplied nmapping. */
} else {
(void) astAnnul( map );
map = astClone( smap );
}
ins = astFree( ins );
/* If the supplied Mapping could not be split, use the supplied nmapping. */
} else {
map = astClone( smap );
}
/* Check the Mapping is defined in both directions. */
if( astGetTranForward( map ) && astGetTranInverse( map ) ) {
/* Allocate resources. */
nin = astGetNin( map );
nout = astGetNout( map );
xl = astMalloc( sizeof( double )*(size_t) nin );
pset1 = astPointSet( NP, nin, "", status );
ptr1 = astGetPoints( pset1 );
pset2 = astPointSet( NP, nout, "", status );
ptr2 = astGetPoints( pset2 );
/* Call astMapBox in a new error reporting context. */
boxok = 0;
if( astOK ) {
/* Temporarily switch off error reporting so that no report is made if
astMapBox cannot find a bounding box (which can legitimately happen with
some non-linear Mappings). */
oldrep = astReporting( 0 );
/* Find the upper and lower bounds on the specified Mapping output. This also
returns the input coords of a point at which the required output has its
lowest value. */
astMapBox( map, lbnd_in, ubnd_in, 1, coord_out, &lbnd_out, &ubnd_out,
xl, NULL );
/* If the box could not be found, clear the error status and pass on. */
if( !astOK ) {
astClearStatus;
/* If the box was found OK, flag this and check if the bounds are equal.
If so we cannot use them. In this case create new bounds. */
} else {
boxok = 1;
if( EQUAL( lbnd_out, ubnd_out ) ) {
m = 0.5*( lbnd_out + ubnd_out );
if( fabs( m ) > 1.0E-15 ) {
lbnd_out = 0.9*m;
ubnd_out = 1.1*m;
} else {
lbnd_out = -1.0;
ubnd_out = 1.0;
}
}
}
/* Re-instate error reporting. */
astReporting( oldrep );
}
/* Check pointers can be used safely and a box was obtained. */
if( astOK && boxok ) {
/* Transform the input position returned by astMapBox using the supplied
Mapping to get the corresponding output position. Fill all unused
elements of the PointSet with AST__BAD. */
for( i = 0; i < nin; i++ ){
p = ptr1[ i ];
*(p++) = xl[ i ];
for( j = 1; j < NP; j++ ) *(p++) = AST__BAD;
}
(void) astTransform( map, pset1, 1, pset2 );
/* Now create a set of NP points evenly spaced in output coordinates. The
first point is at the output position found above. Each subsequent
point is incremented by a fixed amount along the specified output
coordinate (the values on all other output coordinates is held fixed). */
delta = ( ubnd_out - lbnd_out )/ ( NP - 1 );
for( i = 0; i < nout; i++ ){
p = ptr2[ i ];
if( i == coord_out ) {
for( j = 0; j < NP; j++ ) *(p++) = lbnd_out + j*delta;
} else {
p0 = p[ 0 ];
for( j = 0; j < NP; j++ ) *(p++) = p0;
}
}
/* Transform these output positions into input positions using the
inverse Mapping. */
(void) astTransform( map, pset2, 0, pset1 );
/* Do a least squares fit to each input coordinate. Each fit gives the
corresponding input coordinate value as a linear function of the
specified output coordinate value. Note, linear function should never
produce bad values so abort if a bad value is found. */
ret = 1;
s = ptr2[ coord_out ];
for( i = 0; i < nin; i++ ) {
p = ptr1[ i ];
/* Form the required sums. Also find the largest and smallest input
coordinate value achieved. */
sp = 0.0;
ss = 0.0;
sps = 0.0;
sn = 0.0;
ss2 = 0.0;
in_lbnd = DBL_MAX;
in_ubnd = DBL_MIN;
for( j = 0; j < NP; j++ ) {
sv = s[ j ];
pv = p[ j ];
if( pv != AST__BAD && sv != AST__BAD ) {
sp += pv;
ss += sv;
sps += pv*sv;
sn += 1.0;
ss2 += sv*sv;
if( pv < in_lbnd ) in_lbnd = pv;
if( pv > in_ubnd ) in_ubnd = pv;
} else {
sn = 0.0;
break;
}
}
/* Ignore input axes which are independant of the output axis. */
if( !EQUAL( in_lbnd, in_ubnd ) ) {
/* Calculate the constants "input coord = m*output coord + c" */
d = ss*ss - sn*ss2;
if( sn > 0.0 && d != 0.0 ) {
m = ( sp*ss - sps*sn )/d;
c = ( sps*ss - sp*ss2 )/d;
/* Subtract off the fit value form the "p" values to get the residuals of
the fit. */
for( j = 0; j < NP; j++ ) p[ j ] -= m*s[ j ] + c;
/* We now do a least squares fit to the residuals. This second fit is done
because the first least squares fit sometimes leaves the residuals with a
distinct non-zero gradient. We do not need to worry about bad values
here since we have checked above that there are no bad values. Also we
do not need to recalculate sums which only depend on the "s" values since
they have not changed. */
sp = 0.0;
sps = 0.0;
for( j = 0; j < NP; j++ ) {
pv = p[ j ];
sp += pv;
sps += pv*s[ j ];
}
/* Find the constants in "input residual = m*output coord + c" equation. */
m = ( sp*ss - sps*sn )/d;
c = ( sps*ss - sp*ss2 )/d;
/* Subtract off the fit value form the "p residuals" values to get the
residual redisuals of the fit. */
for( j = 0; j < NP; j++ ) p[ j ] -= m*s[ j ] + c;
/* The requirement for a linear relationship is that the absolute residual
between the input coord produced by the above linear fit and the input
coord produced by the actual Mapping should be less than some small
fraction of the total range of input coord value, at every point. Test
this. */
tol = 1.0E-7*( in_ubnd - in_lbnd );
for( j = 0; j < NP; j++ ) {
if( fabs( p[ j ] ) > tol ) {
ret = 0;
break;
}
}
} else {
ret = 0;
}
}
if( !ret ) break;
}
}
/* Free resources. */
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
xl = astFree( xl );
}
map = astAnnul( map );
/* Return the answer. */
return ret;
}
static AstMapping *IsMapTab1D( AstMapping *map, double scale, const char *unit,
AstFrame *wcsfrm, double *dim, int iax,
int iwcs, AstFitsTable **table, int *icolmain,
int *icolindex, int *interp, int *status ){
/*
* Name:
* IsMapTab1D
* Purpose:
* See if a specified Mapping output is related to a single Mapping input
* via a FITS -TAB algorithm.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstMapping *IsMapTab1D( AstMapping *map, double scale, const char *unit,
* AstFrame *wcsfrm, double *dim, int iax,
* int iwcs, AstFitsTable **table, int *icolmain,
* int *icolindex, int *interp, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* A specified axis of the supplied Mapping is tested to see if it
* can be represented by the -TAB alogirithm described in FITS-WCS
* paper III. If the test is passed, a Mapping is returned from the
* specified WCS axis to the corresponding psi axis. A FitsTable is
* also created holding the information to be stored in the
* corresponding FITS binary table.
*
* Note, when creating a -TAB header, AST uses grid coords for the psi
* axis. See FITS-WCS paper III section 6.1.2 for a definition of the
* psi axes.
* Parameters:
* map
* Pointer to the Mapping from pixel coords to WCS coords.
* scale
* A scale factor by which to multiply the axis values stored in the
* returned FitsTable. Note, the returned Mapping is unaffected by
* this scaling factor.
* unit
* Pointer to the unit string to store with the coords column. If
* NULL, the unit string is extracted form the supplied WCS Frame.
* wcsfrm
* Pointer to a Frame describing WCS coords.
* dim
* An array holding the array dimensions in pixels. AST__BAD should
* be supplied for any unknown dimensions.
* iax
* The zero-based index of the Mapping output which is to be checked.
* iwcs
* The zero-based index of the corresponding FITS WCS axis.
* table
* Pointer to a location holding a pointer to the FitsTable describing
* the -TAB look-up table. If "*table" is NULL on entry, a new
* FitsTable will be created and returned, otherwise the supplied
* FitsTable is used.
* icolmain
* The one-based index of the column within "*table" that holds the
* main data array.
* icolindex
* The one-based index of the column within "*table" that holds the
* index vector. Returned equal to -1 if no index is added to the
* table (i.e. if the index is a unt index).
* interp
* The interpolation method (0=linear, other=nearest neighbour).
* status
* Pointer to the inherited status variable.
* Returned Value:
* If the specified "map" output can be described using the -TAB
* algorithm of FITS-WCS paper III, then a 1-input/1-output Mapping
* from the specified WCS axis to the corresponding psi axis (which is
* assumed to be equal to grid coords) is returned. NULL is returned
* otherwise, of if an error occurs.
*/
/* Local Variables: */
AstCmpMap *cm; /* CmpMap pointer */
AstMapping **map_list; /* Mapping array pointer */
AstMapping *postmap; /* Total Mapping after LutMap */
AstMapping *premap; /* Total Mapping before LutMap */
AstMapping *ret; /* Returned WCS axis Mapping */
AstMapping *tmap; /* Temporary Mapping */
AstPermMap *pm; /* PermMap pointer */
char cellname[ 20 ]; /* Buffer for cell name */
char colname[ 20 ]; /* Buffer for column name */
double *lut; /* Pointer to table of Y values */
double *work1; /* Pointer to work array */
double *work2; /* Pointer to work array */
double inc; /* X increment between table entries */
double start; /* X value at first table entry */
double v[ 2 ]; /* Y values at start and end of interval */
double x[ 2 ]; /* X values at start and end of interval */
int *ins; /* Array of "map" input indices */
int *invert_list; /* Invert array pointer */
int *outs; /* Array of "map" output indices */
int dims[ 2 ]; /* Dimensions of the tab coords array */
int iin; /* Index of Mapping input */
int ilut; /* Index of the LutMap within the mappings list */
int imap; /* Index of current Mapping in list */
int iout; /* Index of Mapping output */
int jout; /* Index of Mapping output */
int nin; /* Number of Mapping inputs */
int nlut; /* Number of elements in "lut" array */
int nmap; /* Number of Mappings in the list */
int nout; /* Number of Mapping outputs */
int ok; /* Were columns added to the table? */
int old_invert; /* Original value for Mapping's Invert flag */
int outperm; /* Index of input that feeds the single output */
/* Initialise */
ret = NULL;
*icolmain = -1;
*icolindex = -1;
*interp = 0;
/* Check inherited status */
if( !astOK ) return ret;
/* Ensure we have aunit string. */
if( !unit ) unit = astGetUnit( wcsfrm, iax );
/* Check that the requested mapping output is fed by only one mapping
input, identify that input, and extract the input->output mapping from
the total mapping. Since astMapSplit splits off a specified input, we
need to invert the Mapping first so we can split off a specified output. */
astInvert( map );
ins = astMapSplit( map, 1, &iax, &ret );
astInvert( map );
/* If the Mapping could not be split, try a different approach in which
each input is checked in turn to see if it feeds the specified output. */
if( !ins ) {
/* Loop round each input of "map". */
nin = astGetNin( map );
for( iin = 0; iin < nin && !ins; iin++ ) {
/* Attempt to find a group of outputs (of "map") that are fed by just
this one input. */
outs = astMapSplit( map, 1, &iin, &ret );
/* If successful, "ret" will be a Mapping with one input corresponding to
input "iin" of "map, and one or more outputs. We loop round these
outputs to see if any of them correspond to output "iax" of "map". */
if( outs ) {
nout = astGetNout( ret );
for( iout = 0; iout < nout; iout++ ) {
if( outs[ iout ] == iax ) break;
}
/* Did input "iin" feed the output "iax" (and possibly other outputs)? */
if( iout < nout ) {
/* The "ret" Mapping is now a 1-input (pixel) N-output (WCS) Mapping in which
output "iout" corresponds to output "iax" of Mapping. To be compatible
with the previous approach, we want "ret" to be a 1-input (WCS) to
1-output (pixel) Mapping in which the input corresponds to output
"iax" of Mapping. To get "ret" into this form, we first append a PermMap
to "ret" that selects a single output ("iout"), and then invert the
whole CmpMap. */
for( jout = 0; jout < nout; jout++ ) {
outs[ jout ] = -1;
}
outs[ iout ] = 0;
outperm = iout;
pm = astPermMap( nout, outs, 1, &outperm, NULL, "", status );
cm = astCmpMap( ret, pm, 1, " ", status );
(void) astAnnul( ret );
pm = astAnnul( pm );
ret = (AstMapping *) cm;
astInvert( ret );
/* The earlier approach leves ins[ 0 ] holding the index of the input to
"map" that feeds output iax. Ensure we have this too. */
ins = outs;
ins[ 0 ] = iin;
/* Free resources if the current input did not feed the required output. */
} else {
outs = astFree( outs );
ret = astAnnul( ret );
}
}
}
}
/* If the Mapping still could not be split, try again on a copy of the
Mapping in which all PermMaps provide an alternative implementation of
the astMapSplit method. */
if( !ins ) {
astInvert( map );
tmap = astCopy( map );
ChangePermSplit( tmap, status );
ins = astMapSplit( tmap, 1, &iax, &ret );
tmap = astAnnul( tmap );
astInvert( map );
}
/* Assume the Mapping cannot be represented by -TAB */
ok = 0;
/* Check a Mapping was returned by astMapSplit. If so, it will be the
mapping from the requested output of "map" (the WCS axis) to the
corresponding input(s) (grid axes). Check only one "map" input feeds the
requested output. */
if( ins && ret && astGetNout( ret ) == 1 ) {
/* Invert the Mapping so that the input is grid coord and the output is
WCS coord. */
astInvert( ret );
/* We now search the "ret" mapping for a non-inverted LutMap, splitting ret
up into three serial components: 1) the mappings before the LutMap, 2) the
LutMap itself, and 3) the mappings following the LutMap. First, decompose
the mapping into a list of series mappings. */
map_list = NULL;
invert_list = NULL;
nmap = 0;
astMapList( ret, 1, astGetInvert( ret ), &nmap, &map_list,
&invert_list );
/* Search the list for a non-inverted LutMap. */
ilut = -1;
for( imap = 0; imap < nmap; imap++ ) {
if( astIsALutMap( map_list[ imap ] ) && !(invert_list[ imap ]) ) {
ilut = imap;
break;
}
}
/* If a LutMap was found, combine all Mappings before the LutMap into a
single Mapping. Remember to set the Mapping Invert flags temporarily to
the values used within the CmpMap. */
if( ilut >= 0 ) {
premap = (AstMapping *) astUnitMap( 1, " ", status );
for( imap = 0; imap < ilut; imap++ ) {
old_invert = astGetInvert( map_list[ imap ] );
astSetInvert( map_list[ imap ], invert_list[ imap ] );
tmap = (AstMapping *) astCmpMap( premap, map_list[ imap ], 1,
" ", status );
astSetInvert( map_list[ imap ], old_invert );
(void) astAnnul( premap );
premap = tmap;
}
/* Also combine all Mappings after the LutMap into a single Mapping, setting
the Mapping Invert flags temporarily to the values used within the
CmpMap. */
postmap = (AstMapping *) astUnitMap( 1, " ", status );
for( imap = ilut + 1; imap < nmap; imap++ ) {
old_invert = astGetInvert( map_list[ imap ] );
astSetInvert( map_list[ imap ], invert_list[ imap ] );
tmap = (AstMapping *) astCmpMap( postmap, map_list[ imap ], 1,
" ", status );
astSetInvert( map_list[ imap ], old_invert );
(void) astAnnul( postmap );
postmap = tmap;
}
/* Get the table of values, and other attributes, from the LutMap. */
lut = astGetLutMapInfo( map_list[ ilut ], &start, &inc, &nlut );
*interp = astGetLutInterp( map_list[ ilut ] );
/* If required, create a FitsTable to hold the returned table info. */
if( ! *table ) *table = astFitsTable( NULL, "", status );
ok = 1;
/* Define the properties of the column in the FitsTable that holds the main
coordinate array. Points on a WCS axis are described by a single value
(wavelength, frequency, or whatever), but the coords array has to be
2-dimensional, with an initial degenerate axis, as required by FITS-WCS
paper III. */
dims[ 0 ] = 1;
dims[ 1 ] = nlut;
sprintf( colname, "COORDS%d", iwcs + 1 );
astAddColumn( *table, colname, AST__DOUBLETYPE, 2, dims, unit );
/* Get the one-based index of the column just added to the table. */
*icolmain = astGetNcolumn( *table );
/* Get workspace. */
work1 = astMalloc( nlut*sizeof( double ) );
if( astOK ) {
/* Transform the LutMap table values using the post-lutmap mapping to
get the list of WCS values in AST units. */
astTran1( postmap, nlut, lut, 1, work1 );
/* Convert them to FITS units (e.g. celestial axis values should be
converted from radians to degrees). */
for( ilut = 0; ilut < nlut; ilut++ ) work1[ ilut ] *= scale;
/* Store them in row 1, column COORDS, in the FitsTable. */
sprintf( cellname, "COORDS%d(1)", iwcs + 1 );
astMapPut1D( *table, cellname, nlut, work1, NULL );
/* Create an array holding the LutMap input value at the centre of each
table entry. Re-use the "lut" array since we no longer need it. */
for( ilut = 0; ilut < nlut; ilut++ ) {
lut[ ilut ] = start + ilut*inc;
}
/* Transform this array using the inverted pre-lutmap mapping to get the
list of grid coord. */
astTran1( premap, nlut, lut, 0, work1 );
/* Test this list to see if they form a unit index (i.e. index(i) == i+1 ).
(not the "+1" is due to the fact that "i" is zero based). */
for( ilut = 0; ilut < nlut; ilut++ ) {
if( fabs( work1[ ilut ] - ilut - 1.0 ) > 1.0E-6 ) break;
}
/* if it is not a unit index, we add the index to the table. */
if( ilut < nlut ) {
/* Define the properties of the column in the FitsTable that holds the
indexing vector. */
sprintf( colname, "INDEX%d", iwcs + 1 );
astAddColumn( *table, colname, AST__DOUBLETYPE, 1, &nlut, " " );
/* Get the one-based index of the column just added to the table. */
*icolindex = astGetNcolumn( *table );
/* Store the values in the column. */
sprintf( cellname, "INDEX%d(1)", iwcs + 1 );
astMapPut1D( *table, cellname, nlut, work1, NULL );
}
}
/* Free resources. */
work1 = astFree( work1 );
lut = astFree( lut );
premap = astAnnul( premap );
postmap = astAnnul( postmap );
/* If no LutMap was found in the Mapping, then we can create a FitsTable
by sampling the full WCS Mapping at selected input (i.e. grid)
positions. But we can only do this if we know the number of pixels
along the WCS axis. */
} else if( dim[ ins[ 0 ] ] != AST__BAD ) {
/* Create two works array each holding a single value. The first holds
the grid coords at which the samples are taken. The second holds the
WCS coords at the sampled positions. These arrays are expanded as
required within function AdaptLut. */
work1 = astMalloc( sizeof( double ) );
work2 = astMalloc( sizeof( double ) );
if( astOK ) {
/* Get the WCS values at the centres of the first and last pixel on
the WCS axis. */
x[ 0 ] = 1.0;
x[ 1 ] = dim[ ins[ 0 ] ];
astTran1( ret, 2, x, 1, v );
/* Put the lower limit into the work arrays. */
work1[ 0 ] = x[ 0 ];
work2[ 0 ] = v[ 0 ];
nlut = 1;
/* Expand the arrays by sampling the WCS axis adaptively so that
more samples occur where the WCS value is changing most rapidly.
We require the maximum error introduced by the table to be 0.25 pixels. */
AdaptLut( ret, 3, 0.25, x[ 0 ], x[ 1 ], v[ 0 ], v[ 1 ],
&work1, &work2, &nlut, status );
/* Create a FitsTable to hold the returned table info. */
if( ! *table ) *table = astFitsTable( NULL, "", status );
ok = 1;
/* Define the properties of the column in the FitsTable that holds the main
coordinate array. */
sprintf( colname, "COORDS%d", iwcs + 1 );
dims[ 0 ] = 1;
dims[ 1 ] = nlut;
astAddColumn( *table, colname, AST__DOUBLETYPE, 2, dims, unit );
*icolmain = astGetNcolumn( *table );
/* Convert the axis values to FITS units (e.g. celestial axis values should be
converted from radians to degrees). */
for( ilut = 0; ilut < nlut; ilut++ ) work2[ ilut ] *= scale;
/* Store the scaled axis values in row 1 of the column. */
sprintf( cellname, "COORDS%d(1)", iwcs + 1 );
astMapPut1D( *table, cellname, nlut, work2, NULL );
/* Test the index vector to see if they form a unit index (i.e. index(i) ==
i+1 ). If not the "+1" is due to the fact that "i" is zero based). If not, store
them as the index vector in the FitsTable. */
for( ilut = 0; ilut < nlut; ilut++ ) {
if( fabs( work1[ ilut ] - ilut - 1.0 ) > 1.0E-6 ) break;
}
/* If the index vector is not a unit index, define the properties of the
column in the FitsTable that holds the indexing vector. Then store values
in row 1 of the column. */
if( ilut < nlut ) {
sprintf( colname, "INDEX%d", iwcs + 1 );
astAddColumn( *table, colname, AST__DOUBLETYPE, 1, &nlut, " " );
*icolindex = astGetNcolumn( *table );
sprintf( cellname, "INDEX%d(1)", iwcs + 1 );
astMapPut1D( *table, cellname, nlut, work1, NULL );
}
}
/* Free resources */
work1 = astFree( work1 );
work2 = astFree( work2 );
}
/* If columns were added to the table, invert the returned Mapping again
so that the input is wcs coord and the output is grid coord. Otherwise,
annul the returned Mapping. */
if( ok ) {
astInvert( ret );
} else {
ret = astAnnul( ret );
}
/* Loop to annul all the Mapping pointers in the list. */
for ( imap = 0; imap < nmap; imap++ ) map_list[ imap ] = astAnnul( map_list[ imap ] );
/* Free the dynamic arrays. */
map_list = astFree( map_list );
invert_list = astFree( invert_list );
}
/* Free resources. */
ins = astFree( ins );
/* If an error occurred, free the returned Mapping. */
if( !astOK ) ret = astAnnul( ret );
/* Return the result. */
return ret;
}
static AstMapping *IsMapTab2D( AstMapping *map, double scale, const char *unit,
AstFrame *wcsfrm, double *dim, int iax1,
int iax2, int iwcs1, int iwcs2,
AstFitsTable **table, int *icolmain1,
int *icolmain2, int *icolindex1,
int *icolindex2, int *max1, int *max2,
int *interp1, int *interp2, int *status ){
/*
* Name:
* IsMapTab2D
* Purpose:
* See if a specified pair of Mapping outputs are related to a pair of
* Mapping inputs via a FITS -TAB algorithm.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstMapping *IsMapTab2D( AstMapping *map, double scale, const char *unit,
* AstFrame *wcsfrm, double *dim, int iax1,
* int iax2, int iwcs1, int iwcs2,
* AstFitsTable **table, int *icolmain1,
* int *icolmain2, int *icolindex1,
* int *icolindex2, int *max1, int *max2,
* int *interp1, int *interp2, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* A specified pair of outputs axes of the supplied Mapping are tested
* to see if they can be represented by the -TAB alogirithm described in
* FITS-WCS paper III. If the test is passed, a Mapping is returned from
* the specified WCS axes to the corresponding psi axes. A FitsTable is
* also created holding the information to be stored in the corresponding
* FITS binary table. Note, when creating a header, AST assumes a unit
* transformaton between psi axes and grid axes (psi axes are defined
* in FITS-WCS paper III section 6.1.2).
* Parameters:
* map
* Pointer to the Mapping from pixel coords to WCS coords.
* scale
* A scale factor by which to multiply the axis values stored in the
* returned FitsTable. Note, the returned Mapping is unaffected by
* this scaling factor.
* unit
* A unit string for the axis values. If supplied, the same
* string is stored for both axes. If NULL, the unit strings are
* extracted from the relavent axes of the supplied WCS Frame.
* wcsfrm
* Pointer to a Frame describing WCS coords.
* dim
* An array holding the array dimensions in pixels. AST__BAD should
* be supplied for any unknown dimensions.
* iax1
* The zero-based index of the first Mapping output which is to be
* checked.
* iax2
* The zero-based index of the second Mapping output which is to be
* checked.
* iwcs1
* The zero-based index of the FITS WCS axis corresponding to "iax1".
* iwcs2
* The zero-based index of the FITS WCS axis corresponding to "iax2".
* table
* Pointer to a location holding a pointer to the FitsTable describing
* the -TAB look-up table. If "*table" is NULL on entry, a new
* FitsTable will be created and returned, otherwise the supplied
* FitsTable is used.
* icolmain1
* The one-based index of the column within "*table" that holds the
* main coord array for the first Mapping output.
* icolmain2
* The one-based index of the column within "*table" that holds the
* main coord array for the second Mapping output.
* icolindex1
* The one-based index of the column within "*table" that holds the
* index vector for the first Mapping output. Returned equal to -1
* if no index is added to the table (e.g. because the index is a
* unit index).
* icolindex2
* The one-based index of the column within "*table" that holds the
* index vector for the second Mapping output. Returned equal to -1
* if no index is added to the table (e.g. because the index is a
* unit index).
* max1
* The one-based index of the dimension describing the first Mapping
* output within the main coord array specified by "icolmain1".
* max2
* The one-based index of the dimension describing the second Mapping
* output within the main coord array specified by "icolmain1".
* interp1
* The interpolation method (0=linear, other=nearest neighbour) for
* the first mapping output.
* interp2
* The interpolation method (0=linear, other=nearest neighbour) for
* the second mapping output.
* status
* Pointer to the inherited status variable.
* Returned Value:
* If the specified "map" outputs can be described using the -TAB
* algorithm of FITS-WCS paper III, then a 2-input/2-output Mapping
* from the specified WCS axes to the corresponding psi axes (i.e.
* grid axes) is returned. NULL is returned otherwise, of if an error
* occurs.
*/
/* Local Variables: */
AstMapping *ret1; /* WCS->IWC Mapping for first output */
AstMapping *ret2; /* WCS->IWC Mapping for second output */
AstMapping *ret; /* Returned WCS axis Mapping */
AstMapping *tmap;
AstPermMap *pm;
int *pix_axes; /* Zero-based indicies of corresponding pixel axes */
int wcs_axes[ 2 ]; /* Zero-based indicies of selected WCS axes */
int inperm[ 1 ];
int outperm[ 2 ];
/* Initialise */
ret = NULL;
/* Check inherited status */
if( !astOK ) return ret;
/* First see if the two required Mapping outputs are separable, in which case
they can be described by two 1D tables. */
ret1 = IsMapTab1D( map, scale, unit, wcsfrm, dim, iax1, iwcs1, table, icolmain1,
icolindex1, interp1, status );
ret2 = IsMapTab1D( map, scale, unit, wcsfrm, dim, iax2, iwcs2, table, icolmain2,
icolindex2, interp2, status );
/* If both outputs are seperable... */
if( ret1 && ret2 ) {
/* Both axes are stored as the first dimension in the corresponding main
coords array. */
*max1 = 1;
*max2 = 1;
/* Get a Mapping from the required pair of WCS axes to the corresponding
pair of grid axes. First try to split the supplied grid->wcs mapping. */
wcs_axes[ 0 ] = iax1;
wcs_axes[ 1 ] = iax2;
astInvert( map );
pix_axes = astMapSplit( map, 2, wcs_axes, &ret );
astInvert( map );
if( pix_axes ) {
pix_axes = astFree( pix_axes );
if( astGetNout( ret ) > 2 ) {
ret = astAnnul( ret );
/* If the two output WCS axes are fed by the same grid axis, we need to
add another pixel axis to form the pair. */
} else if( astGetNout( ret ) == 1 ) {
inperm[ 0 ] = 0;
outperm[ 0 ] = 0;
outperm[ 1 ] = 0;
pm = astPermMap( 1, inperm, 2, outperm, NULL, " ", status );
tmap = (AstMapping *) astCmpMap( ret, pm, 1, " ", status );
ret = astAnnul( ret );
pm = astAnnul( pm );
ret = tmap;
}
}
/* If this was unsuccessful, combine the Mappings returned by IsMapTab1D.
We only do this if the above astMapSplit call failed, since the IsMapTab1D
mappings may well not be independent of each other, and we may end up
sticking together in parallel two mappings that are basically the same
except for ending with PermMapa that select different axes. Is is hard
then to simplify such a parallel CmpMap back into the simpler form
that uses only one of the two identical mappings, without a PermMap. */
if( !ret ) {
ret = (AstMapping *) astCmpMap( ret1, ret2, 0, " ", status );
}
/* Free resources. */
ret1 = astAnnul( ret1 );
ret2 = astAnnul( ret2 );
/* If only one output is separable, remove the corresponding columns from
the returned table. */
} else if( ret1 ) {
ret1 = astAnnul( ret1 );
astRemoveColumn( *table, astColumnName( *table, *icolmain1 ) );
if( icolindex1 >= 0 ) astRemoveColumn( *table, astColumnName( *table, *icolindex1 ) );
} else if( ret2 ) {
ret2 = astAnnul( ret2 );
astRemoveColumn( *table, astColumnName( *table, *icolmain2 ) );
if( icolindex1 >= 0 ) astRemoveColumn( *table, astColumnName( *table, *icolindex2 ) );
}
/* If the required Mapping outputs were not separable, create a single
2D coords array describing both outputs. */
if( !ret ) {
/* TO BE DONE... Until then non-separable Mappings will result in a
failure to create a -TAB header. No point in doing this until AST has
an N-dimensional LutMap class (otherwise AST could never read the
resulting FITS header). */
}
/* If an error occurred, free the returned Mapping. */
if( !astOK ) ret = astAnnul( ret );
/* Return the result. */
return ret;
}
static int IsAIPSSpectral( const char *ctype, char **wctype, char **wspecsys, int *status ){
/*
* Name:
* IsAIPSSpectral
* Purpose:
* See if a given CTYPE value describes a FITS-AIPS spectral axis.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int IsAIPSSpectral( const char *ctype, char **wctype, char **wspecsys, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* The given CTYPE value is checked to see if it conforms to the
* requirements of a spectral axis CTYPE value as specified by
* FITS-AIPS encoding. If so, the equivalent FITS-WCS CTYPE and
* SPECSYS values are returned.
* Parameters:
* ctype
* Pointer to a null terminated string holding the CTYPE value to
* check.
* wctype
* The address of a location at which to return a pointer to a
* static string holding the corresponding FITS-WCS CTYPE value. A
* NULL pointer is returned if the supplied CTYPE string is not an
* AIPS spectral CTYPE value.
* wspecsys
* The address of a location at which to return a pointer to a
* static string holding the corresponding FITS-WCS SPECSYS value. A
* NULL pointer is returned if the supplied CTYPE string is not an
* AIPS spectral CTYPE value.
* status
* Pointer to the inherited status variable.
* Retuned Value:
* Non-zero fi the supplied CTYPE was an AIPS spectral CTYPE value.
* Note:
* - These translations are also used by the FITS-CLASS encoding.
*/
/* Local Variables: */
int ret;
/* Initialise */
ret = 0;
*wctype = NULL;
*wspecsys = NULL;
/* Check the inherited status. */
if( !astOK ) return ret;
/* If the length of the string is not 8, then it is not an AIPS spectral axis. */
if( strlen( ctype ) == 8 ) {
/* Translate AIPS spectral CTYPE values to FITS-WCS paper III equivalents.
These are of the form AAAA-BBB, where "AAAA" can be "FREQ", "VELO" (=VRAD!)
or "FELO" (=VOPT-F2W), and BBB can be "LSR", "LSD", "HEL" (=*Bary*centric!)
or "GEO". */
if( !strncmp( ctype, "FREQ", 4 ) ){
*wctype = "FREQ ";
} else if( !strncmp( ctype, "VELO", 4 ) ){
*wctype = "VRAD ";
} else if( !strncmp( ctype, "FELO", 4 ) ){
*wctype = "VOPT-F2W";
} else if( !strncmp( ctype, "WAVELENG", 8 ) ){
*wctype = "WAVE ";
}
if( !strcmp( ctype + 4, "-LSR" ) ){
*wspecsys = "LSRK";
} else if( !strcmp( ctype + 4, "LSRK" ) ){
*wspecsys = "LSRK";
} else if( !strcmp( ctype + 4, "-LSRK" ) ){
*wspecsys = "LSRK";
} else if( !strcmp( ctype + 4, "-LSD" ) ){
*wspecsys = "LSRD";
} else if( !strcmp( ctype + 4, "-HEL" ) ){
*wspecsys = "BARYCENT";
} else if( !strcmp( ctype + 4, "-EAR" ) || !strcmp( ctype + 4, "-GEO" ) ){
*wspecsys = "GEOCENTR";
} else if( !strcmp( ctype + 4, "-OBS" ) || !strcmp( ctype + 4, "-TOP" ) ){
*wspecsys = "TOPOCENT";
}
if( *wctype && *wspecsys ) {
ret = 1;
} else {
*wctype = NULL;
*wspecsys = NULL;
}
}
/* Return the result. */
return ret;
}
static int IsSkyOff( AstFrameSet *fset, int iframe, int *status ){
/*
* Name:
* IsSkyOff
* Purpose:
* See if a given Frame contains an offset SkyFrame.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int IsSkyOff( AstFrameSet *fset, int iframe, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Returns a flag indicating if the specified Frame within the
* supplied FrameSet is, or contains, a SkyFrame that represents
* offset coordinates. This is the case if the Frame is a SkyFrame
* and its SkyRefIs attribute is "Pole" or "Origin", or is a CmpFrame
* containing such a SkyFrame.
* Parameters:
* fset
* The FrameSet.
* iframe
* Index of the Frame to check within "fset"
* status
* Pointer to the inherited status variable.
* Retuned Value:
* +1 if the Frame is an offset SkyFrame. Zero otherwise.
* Notes:
* - Zero is returned if an error has already occurred.
*/
/* Local Variables: */
AstFrame *frm;
const char *skyrefis;
int oldrep;
int result;
/* Initialise. */
result = 0;
/* Check the inherited status. */
if( !astOK ) return result;
/* Get a pointer to the required Frame in the FrameSet */
frm = astGetFrame( fset, iframe );
/* Since the current Frame may not contain a SkyFrame, we temporarily
switch off error reporting. */
oldrep = astReporting( 0 );
/* Get the SkyRefIs attribute value. */
skyrefis = astGetC( frm, "SkyRefIs" );
/* If it is "Pole" or "Origin", return 1. */
if( skyrefis && ( !Ustrcmp( skyrefis, "POLE", status ) ||
!Ustrcmp( skyrefis, "ORIGIN", status ) ) ) result = 1;
/* Cancel any error and switch error reporting back on again. */
astClearStatus;
astReporting( oldrep );
/* Annul the Frame pointer. */
frm = astAnnul( frm );
/* Return the result. */
return result;
}
static const char *IsSpectral( const char *ctype, char stype[5], char algcode[5], int *status ) {
/*
* Name:
* IsSpectral
* Purpose:
* See if a given FITS-WCS CTYPE value describes a spectral axis.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* char *IsSpectral( const char *ctype, char stype[5], char algcode[5], int *status )
* Class Membership:
* FitsChan member function.
* Description:
* The given CTYPE value is checked to see if it conforms to the
* requirements of a spectral axis CTYPE value as specified by
* FITS-WCS paper 3. If so, the spectral system and algorithm codes
* are extracted from it and returned, together with the default units
* for the spectral system.
* Parameters:
* ctype
* Pointer to a null terminated string holding the CTYPE value to
* check.
* stype
* An array in which to return the null-terminated spectral system type
* (e.g. "FREQ", "VELO", "WAVE", etc). A null string is returned if
* the CTYPE value does not describe a spectral axis.
* algcode
* An array in which to return the null-terminated algorithm code
* (e.g. "-LOG", "", "-F2W", etc). A null string is returned if the
* spectral axis is linear. A null string is returned if the CTYPE
* value does not describe a spectral axis.
* status
* Pointer to the inherited status variable.
* Retuned Value:
* A point to a static string holding the default units associated
* with the spectral system specified by the supplied CTYPE value.
* NULL is returned if the CTYPE value does not describe a spectral
* axis.
* Notes:
* - The axis is considered to be a spectral axis if the first 4
* characters form one of the spectral system codes listed in FITS-WCS
* paper 3. The algorithm code is not checked, except to ensure that
* it begins with a minus sign, or is blank.
* - A NULL pointer is returned if an error has already occurred.
*/
/* Local Variables: */
astDECLARE_GLOBALS
int ctype_len;
/* Initialise */
stype[ 0 ] = 0;
algcode[ 0 ] = 0;
/* Check the inherited status. */
if( !astOK ) return NULL;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(NULL);
/* Initialise more stuff */
isspectral_ret = NULL;
/* If the length of the string is less than 4, then it is not a spectral
axis. */
ctype_len = strlen( ctype );
if( ctype_len >= 4 ) {
/* Copy the first 4 characters (the coordinate system described by the
axis) into a null-terminated buffer. */
strncpy( stype, ctype, 4 );
stype[ 4 ] = 0;
stype[ astChrLen( stype ) ] = 0;
/* Copy any remaining characters (the algorithm code) into a null-terminated
buffer. Only copy a maximum of 4 characters. */
if( ctype_len > 4 ) {
if( ctype_len <= 8 ) {
strcpy( algcode, ctype + 4 );
} else {
strncpy( algcode, ctype + 4, 4 );
algcode[ 4 ] = 0;
}
algcode[ astChrLen( algcode ) ] = 0;
} else {
algcode[ 0 ] = 0;
}
/* See if the first 4 characters of the CTYPE value form one of the legal
spectral coordinate type codes listed in FITS-WCS Paper III. Also note
the default units associated with the system. */
if( !strcmp( stype, "FREQ" ) ) {
isspectral_ret = "Hz";
} else if( !strcmp( stype, "ENER" ) ) {
isspectral_ret = "J";
} else if( !strcmp( stype, "WAVN" ) ) {
isspectral_ret = "/m";
} else if( !strcmp( stype, "VRAD" ) ) {
isspectral_ret = "m/s";
} else if( !strcmp( stype, "WAVE" ) ) {
isspectral_ret = "m";
} else if( !strcmp( stype, "VOPT" ) ) {
isspectral_ret = "m/s";
} else if( !strcmp( stype, "ZOPT" ) ) {
isspectral_ret = "";
} else if( !strcmp( stype, "AWAV" ) ) {
isspectral_ret = "m";
} else if( !strcmp( stype, "VELO" ) ) {
isspectral_ret = "m/s";
} else if( !strcmp( stype, "BETA" ) ) {
isspectral_ret = "";
}
/* Also check that the remaining part of CTYPE (the algorithm code) begins
with a minus sign or is blank. */
if( algcode[ 0 ] != '-' && strlen( algcode ) > 0 ) isspectral_ret = NULL;
}
/* Return null strings if the axis is not a spectral axis. */
if( ! isspectral_ret ) {
stype[ 0 ] = 0;
algcode[ 0 ] = 0;
}
/* Return the result. */
return isspectral_ret;
}
static AstMapping *LinearWcs( FitsStore *store, int i, char s,
const char *method, const char *class, int *status ) {
/*
* Name:
* LinearWcs
* Purpose:
* Create a Mapping describing a FITS-WCS linear algorithm
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstMapping *LinearWcs( FitsStore *store, int i, char s,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function uses the contents of the supplied FitsStore to create
* a Mapping which goes from Intermediate World Coordinate (known as "w"
* in the context of FITS-WCS paper III) to a linearly related axis.
*
* The returned Mapping is a ShiftMap which simply adds on the value of
* CRVALi.
* Parameters:
* store
* Pointer to the FitsStore structure holding the values to use for
* the WCS keywords.
* i
* The zero-based index of the spectral axis within the FITS header
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to a Mapping, or NULL if an error occurs.
*/
/* Local Variables: */
AstMapping *ret;
double crv;
/* Check the global status. */
ret = NULL;
if( !astOK ) return ret;
/* Get the CRVAL value for the specified axis. */
crv = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
if( crv == AST__BAD ) crv = 0.0;
/* Create a 1D ShiftMap which adds this value onto the IWCS value. */
if( crv != 0.0 ) {
ret = (AstMapping *) astShiftMap( 1, &crv, "", status );
} else {
ret = (AstMapping *) astUnitMap( 1, "", status );
}
return ret;
}
static AstMapping *LogAxis( AstMapping *map, int iax, int nwcs, double *lbnd_p,
double *ubnd_p, double crval, int *status ){
/*
* Name:
* LogAxes
* Purpose:
* Test a Frame axis to see if it logarithmically spaced in pixel coords.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstMapping *LogAxis( AstMapping *map, int iax, int nwcs, double *lbnd_p,
* double *ubnd_p, double crval )
* Class Membership:
* FitsChan member function.
* Description:
* A specified axis of the supplied Mappinhg is tested to see if it
* corresponds to the form
*
* S = Sr.exp( w/Sr )
*
* where "w" is one of the Mapping inputs, "S" is the specified
* Mapping output, and "Sr" is the supplied value of "crval". This
* is the form for a FITS log axis as defined in FITS-WCS paper III.
*
* If the above test is passed, a Mapping is returned from "S" to "w"
* (the inverseof the above expression).
* Parameters:
* map
* Pointer to the Mapping. This will usually be a Mapping from
* pixel coords to WCS coords.
* iax
* The index of the output of "map" which correspoinds to "S".
* nwcs
* The number of outputs from "map".
* lbnd_p
* Pointer to an array of double, with one element for each
* Mapping input coordinate. This should contain the lower bound
* of the input pixel box in each input dimension.
* ubnd_p
* Pointer to an array of double, with one element for each
* Mapping input coordinate. This should contain the upper bound
* of the input pixel box in each input dimension.
* crval
* The reference value ("Sr") to use. Must not be zero.
* Returned Value:
* If the specified axis is logarithmically spaced, a Mapping with
* "nwcs" inputs and "nwcs" outputs is returned. This Mapping transforms
* its "iax"th input using the transformation:
*
* w = Sr.Log( S/Sr )
*
* (where "S" is the Mapping is the "iax"th input and "w" is the
* "iax"th output). Other inputs are copied to the corresponding
* output without change. NULL is returned if the specified axis is
* not logarithmically spaced.
*/
/* Local Variables: */
AstMapping *result; /* Returned Mapping */
AstMapping *tmap0; /* A temporary Mapping */
AstMapping *tmap1; /* A temporary Mapping */
AstMapping *tmap2; /* A temporary Mapping */
AstMapping *tmap3; /* A temporary Mapping */
AstMapping *tmap4; /* A temporary Mapping */
const char *fexps[ 1 ]; /* Forward MathMap expressions */
const char *iexps[ 1 ]; /* Inverse MathMap expressions */
/* Initialise */
result = NULL;
/* Check the inherited status and crval value. */
if( !astOK || crval == 0.0 ) return result;
/* If the "log" algorithm is appropriate, the supplied axis (s) is related
to pixel coordinate (p) by s = Sr.EXP( a*p - b ). If this is the case,
then the log of s will be linearly related to pixel coordinates. To test
this, we create a CmpMap which produces log(s). */
fexps[ 0 ] = "logs=log(s)";
iexps[ 0 ] = "s=exp(logs)";
tmap1 = (AstMapping *) astMathMap( 1, 1, 1, fexps, 1, iexps,
"simpfi=1,simpif=1", status );
tmap2 = AddUnitMaps( tmap1, iax, nwcs, status );
tmap0 = (AstMapping *) astCmpMap( map, tmap2, 1, "", status );
tmap2 = astAnnul( tmap2 );
/* See if this Mapping is linear. */
if( IsMapLinear( tmap0, lbnd_p, ubnd_p, iax, status ) ) {
/* Create the Mapping which defines the IWC axis. This is the Mapping from
WCS to IWCS - "W = Sr.log( S/Sr )". Other axes are left unchanged by the
Mapping. The IWC axis has the same axis index as the WCS axis. */
tmap2 = (AstMapping *) astZoomMap( 1, 1.0/crval, "", status );
tmap3 = (AstMapping *) astCmpMap( tmap2, tmap1, 1, "", status );
tmap2 = astAnnul( tmap2 );
tmap2 = (AstMapping *) astZoomMap( 1, crval, "", status );
tmap4 = (AstMapping *) astCmpMap( tmap3, tmap2, 1, "", status );
tmap3 = astAnnul( tmap3 );
tmap2 = astAnnul( tmap2 );
result = AddUnitMaps( tmap4, iax, nwcs, status );
tmap4 = astAnnul( tmap4 );
}
/* Free resources. */
tmap0 = astAnnul( tmap0 );
tmap1 = astAnnul( tmap1 );
/* Return the result. */
return result;
}
static AstMapping *LogWcs( FitsStore *store, int i, char s,
const char *method, const char *class, int *status ) {
/*
* Name:
* LogWcs
* Purpose:
* Create a Mapping describing a FITS-WCS logarithmic algorithm
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstMapping *LogWcs( FitsStore *store, int i, char s,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function uses the contents of the supplied FitsStore to create
* a Mapping which goes from Intermediate World Coordinate (known as "w"
* in the context of FITS-WCS paper III) to a logarthmic version of w
* called "S" given by:
*
* S = Sr.exp( w/Sr )
*
* where Sr is the value of S corresponding to w=0.
* Parameters:
* store
* Pointer to the FitsStore structure holding the values to use for
* the WCS keywords.
* i
* The zero-based index of the axis within the FITS header
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to a Mapping, or NULL if an error occurs.
*/
/* Local Variables: */
AstMapping *ret;
char forexp[ 12 + DBL_DIG*2 ];
char invexp[ 12 + DBL_DIG*2 ];
const char *fexps[ 1 ];
const char *iexps[ 1 ];
double crv;
/* Check the global status. */
ret = NULL;
if( !astOK ) return ret;
/* Get the CRVAL value for the specified axis. Use a default of zero. */
crv = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
if( crv == AST__BAD ) crv = 0.0;
/* Create the MathMap, if possible. */
if( crv != 0.0 ) {
sprintf( forexp, "s=%.*g*exp(w/%.*g)", DBL_DIG, crv, DBL_DIG, crv );
sprintf( invexp, "w=%.*g*log(s/%.*g)", DBL_DIG, crv, DBL_DIG, crv );
fexps[ 0 ] = forexp;
iexps[ 0 ] = invexp;
ret = (AstMapping *) astMathMap( 1, 1, 1, fexps, 1, iexps, "simpfi=1,simpif=1", status );
}
/* Return the result */
return ret;
}
static int LooksLikeClass( AstFitsChan *this, const char *method,
const char *class, int *status ){
/*
* Name:
* LooksLikeClass
* Purpose:
* Does the FitsChan seem to use FITS-CLASS encoding?
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int LooksLikeClass( AstFitsChan *this, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Returns non-zero if the supplied FitsChan probably uses FITS-CLASS
* encoding. This is the case if it contains a DELTAV keyword and a
* keyword of the form VELO-xxx", where xxx is one of the accepted
* standards of rest, or "VLSR".
* Parameters:
* this
* Pointer to the FitsChan.
* 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:
* Non-zero if the encoding in use lookslike FITS-CLASS.
*/
/* Local Variables... */
int ret; /* Returned value */
/* Initialise */
ret = 0;
/* Check the global status. */
if( !astOK ) return ret;
/* See if there is a "DELTAV" card, and a "VELO-xxx" or "VLSR" card. */
if( astKeyFields( this, "DELTAV", 0, NULL, NULL ) && (
astKeyFields( this, "VLSR", 0, NULL, NULL ) ||
astKeyFields( this, "VELO-OBS", 0, NULL, NULL ) ||
astKeyFields( this, "VELO-HEL", 0, NULL, NULL ) ||
astKeyFields( this, "VELO-EAR", 0, NULL, NULL ) ||
astKeyFields( this, "VELO-LSR", 0, NULL, NULL ) ) ) {
ret = 1;
}
/* Return the result. */
return ret;
}
static void MakeBanner( const char *prefix, const char *middle,
const char *suffix,
char banner[ AST__FITSCHAN_FITSCARDLEN -
FITSNAMLEN + 1 ], int *status ) {
/*
* Name:
* MakeBanner
* Purpose:
* Create a string containing a banner comment.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void MakeBanner( const char *prefix, const char *middle,
* const char *suffix,
* char banner[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ], int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function creates a string which can be written as a FITS
* comment card to produce a banner heading (or tail) for an AST
* Object when it is written to a FitsChan. The banner will occupy
* the maximum permitted width for text in a FITS comment card.
* Parameters:
* prefix
* A pointer to a constant null-terminated string containing the
* first part of the text to appear in the banner.
* middle
* A pointer to a constant null-terminated string containing the
* second part of the text to appear in the banner.
* suffix
* A pointer to a constant null-terminated string containing the
* third part of the text to appear in the banner.
* banner
* A character array to receive the null-terminated result string.
* status
* Pointer to the inherited status variable.
* Notes:
* - The text to appear in the banner is constructed by
* concatenating the three input strings supplied.
*/
/* Local Variables: */
char token[] = "AST"; /* Identifying token */
int i; /* Loop counter for input characters */
int len; /* Number of output characters */
int ltok; /* Length of token string */
int mxlen; /* Maximum permitted output characters */
int start; /* Column number where text starts */
/* Check the global error status. */
if ( !astOK ) return;
/* Calculate the maximum number of characters that the output banner
can hold and the length of the token string. */
mxlen = AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN;
ltok = (int) strlen( token );
/* Calculate the column in which to start the text, so that it is
centred in the banner (with 4 non-text characters on each side). */
start = ltok + 2 + ( mxlen - ltok - 1 -
(int) ( strlen( prefix ) +
strlen( middle ) +
strlen( suffix ) ) - 1 - ltok ) / 2;
if ( start < ltok + 2 ) start = ltok + 2;
/* Start building the banner with the token string. */
len = 0;
for ( i = 0; token[ i ] && ( len < mxlen ); i++ ) {
banner[ len++ ] = token[ i ];
}
/* Then pad with spaces up to the start of the text. */
while ( len < start - 1 ) banner[ len++ ] = ' ';
/* Insert the prefix data, truncating it if it is too long. */
for ( i = 0; prefix[ i ] && ( len < mxlen - ltok - 1 ); i++ ) {
banner[ len++ ] = prefix[ i ];
}
/* Insert the middle data, truncating it if it is too long. */
for ( i = 0; middle[ i ] && ( len < mxlen - ltok - 1 ); i++ ) {
banner[ len++ ] = middle[ i ];
}
/* Insert the suffix data, truncating it if it is too long. */
for ( i = 0; suffix[ i ] && ( len < mxlen - ltok - 1 ); i++ ) {
banner[ len++ ] = suffix[ i ];
}
/* Pad the end of the text with spaces. */
while ( len < mxlen - ltok ) banner[ len++ ] = ' ';
/* Finish the banner with the token string. */
for ( i = 0; token[ i ] && ( len < mxlen ); i++ ) {
banner[ len++ ] = token[ i ];
}
/* Terminate the output string. */
banner[ len ] = '\0';
}
static AstMapping *MakeColumnMap( AstFitsTable *table, const char *col,
int isindex, int interp, const char *method,
const char *class, int *status ){
/*
* Name:
* MakeColumnMap
* Purpose:
* Create a Mapping describing a look-up table supplied in a cell of a
* FITS binary table.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstMapping *MakeColumnMap( AstFitsTable *table, const char *col,
* int isindex, int interp, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function returns a Mapping representing the array of values
* stored in row 1 of a named column of a supplied FitsTable. The
* array of values is treated as a look-up table following the prescription
* of FITS-WCS paper III (the "-TAB" algorithm). If the array has (N+1)
* dimensions (where N is one or more), the returned Mapping has N
* inputs and N outputs. The inputs correspond to FITS GRID coords
* within the array. FITS-WCS paper III requires that the first dimension
* in the array has a length of "N" and contains the N output values
* at each input values.
* Parameters:
* table
* Pointer to the Fitstable.
* col
* A string holding the name of the column to use.
* isindex
* Non-zero if the column hold an index array, zero if it holds a
* coordinate array.
* interp
* The value to use for the Interp attribute of the LutMap. A value
* of zero tells the LutMap class to use linear interpolation. Other
* values tell the LutMap class to use nearest neighbour interpolation.
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the Mapping, or NULL if an error occurs.
*/
/* Local Variables: */
AstMapping *result;
char *key;
double *lut;
int *dims;
int ndim;
int nel;
/* Initialise */
result = NULL;
/* Check the inherited status */
if( !astOK ) return result;
/* Get the number of dimensions spanned by the value in the named column. */
ndim = astGetColumnNdim( table, col );
/* First deal with index vectors. */
if( isindex ) {
/* FITS-WCS paper II mandates that index arrays must be 1-dimensional. */
if( ndim != 1 && astOK ) {
astError( AST__BADTAB, "%s(%s): Column '%s' has %d dimensions but it "
"holds an index vector and should therefore be 1-dimensional.",
status, method, class, col, ndim );
}
/* Get the length of the index vector. */
nel = astGetColumnLength( table, col );
/* Allocate memory to hold the array values, and to hold the cell key. */
lut = astMalloc( nel*sizeof( double ) );
key = astMalloc( strlen( col ) + 5 );
if( astOK ) {
/* Create the key for the table cell holding the required array. FITS-WCS
paper III mandates that tables always occur in the first row of the
table (and that the table only has one row). Ignore trailing spaces in
the column name. */
sprintf( key, "%.*s(1)", (int) astChrLen( col ), col );
/* Copy the array values into the above memory. */
if( astMapGet1D( table, key, nel, &nel, lut ) ) {
/* Create a 1D LutMap. FITS-WCS paper III (sec 6.1.2) mandates that the input
corresponds to FITS grid coord (i.e. 1.0 at the centre of the first entry).
Ensure the LutMap uses linear interpolation. */
result = (AstMapping *) astLutMap( nel, lut, 1.0, 1.0,
"LutInterp=%d", status, interp );
/* Report an error if the table cell was empty. */
} else if( astOK ) {
astError( AST__BADTAB, "%s(%s): Row 1 of the binary table "
"contains no value for column '%s'.", status, method,
class, col );
}
}
/* Free memory. */
lut = astFree( lut );
key = astFree( key );
/* Now deal with coordinate arrays. */
} else {
/* Get the shape of the array. */
dims = astMalloc( sizeof( int )*ndim );
astColumnShape( table, col, ndim, &ndim, dims );
/* For coordinate arrays, check the length of the first axis is "ndim-1", as
required by FITS-WCS paper III. */
if( astOK && dims[ 0 ] != ndim - 1 && !isindex ) {
astError( AST__BADTAB, "%s(%s): The first dimension of the coordinate "
"array has length %d (should be %d since the array has %d "
"dimensions).", status, method, class, dims[ 0 ], ndim - 1,
ndim );
}
/* We can currently only handle 1D look-up tables. These are stored in
notionally two-dimensional arrays in which the first dimension is
degenarate (i.e. spans only a single element). */
if( ndim > 2 ) {
if( astOK ) astError( AST__INTER, "%s(%s): AST can currently only "
"handle 1-dimensional coordinate look-up tables "
"(the supplied table has %d dimensions).", status,
method, class, ndim - 1 );
/* Handle 1-dimensional look-up tables. */
} else if( astOK ){
/* Allocate memory to hold the array values, and to hold the cell key. */
lut = astMalloc( dims[ 1 ]*sizeof( double ) );
key = astMalloc( strlen( col ) + 5 );
if( astOK ) {
/* Create the key for the table cell holding the required array. FITS-WCS
paper III mandates that tables always occur in the first row of the
table (and that the table only has one row). Ignore trailing spaces in
the column name. */
sprintf( key, "%.*s(1)", (int) astChrLen( col ), col );
/* Copy the array values into the above memory. */
if( astMapGet1D( table, key, dims[ 1 ], dims, lut ) ) {
/* Create a 1D LutMap. FITS-WCS paper III (sec 6.1.2) mandates that the input
corresponds to FITS grid coord (i.e. 1.0 at the centre of the first entry).
Ensure the LutMap uses linear interpolation. */
result = (AstMapping *) astLutMap( dims[ 1 ], lut, 1.0, 1.0,
"LutInterp=%d", status,
interp );
/* Report an error if the table cell was empty. */
} else if( astOK ) {
astError( AST__BADTAB, "%s(%s): Row 1 of the binary table "
"contains no value for column '%s'.", status, method,
class, col );
}
}
/* Free memory. */
lut = astFree( lut );
key = astFree( key );
}
dims = astFree( dims );
}
/* Issue a context message and annul the returned Mapping if an error
has occurred. */
if( !astOK ) {
astError( astStatus, "%s(%s): Cannot read a look-up table for a "
"tabular WCS axis from column '%s' of a FITS binary table.",
status, method, class, col );
result = astAnnul( result );
}
/* Return the result. */
return result;
}
static AstFrameSet *MakeFitsFrameSet( AstFitsChan *this, AstFrameSet *fset,
int ipix, int iwcs, int encoding,
const char *method, const char *class,
int *status ) {
/*
* Name:
* MakeFitsFrameSet
* Purpose:
* Create a FrameSet which conforms to the requirements of the FITS-WCS
* papers.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstFrameSet *MakeFitsFrameSet( AstFitsChan *this, AstFrameSet *fset,
* int ipix, int iwcs, int encoding,
* const char *method, const char *class,
* int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function constructs a new FrameSet holding the pixel and WCS
* Frames from the supplied FrameSet, but optionally extends the WCS
* Frame to include any extra axes needed to conform to the FITS model.
* Currently, this function does the following:
*
* - if the WCS Frame contains a 1D spectral Frame with a defined celestial
* reference position (SpecFrame attributes RefRA and RefDec), then
* it ensures that the WCS Frame also contains a pair of celestial
* axes (such axes are added if they do not already exist within the
* supplied WCS Frame). The pixel->WCS Mapping is adjusted accordingly.
*
* - if the WCS Frame contains a spectral axis and a pair of celestial
* axes, then the SpecFrame attributes RefRA and RefDec are set to the
* reference position defined by the celestial axes. The pixel->WCS
* Mapping is adjusted accordingly.
*
* - NULL is returned if the WCS Frame contains more than one spectral
* axis.
*
* - NULL is returned if the WCS Frame contains more than one pair of
* celestial axes.
*
* - Any isolated sky axes (i.e. not contained within a SkyFrame) are
* re-mapped from radians into degrees.
* Parameters:
* this
* The FitsChan.
* fset
* The FrameSet to check.
* ipix
* The index of the FITS pixel Frame within "fset".
* iwcs
* The index of the WCS Frame within "fset".
* encoding
* The encoding in use.
* 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:
* A new FrameSet which confoms to the requirements of the FITS-WCS
* papers. The base Frame in this FrameSet will be the FITS pixel
* Frame, and the current Frame will be the WCS Frame. NULL is
* returned if an error has already occurred, or if the FrameSet cannot
* be produced for any reason.
*/
/* Local Variables: */
AstFitsChan *fc; /* Pointer to temporary FitsChan */
AstFrame *pframe; /* Pointer to the primary Frame */
AstFrame *pixfrm; /* Pointer to the FITS pixel Frame */
AstFrame *tfrm0; /* Pointer to a temporary Frame */
AstFrame *tfrm; /* Pointer to a temporary Frame */
AstFrame *wcsfrm; /* Pointer to the FITS WCS Frame */
AstFrameSet *ret; /* The returned FrameSet */
AstFrameSet *tfs; /* Pointer to a temporary FrameSet */
AstMapping *map1; /* Pointer to pre-WcsMap Mapping */
AstMapping *map3; /* Pointer to post-WcsMap Mapping */
AstMapping *map; /* Pointer to the pixel->wcs Mapping */
AstMapping *remap; /* Total Mapping from internal to external units */
AstMapping *smap; /* Simplified Mapping */
AstMapping *tmap0; /* Pointer to a temporary Mapping */
AstMapping *tmap1; /* Pointer to a temporary Mapping */
AstMapping *tmap2; /* Pointer to a temporary Mapping */
AstMapping *tmap; /* Pointer to a temporary Mapping */
AstMapping *umap; /* 1D Mapping from internal to external units */
AstSpecFrame *skyfrm; /* Pointer to the SkyFrame within WCS Frame */
AstSpecFrame *specfrm; /* Pointer to the SpecFrame within WCS Frame */
AstWcsMap *map2; /* Pointer to WcsMap */
char card[ AST__FITSCHAN_FITSCARDLEN + 1 ]; /* A FITS header card */
char equinox_attr[ 13 ];/* Name of Equinox attribute for sky axes */
char system_attr[ 12 ]; /* Name of System attribute for sky axes */
const char *eqn; /* Pointer to original sky Equinox value */
const char *extunit; /* External units string */
const char *intunit; /* Internal units string */
const char *skysys; /* Pointer to original sky System value */
double con; /* Constant axis value */
double reflat; /* Celestial latitude at reference point */
double reflon; /* Celestial longitude at reference point */
int *perm; /* Pointer to axis permutation array */
int iax; /* Axis inex */
int icurr; /* Index of original current Frame in returned FrameSet */
int ilat; /* Celestial latitude index within WCS Frame */
int ilon; /* Celestial longitude index within WCS Frame */
int npix; /* Number of pixel axes */
int nwcs; /* Number of WCS axes */
int ok; /* Is the supplied FrameSet usable? */
int paxis; /* Axis index within the primary Frame */
int rep; /* Was error reporting switched on? */
/* Initialise */
ret = NULL;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Get copies of the pixel Frame, the WCS Frame and the Mapping. */
tfrm = astGetFrame( fset, ipix );
pixfrm = astCopy( tfrm );
tfrm = astAnnul( tfrm );
tfrm = astGetFrame( fset, iwcs );
wcsfrm = astCopy( tfrm );
tfrm = astAnnul( tfrm );
tmap = astGetMapping( fset, ipix, iwcs );
map = astCopy( tmap );
tmap = astAnnul( tmap );
/* Store the number of pixel and WCS axes. */
npix = astGetNaxes( pixfrm );
nwcs = astGetNaxes( wcsfrm );
/* Search the WCS Frame for SkyFrames and SpecFrames. */
umap = NULL;
remap = NULL;
specfrm = NULL;
skyfrm = NULL;
ok = 1;
ilat = -1;
ilon = -1;
for( iax = 0; iax < nwcs; iax++ ) {
/* Obtain a pointer to the primary Frame containing the current WCS axis. */
astPrimaryFrame( wcsfrm, iax, &pframe, &paxis );
/* If the current axis is a SpecFrame, save a pointer to it. If we have already
found a SpecFrame, abort. */
if( astIsASpecFrame( pframe ) ) {
if( specfrm ) {
ok = 0;
break;
}
specfrm = astClone( pframe );
/* If the current axis is a SkyFrame, save a pointer to it, and its WCS
index. If we have already found a different SkyFrame, abort. */
} else if( IsASkyFrame( pframe ) ) {
if( skyfrm ) {
if( pframe != (AstFrame *) skyfrm ) {
ok = 0;
break;
}
} else {
skyfrm = astClone( pframe );
}
if( paxis == 0 ) {
ilon = iax;
} else {
ilat = iax;
}
/* If the internal and external units differ, attempt to remap the axis
into its external units. */
} else {
/* Get the string describing the external units (the "Unit" attribute). */
extunit = astGetUnit( pframe, paxis );
/* Get the string describing the internal units (the "InternalUnit"
attribute). */
intunit = astGetInternalUnit( pframe, paxis );
/* If they are the same, we do not need to modify this axis. */
if( astOK && strcmp( extunit, intunit ) ){
/* Otherwise, get the mapping from the internal units to the external
units, if possible. Ignore any error reported by unitmapper. */
rep = astReporting( 0 );
umap = astUnitMapper( intunit, extunit, NULL, NULL );
if( !astOK ) astClearStatus;
astReporting( rep );
if( !umap ) {
/* If the above failed, ensure that the external units are the same as
the internal units (except that internal radians are converted to
external degrees). */
if( !strcmp( intunit, "rad" ) ) {
umap = (AstMapping *) astZoomMap( 1, AST__DR2D, " ", status );
extunit = "deg";
} else {
extunit = intunit;
}
astSetUnit( wcsfrm, iax, extunit );
}
}
}
/* If no change is needed for the mapping for this axis, use a UnitMap. */
if( !umap ) umap = (AstMapping *) astUnitMap( 1, " ", status );
/* Extend the parallel CmpMap to encompass the current axis. */
if( remap ) {
tmap = (AstMapping *) astCmpMap( remap, umap, 0, " ", status );
(void) astAnnul( remap );
remap = tmap;
} else {
remap = astClone( umap );
}
/* Free resources. */
umap = astAnnul( umap );
pframe = astAnnul( pframe );
}
/* See if the pixel->wcs mapping needs to be modified to take account of
any changes to axis units. */
smap = astSimplify( remap );
if( ! astIsAUnitMap( smap ) ) {
tmap = (AstMapping *) astCmpMap( map, remap, 1, " ", status );
map = astAnnul( map );
remap = astAnnul( remap );
map = tmap;
}
/* If the supplied FrameSet is usable... */
if( ok ) {
/* If we did not find a SpecFrame, return a FrameSet made from the base
and current Frames in the supplied FrameSet. */
if( !specfrm ) {
ret = astFrameSet( pixfrm, "", status );
astAddFrame( ret, AST__BASE, map, wcsfrm );
/* If we have a SpecFrame, proceed. */
} else {
/* Check that both the RefRA and RefDec attributes of the SpecFrame are set.
If not, return a FrameSet made from the base and current Frames in the
supplied FrameSet. Also do this if the original WCS Frame contains 3
or more axes (since it is almost always inappropriate to add extra sky
axes in such circumestances). But if the other axes form a skyfram,
then we need to make sure they use the right refrence point. */
if( !astTestRefRA( specfrm ) || !astTestRefDec( specfrm ) ||
( nwcs > 2 && !skyfrm ) ) {
ret = astFrameSet( pixfrm, "", status );
astAddFrame( ret, AST__BASE, map, wcsfrm );
/* If we have a celestial reference position for the spectral axis, ensure
it is described correctly by a pair of celestial axes. */
} else {
/* If the WCS Frame does not contain any celestial axes, we add some now. */
if( !skyfrm ) {
/* The easiest way to create the required mapping from pixel to celestial
to create a simple FITS header and read it in via a FitsChan to create a
FrameSet. */
fc = astFitsChan( NULL, NULL, "", status );
astPutFits( fc, "CRPIX1 = 0", 0 );
astPutFits( fc, "CRPIX2 = 0", 0 );
astPutFits( fc, "CDELT1 = 0.0003", 0 );
astPutFits( fc, "CDELT2 = 0.0003", 0 );
astPutFits( fc, "CTYPE1 = 'RA---TAN'", 0 );
astPutFits( fc, "CTYPE2 = 'DEC--TAN'", 0 );
astPutFits( fc, "RADESYS = 'FK5'", 0 );
astPutFits( fc, "EQUINOX = 2000.0", 0 );
sprintf( card, "CRVAL1 = %.*g", DBL_DIG,
AST__DR2D*astGetRefRA( specfrm ) );
astPutFits( fc, card, 0 );
sprintf( card, "CRVAL2 = %.*g", DBL_DIG,
AST__DR2D*astGetRefDec( specfrm ) );
astPutFits( fc, card, 0 );
sprintf( card, "MJD-OBS = %.*g", DBL_DIG,
TDBConv( astGetEpoch( specfrm ), AST__UTC, 1,
"astWrite", "FitsChan", status ) );
astPutFits( fc, card, 0 );
astClearCard( fc );
tfs = astRead( fc );
if( tfs ) {
/* Create the new pixel->wcs Mapping. First get the 2-input,2-output
Mapping between pixel and sky coords from the above FrameSet. Then add
this Mapping in parallel with the original pixel->wcs Mapping. */
tmap0 = astGetMapping( tfs, AST__BASE, AST__CURRENT );
tmap1 = (AstMapping *) astCmpMap( map, tmap0, 0, "", status );
tmap0 = astAnnul( tmap0 );
/* We now have a (npix+2)-input,(nwcs+2)-output Mapping. We now add a
PermMap in series with this which feeds the constant value 0.0 (the
CRPIX value in the above set of FITS headers) into the 2 pixel axes
corresponding to RA and Dec. This PermMap has npix-inputs and (npix+2)
outputs. The total Mapping then has npix inputs and (nwcs+2) outputs. */
perm = astMalloc( sizeof( int )*(size_t) ( npix + 2 ) );
if( astOK ) {
for( iax = 0; iax < npix; iax++ ) perm[ iax ] = iax;
perm[ npix ] = -1;
perm[ npix + 1 ] = -1;
con = 0.0;
tmap0 = (AstMapping *) astPermMap( npix, perm, npix + 2, perm, &con, "", status );
tmap2 = (AstMapping *) astCmpMap( tmap0, tmap1, 1, "", status );
tmap0 = astAnnul( tmap0 );
tmap1 = astAnnul( tmap1 );
/* We now create the new WCS Frame with the extra RA and Dec axes. This
is just a CmpFrame made up of the original WCS Frame and the new
SkyFrame. */
tfrm = astGetFrame( tfs, AST__CURRENT );
tfrm0 = (AstFrame *) astCmpFrame( wcsfrm, tfrm, "", status );
tfrm = astAnnul( tfrm );
/* Construct the returned FrameSet. */
ret = astFrameSet( pixfrm, "", status );
astAddFrame( ret, AST__BASE, tmap2, tfrm0 );
tmap2 = astAnnul( tmap2 );
tfrm0 = astAnnul( tfrm0 );
/* Free remaining resources. */
perm = astFree( perm );
}
tfs = astAnnul( tfs );
}
fc = astAnnul( fc );
/* If the WCS Frame does contain celestial axes we make sure that the
SpecFrame uses the same reference point. */
} else {
/* The returned FrameSet has no extra Frames (although some attributes
may be changed) so just create a new FrameSet equaivalent to the supplied
FrameSet. */
tfs = astFrameSet( pixfrm, "", status );
astAddFrame( tfs, AST__BASE, map, wcsfrm );
/* The RefRA and RefDec attributes of the SpecFrame must be set in FK5
J2000. Therefore we need to know the celestial reference point in
FK5 J2000. Modify the SkyFrame within the FrameSet to represent FK5
J2000, noting the original sky system and equinox first so that they
can be re-instated (if set) later on. */
sprintf( system_attr, "System(%d)", ilon + 1 );
if( astTest( tfs, system_attr ) ) {
skysys = astGetC( tfs, system_attr );
} else {
skysys = NULL;
}
astSetC( tfs, system_attr, "FK5" );
sprintf( equinox_attr, "Equinox(%d)", ilon + 1 );
if( astTest( tfs, equinox_attr ) ) {
eqn = astGetC( tfs, equinox_attr );
} else {
eqn = NULL;
}
astSetC( tfs, equinox_attr, "J2000" );
/* The reference point for the celestial axes is defined by the WcsMap
contained within the Mapping. Split the mapping up into a list of serial
component mappings, and locate the first WcsMap in this list. The first
Mapping returned by this call is the result of compounding all the
Mappings up to (but not including) the WcsMap, the second returned Mapping
is the (inverted) WcsMap, and the third returned Mapping is anything
following the WcsMap. Only proceed if one and only one WcsMap is found. */
tmap0 = astGetMapping( tfs, AST__BASE, AST__CURRENT );
if( SplitMap( tmap0, astGetInvert( tmap0 ), ilon, ilat, &map1, &map2, &map3, status ) ){
/* The reference point in the celestial coordinate system is found by
transforming the fiducial point in native spherical co-ordinates
into absolute physical coordinates using map3. */
if( GetFiducialWCS( map2, map3, ilon, ilat, &reflon, &reflat, status ) ){
/* Use reflon and reflat (which represent FK5 J2000 RA and Dec) to set
the values of the SpecFrame RefRA and RefDec attributes. Format the
values first so that we can use the FrameSet astSetC method, and so
maintain the FrameSet integrity. */
astSetC( tfs, "RefRA", astFormat( wcsfrm, ilon, reflon ) );
astSetC( tfs, "RefDec", astFormat( wcsfrm, ilat, reflat ) );
/* If succesfull, return a pointer to the FrameSet. */
if( astOK ) ret = astClone( tfs );
}
/* Release resources. */
map1 = astAnnul( map1 );
map2 = astAnnul( map2 );
map3 = astAnnul( map3 );
/* If no WcsMap was found, the celestial axes have no reference point and
so we can retain the original spectral reference point, so just return
the temporary FrameSet. */
} else if( astOK ) {
ret = astClone( tfs );
}
tmap0 = astAnnul( tmap0 );
/* Re-instate the original sky system and equinox. */
if( skysys ) astSetC( tfs, system_attr, skysys );
if( eqn ) astSetC( tfs, equinox_attr, eqn );
/* Release resources. */
tfs = astAnnul( tfs );
}
}
}
}
/* Add a new current Frame into the FrameSet which increases the chances of
the requested encoding being usable. The index of the original current
Frame is returned, or AST__NOFRAME if no new Frame was added. */
icurr = AddEncodingFrame( this, ret, encoding, method, class, status );
/* If a new Frame was added, remove the original current Frame. */
if( icurr != AST__NOFRAME ) astRemoveFrame( ret, icurr );
/* Free resources. */
if( specfrm ) specfrm = astAnnul( specfrm );
if( skyfrm ) skyfrm = astAnnul( skyfrm );
pixfrm = astAnnul( pixfrm );
wcsfrm = astAnnul( wcsfrm );
map = astAnnul( map );
/* Return NULL if an error has occurred. */
if( !astOK && ret ) ret = astAnnul( ret );
/* Return the result. */
return ret;
}
static void MakeIndentedComment( int indent, char token,
const char *comment, const char *data,
char string[ AST__FITSCHAN_FITSCARDLEN -
FITSNAMLEN + 1 ], int *status ) {
/*
* Name:
* MakeIndentedComment
* Purpose:
* Create a comment string containing an indentation bar.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void MakeIndentedComment( int indent, char token,
* const char *comment, const char *data,
* char string[ AST__FITSCHAN_FITSCARDLEN -
* FITSNAMLEN + 1 ], int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function creates a string that may be used as text in a
* FITS comment card. The string contains a textual comment
* preceded by a bar (a line of characters) whose length can be
* used to indicate a level of indentation (in the absence of any
* way of indenting FITS keywords).
* Parameters:
* indent
* The level of indentation, in characters.
* token
* The character used to form the indentation bar.
* comment
* A pointer to a constant null-terminated string containing the text
* of the comment to be included.
* data
* A pointer to a constant null-terminated string containing any
* textual data to be appended to the comment.
* string
* A character array to receive the output string.
* status
* Pointer to the inherited status variable.
* Notes:
* - The comment text that appears in the output string is formed by
* concatenating the "comment" and "data" strings.
*/
/* Local Variables: */
int i; /* Loop counter for input characters */
int len; /* Number of output characters */
int mxlen; /* Maximum length of output string */
/* Check the global error status. */
if ( !astOK ) return;
/* Calculate the maximum number of characters that the output string
can accommodate. */
mxlen = AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN;
/* Start the string with "indent" copies of the token character, but
without exceeding the output string length. */
len = 0;
while ( ( len < indent ) && ( len < mxlen ) ) string[ len++ ] = token;
/* Pad with spaces up to the start of the comment, if necessary. */
while ( len < ( FITSCOMCOL - FITSNAMLEN - 1 ) ) {
string[ len++ ] = ' ';
}
/* Add "/ " to introduce the comment (strictly not necessary as the
whole card will be a comment, but it matches the other non-comment
cards). Truncate if necessary. */
for ( i = 0; ( i < 2 ) && ( len < mxlen ); i++ ) {
string[ len++ ] = "/ "[ i ];
}
/* Append the comment string, truncating it if it is too long. */
for ( i = 0; comment[ i ] && ( len < mxlen ); i++ ) {
string[ len++ ] = comment[ i ];
}
/* Append the data string, again truncating if too long. */
for ( i = 0; data[ i ] && ( len < mxlen ); i++ ) {
string[ len++ ] = data[ i ];
}
/* Terminate the output string. */
string[ len ] = '\0';
}
static void MakeIntoComment( AstFitsChan *this, const char *method,
const char *class, int *status ){
/*
* Name:
* MakeIntoComment
* Purpose:
* Convert a card into a FITS COMMENT card.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void MakeIntoComment( AstFitsChan *this, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function formats the card stored just prior to the current card,
* and re-stores it as a COMMENT card. It is used (when writing an Object
* to a FitsChan) to output values that are not "set" and which are
* therefore provided for information only, and should not be read back.
* the COMMENT card has the effect of "commenting out" the value.
* Parameters:
* this
* Pointer to the FitsChan.
* method
* Calling method.
* class
* Object class.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
char card[ AST__FITSCHAN_FITSCARDLEN + 1 ]; /* Character buffer for FITS card data */
/* Check the global error status. */
if ( !astOK ) return;
/* Move the current card backwards by one card. */
MoveCard( this, -1, method, class, status );
/* Format the new current card. */
FormatCard( this, card, method, status );
/* Write the resulting string to the FitsChan as the contents of a COMMENT
card, overwriting the existing card. The current card is incremented
by this call so that it refers to the same card as on entry. */
astSetFitsCom( this, "COMMENT", card, 1 );
}
static int MakeIntWorld( AstMapping *cmap, AstFrame *fr, int *wperm, char s,
FitsStore *store, double *dim,
const char *method, const char *class, int *status ){
/*
* Name:
* MakeIntWorld
* Purpose:
* Create FITS header values which map grid into intermediate world
* coords.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int MakeIntWorld( AstMapping *cmap, AstFrame *fr, int *wperm, char s,
* FitsStore *store, double *dim,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function adds values to the supplied FitsStore which describe
* the transformation from grid (pixel) coords to intermediate world
* coords. The values added to the FitsStore correspond to the CRPIXj,
* PCi_j, CDELTi and WCSAXES keywords, and are determined by examining the
* suppliedMapping, which must be linear with an optional shift of
* origin (otherwise a value of zero is returned).
*
* Much of the complication in the algorithm arises from the need to
* support cases where the supplied Mapping has more outputs than
* inputs. In these case we add some "degenerate" axes to the grid
* coord system, choosing their unit vectors to be orthogonal to all
* the other grid axes. It is assumed that degenerate axes will never
* be used to find a position other than at the axis value of 1.0.
*
* NOTE, appropriate values for CRVAL keywords should have been stored
* in the FitsStore before calling this function (since this function may
* modify them).
* Parameters:
* cmap
* A pointer to a Mapping which transforms grid coordinates into
* intermediate world coordinates. The number of outputs must be
* greater than or equal to the number of inputs.
* fr
* Pointer to the final WCS coordinate Frame.
* wperm
* Pointer to an array of integers with one element for each axis of
* the "fr" Frame. Each element holds the zero-based index of the
* FITS-WCS axis (i.e. the value of "i" in the keyword names "CTYPEi",
* "CDi_j", etc) which describes the Frame axis.
* s
* The co-ordinate version character. A space means the primary
* axis descriptions. Otherwise the supplied character should be
* an upper case alphabetical character ('A' to 'Z').
* store
* A pointer to the FitsStore into which the calculated CRPIX and
* CDi_j values are to be put.
* dim
* An array holding the image dimensions in pixels. AST__BAD can be
* supplied for any unknwon dimensions.
* 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:
* A value of 1 is returned if the CRPIX and CDi_j values are
* succesfully calculated. Zero is returned otherwise.
* Notes:
* - Zero is returned if an error occurs.
*/
/* Local Variables: */
AstFrame *pfrm;
AstFrame *sfrm;
AstMapping *map;
AstPointSet *psetw;
AstPointSet *psetg;
double **fullmat;
double **partmat;
double **ptrg;
double **ptrw;
double *c;
double *cdelt;
double *cdmat;
double *colvec;
double *d;
double *g;
double *g0;
double *m;
double *mat;
double *tol;
double *w0;
double *y;
double cd;
double crp;
double crv;
double cv;
double det;
double err;
double k;
double mxcv;
double skydiag1;
double skydiag0;
double val;
int *iw;
int *lin;
int *pperm;
int *skycol;
int i;
int ii;
int j;
int jax;
int jj;
int nin;
int nout;
int nwcs;
int paxis;
int ret;
int sing;
int skycol0;
int skycol1;
/* Initialise */
ret = 0;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Simplify the supplied Mapping to reduce rounding errors when
transforming points. */
map = astSimplify( cmap );
/* Get the number of inputs and outputs for the Mapping. Return if the
number of outputs is smaller than the number of inputs. */
nin = astGetNin( map );
nout = astGetNout( map );
if( nout < nin ) return ret;
/* Note the number of final World Coordinate axes (not necessarily the
same as "nout", since some intermediate axes may be discarded by a
later PermMap. */
nwcs = astGetNaxes( fr );
/* Allocate work space. */
g = astMalloc( sizeof(double)*(size_t) nin );
g0 = astMalloc( sizeof(double)*(size_t) nin );
w0 = astMalloc( sizeof(double)*(size_t) nout );
tol = astMalloc( sizeof(double)*(size_t) nout );
partmat = astMalloc( sizeof(double *)*(size_t) nout );
lin = astMalloc( sizeof(int)*(size_t) nout );
pperm = astMalloc( sizeof(int)*(size_t) nout );
skycol = astMalloc( sizeof(int)*(size_t) nout );
cdmat = astMalloc( sizeof(double)*(size_t) (nout*nout) );
cdelt = astMalloc( sizeof(double)*(size_t) nout );
/* For safety, initialise all other pointers. */
if( partmat ) for( j = 0; j < nout; j++ ) partmat[ j ] = NULL;
fullmat = NULL;
/* Create a PointSet to hold an input (grid) position for each axis, plus
an extra one. Create two other PointSets to hold corresponding
output (IWC) coordinates. */
psetg = astPointSet( nin + 1, nin, "", status );
ptrg = astGetPoints( psetg );
psetw = astPointSet( nin + 1, nout, "", status );
ptrw = astGetPoints( psetw );
/* Check the pointers can be used safely. */
if( astOK ) {
/* Assume success. */
ret = 1;
/* The next section finds a 'root' grid position for which the
corresponding IWC coordinates are all good. It also finds these IWC
coordinates, together with the IWC coordinates of "nin" points which
are a unit distance away from the root grid position along each
grid axis. It also finds an estimate of the rounding error in each
Mapping output.
================================================================= */
ret = FindBasisVectors( map, nin, nout, dim, psetg, psetw, status );
/* Save the grid root position in "g0". */
for( j = 0; j < nin; j++ ) g0[ j ] = ptrg[ j ][ 0 ];
/* Save the transformed root position in "w0". This is the grid root
position represented as a vector within the Intermediate World
Coordinate system. */
for( j = 0; j < nout; j++ ) {
w0[ j ] = ptrw[ j ][ 0 ];
/* Find the tolerance for positions on the j'th IWC axis. This is one
tenth of the largest change in the j'th IWC axis value caused by
moving out 1 pixel along any grid axis. */
tol[ j ] = 0.0;
for( i = 0; i < nin; i++ ) {
err = fabs( ptrw[ j ][ i + 1 ] - w0[ j ] );
if( err > tol[ j ] ) tol[ j ] = err;
}
tol[ j ] *= 0.1;
/* If the tolerance is zero (e.g. as is produced for degenerate axes),
then use a tolerance equal to a very small fraction of hte degenerate
axis value. If the axis value is zero use a fixed small value. */
if( tol[ j ] == 0.0 ) tol[ j ] = w0[ j ]*DBL_EPSILON*1.0E5;
if( tol[ j ] == 0.0 ) tol[ j ] = sqrt( DBL_MIN );
}
/* The next section finds the CD matrix.
===================================== */
/* Initialise the CD matrix elements to "all missing". */
for( i = 0; i < nout*nout; i++ ) cdmat[ i ] = AST__BAD;
/* The elements of column "j" of the CD matrix form a vector (in Intermediate
World Coords) which corresponds to a unit vector along grid axis "j".
We now find these vectors for all the grid axes represented by the
inputs to the supplied Mapping. */
for( i = 0; i < nin && ret; i++ ) {
/* Form a unit vector along the current input axis. */
for( ii = 0; ii < nin; ii++ ) g[ ii ] = 0.0;
g[ i ] = 1.0;
/* Fit a straight line (within IWC) to the current input axis of the Mapping.
The IWC vector corresponding to a unit vector along the current input axis
is returned if the Mapping is linear. A NULL pointer is returned if the
Mapping is not linear. */
partmat[ i ] = FitLine( map, g, g0, w0, dim[ i ], tol, status );
/* If unsuccesful, indicate failure and break out of the loop. */
if( !partmat[ i ] ) {
ret = 0;
break;
}
}
/* If the number of outputs for "map" is larger than the number of inputs,
then we will still be missing some column vectors for the CDi_j matrix
(which has to be square). We invent these such that the they are
orthogonal to all the other column vectors. Only do this if the
Mapping is linear. */
if( ret ) {
fullmat = OrthVectorSet( nout, nin, partmat, status );
if( !fullmat ) ret = 0;
}
/* Check everything is OK. */
if( ret ) {
/* Check that the full matrix is invertable, and if not, see if there is
any way to make it invertable. */
MakeInvertable( fullmat, nout, dim, status );
/* Set up an array holding index of the Mapping output corresponding to
each IWC axis (the inverse of "wperm"). Also look for matching pairs of
celestial WCS axes. For the first such pair, note the corresponding
column indices and the diagonal element of the matrix which gives the
scaling for the axis (taking account of the permutation of WCS axes).
Also note if the Mapping from intermediate world coords to final world
coords is linear for each axis (this is assumed to be the case if the
axis is part of a simple Frame). */
sfrm = NULL;
skydiag0 = AST__BAD;
skydiag1 = AST__BAD;
skycol0 = -1;
skycol1 = -1;
for( i = 0; i < nout; i++ ) {
pperm[ wperm[ i ] ] = i;
astPrimaryFrame( fr, i, &pfrm, &paxis );
if( IsASkyFrame( pfrm ) ) {
skycol[ wperm[ i ] ] = paxis + 1;
lin[ i ] = 0;
if( !sfrm ) {
sfrm = astClone( pfrm );
skycol0 = wperm[ i ];
skydiag0 = fullmat[ skycol0 ][ i ];
} else if( sfrm == pfrm ) {
skycol1 = wperm[ i ];
skydiag1 = fullmat[ skycol1 ][ i ];
}
} else {
skycol[ wperm[ i ] ] = 0;
lin[ i ] = !strcmp( astGetClass( pfrm ), "Frame" );
}
pfrm = astAnnul( pfrm );
}
if( sfrm ) sfrm = astAnnul( sfrm );
/* We now have the complete CDi_j matrix. Now to find the CRPIX values.
These are the grid coords of the reference point (which corresponds to
the origin of Intermediate World Coords). The "w0" array currently holds
the position of the root position, as a position within IWC, and the
"g0" array holds the corresponding position in grid coordinates. We
also have IWC vectors which correspond to unit vectors on each grid
axis. The CRPIX values are defined by the matrix equation
w0 = fullmat*( g0 - crpix )
The "g0" array only contains "nin" values. If nout>nin, then the
missing g0 values will be assumed to be zero when we come to find the
CRPIX values below.
We use palDmat to solve this system of simultaneous equations to get
crpix. The "y" array initially holds "w0" but is over-written to hold
"g0 - crpix". */
mat = astMalloc( sizeof( double )*(size_t)( nout*nout ) );
y = astMalloc( sizeof( double )*(size_t) nout );
iw = astMalloc( sizeof( int )*(size_t) nout );
if( astOK ) {
m = mat;
for( i = 0; i < nout; i++ ) {
for( j = 0; j < nout; j++ ) *(m++) = fullmat[ j ][ i ];
y[ i ] = w0[ i ];
}
palDmat( nout, mat, y, &det, &sing, iw );
}
mat = astFree( mat );
iw = astFree( iw );
/* Loop round all axes, storing the column vector pointer. */
for( j = 0; j < nout; j++ ) {
colvec = fullmat[ j ];
/* Get the CRPIX values from the "y" vector created above by palDmat.
First deal with axes for which there are Mapping inputs. */
if( j < nin ) {
crp = g0[ j ] - y[ j ];
/* If this is a grid axis which has been created to represent a "missing"
input to the mapping, we need to add on 1.0 to the crpix value found
above. This is because the "w0" vector corresponds to a value of zero
on any missing axes, but the FITS grid value for any missing axes is
1.0. */
} else {
crp = 1.0 - y[ j ];
}
/* Store the CD and CRPIX values for axes which correspond to inputs
of "map". The CD matrix elements are stored in an array and are
converted later to the corresponding PC and CDELT values. */
if( j < nin || crp == 0.0 ) {
for( i = 0; i < nout; i++ ) {
cdmat[ wperm[ i ]*nout+j ] = colvec[ i ] ;
}
SetItem( &(store->crpix), 0, j, s, crp, status );
/* The length of the unit vector along any "degenerate" axes was fixed
arbitrarily at 1.0 by the call to OrthVectorSet. We can probably
choose a more appropriate vector length. The choice shouldn't make any
difference to the transformation, but an appropriate value will look
more natural to human readers. */
} else {
/* First, try to arrange for longitude/latitude axis pairs to have the same
scale. Do we have a matching pair of celestial axes? */
k = AST__BAD;
if( skydiag0 != AST__BAD && skydiag1 != AST__BAD ) {
/* Is the current column the one which corresponds to the first celestial
axis, and does the other sky column correspond to a Mapping input? */
if( skycol0 == j && skycol1 < nin ) {
/* If so, scale this column so that its diagonal element is the negative
of the diagonal element of the other axis. This is on the assumption that
the scales on the two axes should be equal, and that longitude increases
east whilst latitude increases north, and that the CD matrix does not
introduce an axis permutation. */
if( skydiag0 != 0.0 ) k = -skydiag1/skydiag0;
/* Now see if the current column the one which corresponds to the second
celestial axis. Do the same as above. */
} else if( skycol1 == j && skycol0 < nin ) {
if( skydiag1 != 0.0 ) k = -skydiag0/skydiag1;
/* If neither of the above conditions was met, assume a diagonal element
value of 1.0 degrees for latitude axes, and -1.0 degrees for longitude
axes. */
}
}
/* If this failed, the next choice is to arrange for diagonally opposite
elements to be equal and opposite in value. Look for the element of the
column which has the largest diagonally opposite element, and choose a
scaling factor which makes this column element equal to the negative value
of its diagonally opposite element. Be careful to take axis permutations
into account when finding the value of the diagonal element. */
if( k == AST__BAD ) {
mxcv = 0.0;
ii = pperm[ j ];
for( i = 0; i < nout; i++ ) {
jj = wperm[ i ];
if( jj < nin ) {
cv = fullmat[ jj ][ ii ];
if( !EQUAL( colvec[ i ], 0.0 ) && fabs( cv ) > mxcv ) {
mxcv = fabs( cv );
k = -cv/colvec[ i ];
}
}
}
}
/* If still no scaling factor is available, use a scaling factor which
produces a diagonal element of 1.0 degree if the corresponding row is a
sky latitude axis, -1.0 degree of sky longitude axes, and 1.0 for other
axes. */
if( k == AST__BAD && colvec[ pperm[ j ] ] != 0.0 ) {
if( skycol[ j ] ) {
k = AST__DD2R/colvec[ pperm[ j ] ];
if( skycol[ j ] == 1 ) k = -k;
} else {
k = 1.0/colvec[ pperm[ j ] ];
}
}
/* If we still do not have a scaling, use 1.0 (no scaling). */
if( k == AST__BAD ) k = 1.0;
/* Now scale and store the column elements. */
for( i = 0; i < nout; i++ ) {
cdmat[ wperm[ i ]*nout+j ] = k*colvec[ i ];
}
/* Find the corresponding modified CRPIX value and store it. */
crp = 1.0 + ( crp - 1.0 )/k;
SetItem( &(store->crpix), 0, j, s, crp, status );
}
/* Free resources */
if( pfrm ) pfrm = astAnnul( pfrm );
}
/* Any "degenerate" axes added in the above process for which the
intermediate->world mapping is linear, and which depend only on one
pixel axis, can be adjusted so that the reference point is at grid
coord 1.0. */
for( i = 0; i < nout; i++ ) {
if( lin[ i ] ) {
/* Check only one pixel axis contributes to this intermediate world axis
and find which one it is. */
jax = -1;
for( j = 0; j < nout; j++ ) {
if( !EQUAL( fullmat[ j ][ i ], 0.0 ) ) {
if( jax == -1 ) {
jax = j;
} else {
jax = -1;
break;
}
}
}
/* We only adjust values for "degenerate" axes. */
if( jax >= nin ) {
/* Check that this pixel axis only contributes to the single world axis
currently being considered. */
for( ii = 0; ii < nout; ii++ ) {
if( ii != i ) {
if( !EQUAL( fullmat[ jax ][ ii ], 0.0 ) ) {
jax = -1;
break;
}
}
}
if( jax != -1 ) {
/* Get the original CRVAL, CRPIX and CD values. Check they are defined.*/
crv = GetItem( &(store->crval), wperm[ i ], 0, s, NULL,
method, class, status );
cd = cdmat[ wperm[ i ]*nout + jax ];
crp = GetItem( &(store->crpix), 0, jax, s, NULL, method, class, status );
if( crv != AST__BAD && crp != AST__BAD &&
cd != AST__BAD ) {
/* Modify the CRPIX to be 1.0 and modify the CRVAL value accordingly. */
SetItem( &(store->crpix), 0, jax, s, 1.0, status );
SetItem( &(store->crval), wperm[ i ], 0, s,
cd*( 1.0 - crp ) + crv, status );
}
}
}
}
}
/* Finally, if there are fewer input axes than output axes, put a value for
the WCSAXES keyword into the store. */
if( nin < nwcs ) SetItem( &(store->wcsaxes), 0, 0, s, nwcs, status );
/* Release resources. */
y = astFree( y );
}
/* Produce and store PC and CDELT values from the above CD matrix */
SplitMat( nout, cdmat, cdelt, status );
c = cdmat;
d = cdelt;
for( i = 0; i < nout; i++ ){
for( j = 0; j < nout; j++ ){
val = *(c++);
if( i == j ){
if( EQUAL( val, 1.0 ) ) val = AST__BAD;
} else {
if( EQUAL( val, 0.0 ) ) val = AST__BAD;
}
if( val != AST__BAD ) SetItem( &(store->pc), i, j, s, val, status );
}
SetItem( &(store->cdelt), i, 0, s, *(d++), status );
}
}
/* Annul pointsets. */
psetg = astAnnul( psetg );
psetw = astAnnul( psetw );
/* Free other resources*/
map = astAnnul( map );
if( fullmat ) for( j = 0; j < nout; j++ ) fullmat[ j ] = astFree( fullmat[ j ] );
if( partmat ) for( j = 0; j < nout; j++ ) partmat[ j ] = astFree( partmat[ j ] );
fullmat = astFree( fullmat );
partmat = astFree( partmat );
cdmat = astFree( cdmat );
cdelt = astFree( cdelt );
g = astFree( g );
g0 = astFree( g0 );
w0 = astFree( w0 );
tol = astFree( tol );
lin = astFree( lin );
skycol = astFree( skycol );
pperm = astFree( pperm );
/* If an error has occurred, return zero. */
if( !astOK ) ret = 0;
/* Return the answer. */
return ret;
}
static void MakeInvertable( double **fullmat, int n, double *dim, int *status ){
/*
* Name:
* MakeInvertable
* Purpose:
* Modify a supplied square CD matrix if possible to make it invertable.
* Type:
* Private function.
* Synopsis:
* void MakeInvertable( double **fullmat, int n, double *dim, int *status )
* Class Membership:
* FitsChan
* Description:
* A search is made for matrix inputs that have no effect on any
* matrix outputs. if any such matrix inputs are associated with
* degenerate pixel axes (i.e. pixel axes that span only a single
* pixel), then the matrix input should always have the value zero and
* so the corresponding diagonal element of the matrix can be set to
* 1.0 without changing and of the outputs.
* Parameters:
* fullmat
* A pointer to an array with "n" elements corresponding to the n
* inputs of the matrix, each element being a pointer to an array
* with "n" elements corresponding to the n outputs of the matrix.
* n
* The number of inputs and outputs for the square matrix.
* dim
* Pointer to an array of "n" input (i.e. pixel) axis dimensions.
* Individual elements will be AST__BAD if dimensions are not known.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
int i; /* Input index */
int j; /* Output index */
int unused; /* Does the current input have no effect on any output? */
/* Check inherited status */
if( !astOK ) return;
/* Look for any inputs that have no effect on any of the outputs. If such
an input is associated with a degenerate grid axis (i.e. a grid axis
with a dimension of 1.0), then the input value will always be zero and
so the corresponding diagonal element of the matrix can eb set to 1.0
without affecting the output value (which will always be zero since
zero times anything is zero). Loop over all inputs. */
for( i = 0; i < n; i++ ) {
/* Assume this input has no effect on any output. */
unused = 1;
/* Loop over all outputs. */
for( j = 0; j < n; j++ ) {
/* If the corresponding matrix term is non-zero, the the input will have
an effect on the output, so set the unused flag false and break out of
the output loop. */
if( fullmat[ i ][ j ] != 0.0 ) {
unused = 0;
break;
}
}
/* If the input is unused, and it is associated with a degenerate pixel
axis, we can set the corresponding diagonal element of the matrix to
1.0. */
if( unused && dim[ i ] == 1.0 ) fullmat[ i ][ i ] = 1.0;
}
}
#if defined(THREAD_SAFE)
static int ManageLock( AstObject *this_object, int mode, int extra,
AstObject **fail, int *status ) {
/*
* Name:
* ManageLock
* Purpose:
* Manage the thread lock on an Object.
* Type:
* Private function.
* Synopsis:
* #include "object.h"
* AstObject *ManageLock( AstObject *this, int mode, int extra,
* AstObject **fail, int *status )
* Class Membership:
* FitsChan member function (over-rides the astManageLock protected
* method inherited from the parent class).
* Description:
* This function manages the thread lock on the supplied Object. The
* lock can be locked, unlocked or checked by this function as
* deteremined by parameter "mode". See astLock for details of the way
* these locks are used.
* Parameters:
* this
* Pointer to the Object.
* mode
* An integer flag indicating what the function should do:
*
* AST__LOCK: Lock the Object for exclusive use by the calling
* thread. The "extra" value indicates what should be done if the
* Object is already locked (wait or report an error - see astLock).
*
* AST__UNLOCK: Unlock the Object for use by other threads.
*
* AST__CHECKLOCK: Check that the object is locked for use by the
* calling thread (report an error if not).
* extra
* Extra mode-specific information.
* fail
* If a non-zero function value is returned, a pointer to the
* Object that caused the failure is returned at "*fail". This may
* be "this" or it may be an Object contained within "this". Note,
* the Object's reference count is not incremented, and so the
* returned pointer should not be annulled. A NULL pointer is
* returned if this function returns a value of zero.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A local status value:
* 0 - Success
* 1 - Could not lock or unlock the object because it was already
* locked by another thread.
* 2 - Failed to lock a POSIX mutex
* 3 - Failed to unlock a POSIX mutex
* 4 - Bad "mode" value supplied.
* Notes:
* - This function attempts to execute even if an error has already
* occurred.
*/
/* Local Variables: */
AstFitsChan *this; /* Pointer to FitsChan structure */
int result; /* Returned status value */
/* Initialise */
result = 0;
/* Check the supplied pointer is not NUL. */
if( ! this_object ) return result;
/* Obtain a pointers to the FitsChan structure. */
this = (AstFitsChan *) this_object;
/* Invoke the ManageLock method inherited from the parent class. */
if( !result ) result = (*parent_managelock)( this_object, mode, extra,
fail, status );
/* Invoke the astManageLock method on any Objects contained within
the supplied Object. */
if( !result ) result = astManageLock( this->keyseq, mode, extra, fail );
if( !result ) result = astManageLock( this->keywords, mode, extra, fail );
return result;
}
#endif
static int Match( const char *test, const char *temp, int maxfld, int *fields,
int *nfld, const char *method, const char *class, int *status ){
/*
* Name:
* Match
* Purpose:
* Sees if a test keyword name matches a template.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int Match( const char *test, const char *temp, int maxfld, int *fields,
* int *nfld, const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* All characters in the template other than "%" (and the field width
* and type specifiers which follow a "%") must be matched by an
* identical character (ignoring case) in the test string. If a "%" occurs
* in the template, then the next character in the template should be a
* single digit specifying a field width. If it is zero, then the test
* string may contain zero or more matching characters. Otherwise,
* the test string must contain exactly the specified number of matching
* characters (i.e. 1 to 9). The field width digit may be omitted, in
* which case the test string must contain one or more matching
* characters. The next character in the template specifies the type of
* matching characters and must be one of "d", "c" or "f". Decimal digits
* are matched by "d", all upper (but not lower) case alphabetical
* characters are matched by "c", and all characters which are legal within
* a FITS keyword (i.e. upper case letters, digits, underscores and
* hyphens) are matched by "f".
* Parameters:
* test
* Pointer to a null terminated string holding the keyword name to
* be tested.
* temp
* Pointer to a null terminated string holding the template.
* maxfld
* The maximum number of integer field values which should be
* returned in "fields".
* fields
* A pointer to an array of at least "maxfld" integers. This is
* returned holding the values of any integer fields specified
* in the template. The values are extracted from the test string,
* and stored in the order they appear in the template string.
* nfld
* Pointer to a location at which is returned the total number of
* integer fields in the test string. This may be more than the
* number returned in "fields" if "maxfld" is smaller than "*nfld".
* 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:
* Zero is returned if the test string does not match the template
* string, and one is returned if it does.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
char type; /* Field type specifier */
const char *a; /* Pointer to next test character */
const char *b; /* Pointer to next template character */
int extend; /* Can the width of the first field be extended? */
int i; /* Field index */
int match; /* Does "test" match "temp"? */
int nfret; /* No. of fields returned */
int tmp; /* Field value */
/* Check global status. */
if( !astOK ) return 0;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(NULL);
/* On the first entry to this function, indicate that no integer fields
have yet been returned, and save a pointer to the start of the template
string. */
if( !match_nentry ) {
*nfld = 0;
match_template = temp;
}
/* Increment the number of entries into this function. */
match_nentry++;
/* Initialise pointers to the start of each string. */
a = test;
b = temp;
/* Initialise the returned flag to indicate that the two strings do not
match. */
match = 0;
/* Check that the initial part of the test string can match the first
field in the template. */
if( MatchFront( a, b, &type, &extend, &match_na, &match_nb, method, class, match_template, status ) ){
/* If it does, increment the pointers to skip over the characters
used up in the comparison. */
a += match_na;
b += match_nb;
/* If the ends of both strings have been reached, they match. */
if( *a == 0 && *b == 0 ){
match = 1;
/* Otherwise, if the end of the template has been reached but there are
still characters to be read from the test string, we could still have
a match if all the remaining test characters match an extandable field. */
} else if( *b == 0 && *a != 0 && extend ){
/* Loop until all the matching characters have been read from the end of
the test string. */
while( *a != 0 && MatchChar( *a, type, method, class, match_template, status ) ) a++;
/* If we reached the end of the test string, we have a match. */
if( *a == 0 ) match = 1;
/* Otherwise, we need to carry on checking the remaining fields. */
} else {
/* Call this function recursively to see if the remainder of the
strings match. */
if( Match( a, b, maxfld, fields, nfld, method, class, status ) ){
match = 1;
/* If the remainder of the strings do not match, we may be able to make
them match by using up some extra test characters on the first field.
This can only be done if the first field has an unspecified field width,
and if the next test character if of a type which matches the first
field in the template. */
} else if( extend ){
/* Loop until all the suitable characters have been read from the
test string. Break out of the loop early if we find a field width
which results in the whole string matching. */
while( MatchChar( *a, type, method, class, match_template, status ) ){
a++;
if( Match( a, b, maxfld, fields, nfld, method, class, status ) ){
match = 1;
break;
}
}
}
}
}
/* If the strings match and the leading field is an integer, decode
the field and store it in the supplied array (if there is room). */
if( match && type == 'd' && a > test ){
if( *nfld < maxfld ){
sprintf( match_fmt, "%%%dd", (int) ( a - test ) );
astSscanf( test, match_fmt, fields + *nfld );
}
(*nfld)++;
}
/* Decrement the number of entries into this function. */
match_nentry--;
/* If we are leaving this function for the last time, reverse the
order of the returned integer fields so that they are returned
in the same order that they occur in the template. */
if( !match_nentry ){
nfret = ( *nfld < maxfld ) ? (*nfld) : maxfld;
match_pa = fields;
match_pb = fields + nfret - 1;
for( i = 0; i < nfret/2; i++ ){
tmp = *match_pa;
*(match_pa++) = *match_pb;
*(match_pb--) = tmp;
}
}
/* Return the result. */
return match;
}
static int MatchChar( char test, char type, const char *method,
const char *class, const char *template, int *status ){
/*
* Name:
* MatchChar
* Purpose:
* See if a given character is of a specified type.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int MatchChar( char test, char type, const char *method,
* const char *class, const char *template, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function checks that the supplied test character belongs
* to the set of characters specified by the parameter "type".
* Parameters:
* test
* The character to test.
* type
* The character specifying the set of acceptable characters. This
* should be one of the field type characters accepted by function
* Match (e.g. "d", "c" or "f").
* 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.
* template
* Pointer to the start of the whole template string, for use in error
* messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Zero is returned if the test character does not belongs to the
* specified character set, and one is returned if it does.
* Notes:
* - An error is reported if the type specifier is not legal.
* - Zero is returned if an error has already occurred, or if ths
* function fails for any reason.
*/
/* Local Variables: */
int ret; /* Returned flag */
/* Check global status. */
ret = 0;
if( !astOK ) return ret;
/* Check for "d" specifiers (digits). */
if( type == 'd' ){
ret = isdigit( (int) test );
/* Check for "c" specifiers (upper case letters). */
} else if( type == 'c' ){
ret = isupper( (int) test );
/* Check for "s" specifiers (any legal FITS keyword character). */
} else if( type == 'f' ){
ret = isFits( (int) test );
/* Report an error for any other specifier. */
} else if( astOK ){
ret = 0;
astError( AST__BDFMT, "%s(%s): Illegal field type or width "
"specifier '%c' found in filter template '%s'.", status,
method, class, type, template );
}
/* Return the answer. */
return ret;
}
static int MatchFront( const char *test, const char *temp, char *type,
int *extend, int *ntest, int *ntemp,
const char *method, const char *class,
const char *template, int *status ){
/*
* Name:
* MatchFront
* Purpose:
* Sees if the start of a test string matches the start of a template.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int MatchFront( const char *test, const char *temp, char *type,
* int *extend, int *ntest, int *ntemp,
* const char *method, const char *class,
* const char *template )
* Class Membership:
* FitsChan member function.
* Description:
* This function looks for a match between the first field in the
* template string and the string at the start of the test string,
* using the syntax described in function Match.
* Parameters:
* test
* Pointer to a null terminated string holding the keyword name to
* be tested.
* temp
* Pointer to a null terminated string holding the template.
* type
* Pointer to a location at which to return a character specifying the
* sort of field that was matched. This will be one of the legal field
* types accepted by Match (e.g. "d", "c" or "f"), or null (zero) if
* the first field in the template string was a literal character (i.e.
* did not start with a "%").
* extend
* Pointer to a location at which to return a flag which will be non-zero
* if the further test characters could be matched by the first field in
* the template. This will be the case if the template field only
* specifies a minimum number of matching characters (i.e. if the field
* width can be extended). For instance, "%d" can be extended, but "%1d"
* cannot.
* ntest
* Pointer to a location at which to return the number of characters
* matched in the test string. This will be the minimum number allowed
* by the template field.
* ntemp
* Pointer to a location at which to return the number of characters
* read from the template string (i.e. the number of characters in the
* field specification).
* 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.
* template
* Pointer to the start of the whole template string, for use in error
* messages.
* Returned Value:
* Zero is returned if the test string starts with fewer than the
* minimum number of characters matching the template string, and one
* is returned if it does.
* Notes:
* - Zero is returned if an error has already occurred, or if this
* function fails for any reason.
*/
/* Local Variables: */
const char *a; /* Pointer to next test character */
const char *b; /* Pointer to next template character */
int i; /* Character index */
int match; /* Does "test" match "temp"? */
/* Check global status. */
if( !astOK ) return 0;
/* Initialise pointers to the start of each string. */
a = test;
b = temp;
/* Initialise the returned value to indicate that the strings match. */
match = 1;
/* If the current character in the template is not a % sign, it must
match the current character in the test string (except for case). */
if( *b != '%' ){
if( toupper( (int) *b ) != toupper( (int) *a ) ) {
match = 0;
/* If the characters match, return all the required information. */
} else {
*type = 0;
*extend = 0;
*ntest = 1;
*ntemp = 1;
}
/* If the current character of the template is a %, we need to match
a field. */
} else {
*ntemp = 3;
/* The next character in the template string determines the field width.
Get the lowest number of characters which must match in the test string,
and set a flag indicating if this lowest limit can be extended. */
b++;
if( *b == '0' ){
*ntest = 0;
*extend = 1;
} else if( *b == '1' ){
*ntest = 1;
*extend = 0;
} else if( *b == '2' ){
*ntest = 2;
*extend = 0;
} else if( *b == '3' ){
*ntest = 3;
*extend = 0;
} else if( *b == '4' ){
*ntest = 4;
*extend = 0;
} else if( *b == '5' ){
*ntest = 5;
*extend = 0;
} else if( *b == '6' ){
*ntest = 6;
*extend = 0;
} else if( *b == '7' ){
*ntest = 7;
*extend = 0;
} else if( *b == '8' ){
*ntest = 8;
*extend = 0;
} else if( *b == '9' ){
*ntest = 9;
*extend = 0;
/* If no field width was given, one or more test characters are matched.
Step back a character so that the current character will be re-used as
the type specifier. */
} else {
*ntest = 1;
*extend = 1;
b--;
(*ntemp)--;
}
/* The next template character gives the type of character which should
be matched. */
b++;
*type = *b;
/* Report an error if the template string ended within the field
specifier. */
if( !*b ){
match = 0;
astError( AST__BDFMT, "%s(%s): Incomplete field specifier found "
"at end of filter template '%s'.", status, method, class,
template );
/* Otherwise, check that the test string starts with the minimum allowed
number of characters matching the specified type. */
} else {
for( i = 0; i < *ntest; i++ ){
if( !MatchChar( *a, *type, method, class, template, status ) ){
match = 0;
break;
}
a++;
}
}
}
/* Return the answer. */
return match;
}
static void MarkCard( AstFitsChan *this, int *status ){
/*
* Name:
* MarkCard
* Purpose:
* Mark the current card as having been read into an AST object.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void MarkCard( AstFitsChan *this, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* The current card is marked as having been "provisionally used" in
* the construction of an AST object. If the Object is constructed
* succesfully, such cards are marked as having been definitely used,
* and they are then considered to have been removed from the FitsChan.
* Parameters:
* this
* Pointer to the FitsChan containing the list of cards.
* status
* Pointer to the inherited status variable.
* Notes:
* - The card remains the current card even though it is now marked
* as having been read.
*/
int flags;
/* Return if the global error status has been set, or the current card
is not defined. */
if( !astOK || !this->card ) return;
/* Set the PROVISIONALLY_USED flag in the current card, but only if the
PROTECTED flag is not set. */
flags = ( (FitsCard *) this->card )->flags;
if( !( flags & PROTECTED ) ) {
( (FitsCard *) this->card )->flags = flags | PROVISIONALLY_USED;
}
}
static int MoveCard( AstFitsChan *this, int move, const char *method,
const char *class, int *status ){
/*
* Name:
* MoveCard
* Purpose:
* Move the current card a given number of cards forward or backwards.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int MoveCard( AstFitsChan *this, int move, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* The current card is increment by the given number of cards, ignoring
* cards which have been read into an AST object if the ignore_used flag
* is set non-zero.
* Parameters:
* this
* Pointer to the FitsChan containing the list of cards.
* move
* The number of cards by which to move the current card. Positive
* values move towards the end-of-file. Negative values move
* towards the start of the file (i.e. the list head).
* method
* Pointer to string holding name of calling method.
* class
* Pointer to string holding object class.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The number of cards actually moved. This may not always be equal to
* the requested number (for instance, if the end or start of the
* FitsChan is encountered first).
* Notes:
* - If the end-of-file is reached before the required number of
* cards have been skipped, the current card is set NULL, to indicate
* an end-of-file condition.
* - If the start of the file is reached before the required number of
* cards have been skipped, the current card is left pointing to the
* first usable card.
* - This function attempts to execute even if an error has occurred.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
FitsCard *card; /* The current card */
FitsCard *card0; /* The previous non-deleted card */
int moved; /* The number of cards moved by so far */
/* Return if the supplied object is NULL or the FitsChan is
empty, or zero movement is requested. */
if( !this || !this->head || !move ) return 0;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this);
/* Get a pointer to the current card. */
card = (FitsCard *) this->card;
/* Initialise the number of cards moved so far. */
moved = 0;
/* First deal with positive movements (towards the end-of-file). */
if( move > 0 ){
/* Loop round moving on to the next card until the correct number of
moves have been made, or the end-of-file is reached. */
while( moved < move && card ){
/* Get a pointer to the next card in the list, reporting an error if the
links are inconsistent. */
card = GetLink( card, NEXT, method, class, status );
/* If we have moved past the last card and are now pointing back at the
list head, then indicate that we are at end-of-file by setting the
card pointer NULL. */
if( (void *) card == this->head ){
card = NULL;
/* Otherwise, increment the number of cards moved. We ignore cards which
have been read into an AST object if the external "ignore_used" flag is
set. */
} else if( card ){
if( !CARDUSED(card) ) moved++;
}
}
/* Now deal with negative movements (towards the list head), so long as
we are not currently at the list head. */
} else if( (void *) card != this->head ){
/* If we are currently at end-of-file, replace the NULL pointer for the
current card with a pointer to the list head. The first step backwards
will make the last card the current card. */
if( !card ) card = (FitsCard *) this->head;
/* Loop round until the correct number of cards have been moved. */
while( moved < -move && card ){
/* If cards which have been read into an AST object are to be included in the
count of moved cards, get a pointer to the previous card in the list,
reporting an error if the links are inconsistent. */
if( !ignore_used ){
card = GetLink( card, PREVIOUS, method, class, status );
/* If cards which have been read into an AST object are to be ignored... */
} else {
/* We need to find the previous card which has not been read into an AST
object. We do not search beyond the start of the list. */
card0 = GetLink( card, PREVIOUS, method, class, status );
while( card0 && CARDUSED(card0) && (void *) card0 != this->head ){
card0 = GetLink( card0, PREVIOUS, method, class, status );
}
/* If no such card was found we leave the card where it is. */
if( card0 && ( card0->flags & USED ) ) {
break;
/* Otherwise, move back to card found above. */
} else {
card = card0;
}
}
/* Increment the number of cards moved. */
moved++;
/* If the current card is the list head, break out of the loop. */
if( (void *) card == this->head ) break;
}
}
/* Store the new current card. */
this->card = (void *) card;
/* Return the answer. */
return moved;
}
static double NearestPix( AstMapping *map, double val, int axis, int *status ){
/*
* Name:
* NearestPix
* Purpose:
* Find an axis value which corresponds to an integer pixel value.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* double NearestPix( AstMapping *map, double val, int axis, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* The supplied axis value is transformed using the inverse of the
* supplied Mapping (other axes are given the value AST__BAD). The
* resulting axis values are rounded to the nearest whole number, and
* then transformed back using the supplied Mapping in the forward
* direction. If the nominated axis value is good, it is returned as
* the function value, otherwise the supplied value is returned unchanged.
* Parameters:
* map
* A Mapping (usually the input coordinates will correspond to
* pixel coordinates).
* val
* A value for one of the outputs of the "map" Mapping.
* axis
* The index of the Mapping output to which "val" refers.
* status
* Pointer to the inherited status variable.
* Retuned Value:
* The modified output axis value.
*/
/* Local Variables: */
AstMapping *tmap; /* Mapping to be used */
AstPointSet *pset1; /* Pixel coords PointSet */
AstPointSet *pset2; /* WCS coords PointSet */
double **ptr1; /* Pointer to data in pset1 */
double **ptr2; /* Pointer to data in pset2 */
double result; /* Returned value */
int *ins; /* Array holding input axis indicies */
int i; /* Loop count */
int nin; /* Number of Mapping inputs */
int nout; /* Number of Mapping outputs */
/* Initialise. */
result = val;
/* Check inherited status, and that the supplied value is good. */
if( !astOK || result == AST__BAD ) return result;
/* If the supplied Mapping has no inverse, trying splitting off the
transformation for the required axis, which may have an inverse.
If succesful, use the 1-in,1-out Mapping returned by astMapSPlit
instead of the supplied Mapping, and adjust the axis index accordingly. */
if( !astGetTranInverse( map ) ) {
astInvert( map );
ins = astMapSplit( map, 1, &axis, &tmap );
if( tmap ) {
astInvert( tmap );
axis = 0;
} else {
tmap = astClone( map );
}
ins = astFree( ins );
astInvert( map );
} else {
tmap = astClone( map );
}
/* If the Mapping still has no inverse, return the supplied value
unchanged. */
if( astGetTranInverse( tmap ) ) {
/* Get the number of input and output coordinates. */
nin = astGetNin( tmap );
nout = astGetNout( tmap );
/* Create PointSets to hold a single input position and the corresponding
output position. */
pset1 = astPointSet( 1, nin, "", status );
ptr1 = astGetPoints( pset1 );
pset2 = astPointSet( 1, nout, "", status );
ptr2 = astGetPoints( pset2 );
if( astOK ) {
/* Assign AST__BAD values to all output axes, except for the specified
axis, which is given the supplied axis value. */
for( i = 0; i < nout; i++ ) ptr2[ i ][ 0 ] = AST__BAD;
ptr2[ axis ][ 0 ] = val;
/* Transform this output position into an input position. */
(void) astTransform( tmap, pset2, 0, pset1 );
/* Round all good axis values in the resulting input position to the nearest
integer. */
for( i = 0; i < nin; i++ ) {
if( ptr1[ i ][ 0 ] != AST__BAD ) {
ptr1[ i ][ 0 ] = (int) ( ptr1[ i ][ 0 ] + 0.5 );
}
}
/* Transform this input position back into output coords. */
(void) astTransform( tmap, pset1, 1, pset2 );
/* If the resulting axis value is good, return it. */
if( ptr2[ axis ] [ 0 ] != AST__BAD ) result = ptr2[ axis ] [ 0 ];
}
/* Free resources. */
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
}
tmap = astAnnul( tmap );
/* Return the result. */
return result;
}
static void NewCard( AstFitsChan *this, const char *name, int type,
const void *data, const char *comment, int flags,
int *status ){
/*
* Name:
* NewCard
* Purpose:
* Insert a new card in front of the current card.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void NewCard( AstFitsChan *this, const char *name, int type,
* const void *data, const char *comment, int flags,
* int *status )
* Class Membership:
* FitsChan member function.
* Description:
* The supplied keyword name, data type and value, and comment are
* stored in a new FitsCard structure, and this structure is
* inserted into the circular linked list stored in the supplied
* FitsChan. It is inserted in front of the current card.
* Parameters:
* this
* Pointer to the FitsChan containing the list of cards.
* name
* Pointer to a string holding the keyword name of the new card.
* type
* An integer value representing the data type of the keyword.
* data
* Pointer to the data associated with the keyword.
* comment
* Pointer to a null-terminated string holding a comment.
* flags
* The flags to assign to the card.
* status
* Pointer to the inherited status variable.
* Notes:
* - The new card is inserted into the list in front of the current card,
* so that the "next" link from the new card points to the current card.
* If the FitsChan is currently at end-of-file (indicated by a NULL
* pointer being stored for the current card), then the card is appended
* to the end of the list. The pointer to the current card is left
* unchanged.
* - Keyword names are converted to upper case before being stored.
* - Any trailing white space in a string value is saved as supplied.
* - Logical values are converted to zero or one before being stored.
* - The "comment" and/or "data" pointers may be supplied as NULL.
*/
/* Local Variables: */
FitsCard *new; /* Pointer to the new card */
FitsCard *prev; /* Pointer to the previous card in the list */
char *b; /* Pointer to next stored character */
const char *a; /* Pointer to next supplied character */
int lval; /* Logical data value restricted to 0 or 1 */
int nc; /* No. of characters to store */
/* Check the global status. */
if( !astOK ) return;
/* Get memory to hold the new FitsCard structure. */
new = (FitsCard *) astMalloc( sizeof( FitsCard ) );
/* Check the pointer can be used. */
if( astOK ){
/* Copy the keyword name, converting to upper case. */
a = name;
b = new->name;
while( *a ) *(b++) = (char) toupper( (int) *(a++) );
*b = 0;
/* Ensure that a KeyMap exists to hold the keywords currently in the
FitsChan. */
if( !this->keywords ) this->keywords = astKeyMap( " ", status );
/* Add the keyword name to the KeyMap. The value associated with the
KeyMap entry is not used and is set arbitrarily to zero. */
astMapPut0I( this->keywords, new->name, 0, NULL );
/* Copy the data type. */
new->type = type;
/* Copy any data (ignore any data supplied for an UNDEF value). */
if( data && type != AST__UNDEF ){
/* Logical values are converted to zero or one before being stored. */
if( type == AST__LOGICAL ){
lval = *( (int *) data ) ? 1 : 0;
new->size = sizeof( int );
new->data = astStore( NULL, (void *) &lval, sizeof( int ) );
/* String values... */
} else if( type == AST__STRING || type == AST__CONTINUE ){
/* Find the number of characters excluding the trailing null character. */
nc = strlen( data );
/* Store the string, reserving room for a terminating null. */
new->size = (size_t)( nc + 1 );
new->data = astStore( NULL, (void *) data, (size_t)( nc + 1 ) );
/* Terminate it. */
( (char *) new->data)[ nc ] = 0;
/* Other types are stored as supplied. */
} else if( type == AST__INT ){
new->size = sizeof( int );
new->data = astStore( NULL, (void *) data, sizeof( int ) );
} else if( type == AST__FLOAT ){
new->size = sizeof( double );
new->data = astStore( NULL, (void *) data, sizeof( double ) );
} else if( type == AST__COMPLEXF ){
if( *( (double *) data ) != AST__BAD ) {
new->size = 2*sizeof( double );
new->data = astStore( NULL, (void *) data, 2*sizeof( double ) );
} else {
nc = strlen( BAD_STRING );
new->size = (size_t)( nc + 1 );
new->data = astStore( NULL, BAD_STRING, (size_t)( nc + 1 ) );
( (char *) new->data)[ nc ] = 0;
}
} else if( type == AST__COMPLEXI ){
new->size = 2*sizeof( int );
new->data = astStore( NULL, (void *) data, 2*sizeof( int ) );
} else {
new->size = 0;
new->data = NULL;
}
} else {
new->size = 0;
new->data = NULL;
}
/* Find the first non-blank character in the comment, and find the used
length of the remaining string. We retain leading and trailing white
space if the card is a COMMENT card. */
if( comment ){
a = comment;
if( type != AST__COMMENT ) {
while( isspace( *a ) ) a++;
nc = ChrLen( a, status );
} else {
nc = strlen( a );
}
} else {
nc = 0;
}
/* Copy any comment, excluding leading and trailing white space unless
this is a COMMENT card */
if( nc > 0 ){
new->comment = astStore( NULL, (void *) a, (size_t)( nc + 1 ) );
( (char *) new->comment)[ nc ] = 0;
} else {
new->comment = NULL;
}
/* Set the supplied flag values. */
new->flags = flags;
/* Insert the copied card into the list, in front of the current card. If
the current card is the list head, make the new card the list head. */
if( this->card ){
prev = ( ( FitsCard *) this->card )->prev;
( ( FitsCard *) this->card )->prev = new;
new->prev = prev;
prev->next = new;
new->next = (FitsCard *) this->card;
if( this->card == this->head ) this->head = (void *) new;
/* If the FitsChan is at end-of-file, append the new card to the end of
the list (i.e. insert it just before the list head). */
} else {
if( this->head ){
prev = ( (FitsCard *) this->head )->prev;
( (FitsCard *) this->head )->prev = new;
new->prev = prev;
prev->next = new;
new->next = (FitsCard *) this->head;
/* If there are no cards in the list, start a new list. */
} else {
new->prev = new;
new->next = new;
this->head = (void *) new;
this->card = NULL;
}
}
}
/* Return. */
return;
}
static AstMapping *NonLinSpecWcs( AstFitsChan *this, char *algcode,
FitsStore *store, int i, char s,
AstSpecFrame *specfrm, const char *method,
const char *class, int *status ) {
/*
* Name:
* NonLinSpecWcs
* Purpose:
* Create a Mapping describing a FITS-WCS non-linear spectral algorithm
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstMapping *NonLinSpecWcs( AstFitsChan *this, char *algcode,
* FitsStore *store, int i, char s,
* AstSpecFrame *specfrm, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function uses the contents of the supplied FitsStore to create
* a Mapping which goes from Intermediate World Coordinate (known as "w"
* in the context of FITS-WCS paper III) to the spectral system
* described by the supplied SpecFrame.
*
* The returned Mapping implements the non-linear "X2P" algorithms
* described in FITS-WCS paper III. The axis is linearly sampled in
* system "X" but expressed in some other system (specified by the
* supplied SpecFrame).
* Parameters:
* this
* Pointer to the FitsChan.
* algcode
* Pointer to a string holding the non-linear "-X2P" code for the
* required algorithm. This includes aleading "-" character.
* store
* Pointer to the FitsStore structure holding the values to use for
* the WCS keywords.
* i
* The zero-based index of the spectral axis within the FITS header
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* specfrm
* Pointer to the SpecFrame. This specified the "S" system - the
* system in which the CRVAL kewyords (etc) are specified.
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to a Mapping, or NULL if an error occurs.
*/
/* Local Variables: */
AstFrameSet *fs;
AstMapping *map1;
AstMapping *ret;
AstSpecFrame *xfrm;
AstMapping *map2;
char buf[ 100 ];
char pc;
double crv;
double ds;
double in_a;
double in_b;
double out_a;
double out_b;
int ok;
int s_sys;
/* Check the global status. */
ret = NULL;
if( !astOK ) return ret;
/* Identify the spectral "X" system within the "X2P" algorithm code, and
create a SpecFrame describing the X system ("X" is the system in
which the axis is linearly sampled). This is done by copying the
supplied SpecFrame and then setting its System attribute. Copying
the supplied SpecFrame ensures that all the other attributes (RestFreq,
etc.) are set correctly. */
ok = 1;
xfrm = astCopy( specfrm );
if( algcode[ 1 ] == 'F' ) {
astSetSystem( xfrm, AST__FREQ );
astSetUnit( xfrm, 0, "Hz" );
} else if( algcode[ 1 ] == 'W' ) {
astSetSystem( xfrm, AST__WAVELEN );
astSetUnit( xfrm, 0, "m" );
} else if( algcode[ 1 ] == 'V' ) {
astSetSystem( xfrm, AST__VREL );
astSetUnit( xfrm, 0, "m/s" );
} else if( algcode[ 1 ] == 'A' ) {
astSetSystem( xfrm, AST__AIRWAVE );
astSetUnit( xfrm, 0, "m" );
} else {
ok = 0;
}
/* If the X system was identified, find a Mapping from the "S" (specfrm)
system to the X system. */
map1 = NULL;
if( ok ) {
ok = 0;
fs = astConvert( specfrm, xfrm, "" );
if( fs ) {
map1 = astGetMapping( fs, AST__BASE, AST__CURRENT );
fs = astAnnul( fs );
ok = 1;
}
/* Issue a warning if the "P" system is not the correct one for the given
"S" system. We can however continue, sine AST interprets illegal "P"
systems correctly. */
pc = 0;
s_sys = astGetSystem( specfrm );
if( s_sys == AST__FREQ || s_sys == AST__ENERGY ||
s_sys == AST__WAVENUM || s_sys == AST__VRADIO ) {
pc = 'F';
} else if( s_sys == AST__WAVELEN || s_sys == AST__VOPTICAL ||
s_sys == AST__REDSHIFT ){
pc = 'W';
} else if( s_sys == AST__AIRWAVE ) {
pc = 'A';
} else if( s_sys == AST__BETA || s_sys == AST__VREL ) {
pc = 'V';
} else if( astOK ) {
pc = algcode[ 3 ];
astError( AST__INTER, "%s: Function NonLinSpecWcs does not yet "
"support spectral axes of type %s (internal AST "
"programming error).", status, method, astGetC( specfrm, "System" ) );
}
if( algcode[ 3 ] != pc ) {
sprintf( buf, "The spectral CTYPE value %s%s is not legal - "
"using %s%.3s%c instead.", astGetC( specfrm, "System" ),
algcode, astGetC( specfrm, "System" ), algcode, pc );
Warn( this, "badctype", buf, method, class, status );
}
}
/* If succesfull, use this Mapping to find the reference value (CRVAL)
in the "X" system. */
if( ok ) {
/* Get the CRVAL value for the spectral axis (this will be in the S system). */
crv = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
if( crv == AST__BAD ) crv = 0.0;
/* Convert it to the X system. */
astTran1( map1, 1, &crv, 1, &crv );
/* Invert this Mapping so that it forward transformation goes from X to S. */
astInvert( map1 );
/* Find the rate of change of S with respect to X (dS/dX) at the reference
point (x = crv). */
ds = astRate( map1, &crv, 0, 0 );
if( ds != AST__BAD && ds != 0.0 ) {
/* FITS-WCS paper III says that dS/dw must be 1.0 at the reference point.
Therefore dX/dw = dX/dS at the reference point. Also, since the spectral
axis is linear in X, dX/dw must be constant. Therefore the Mapping from
IWC to X is a WinMap which scales the IWC axis ("w") by dX/dw and adds
on the X value at the reference point. */
if( crv != 0.0 ) {
in_a = 0.0;
out_a = crv;
in_b = crv*ds;
out_b = 2.0*crv;
map2 = (AstMapping *) astWinMap( 1, &in_a, &in_b, &out_a, &out_b, "", status );
} else {
map2 = (AstMapping *) astZoomMap( 1, 1.0/ds, "", status );
}
/* The Mapping to be returned is the concatenation of the above Mapping
(from w to X) with the Mapping from X to S. */
ret = (AstMapping *) astCmpMap( map2, map1, 1, "", status );
map1 = astAnnul( map1 );
map2 = astAnnul( map2 );
}
}
xfrm = astAnnul( xfrm );
/* Return the result */
return ret;
}
static double *OrthVector( int n, int m, double **in, int *status ){
/*
* Name:
* OrthVector
* Purpose:
* Find a unit vector which is orthogonal to a set of supplied vectors.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* double *OrthVector( int n, int m, double **in, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* A set of M vectors is supplied, each vector being N-dimensional.
* It is assumed that M < N and that the supplied vectors span M
* axes within the N dimensional space. An N-dimensional unit vector is
* returned which is orthogonal to all the supplied vectors.
*
* The required vector is orthogonal to all the supplied vectors.
* Therefore the dot product of the required vector with each of the
* supplied vectors must be zero. This gives us M equations of the
* form:
*
* a1*r1 + a2*r2 + a3*r3 + .... + aN*rN = 0.0
* b1*r1 + b2*r2 + b3*r3 + .... + bN*rN = 0.0
* ...
*
* where (a1,a2,..,aN), (b1,b2,..,bN), ... are the supplied vectors
* and (r1,r2,...,rN) is the required vector. Since M is less
* than N the system of linear simultaneous equations is under
* specified and we need to assign arbitrary values to some of the
* components of the required vector in order to allow the equations
* to be solved. We arbitrarily assume that 1 element of the required
* vector has value 1.0 and (N-M-1) have value zero. The selection of
* *which* elements to set constant is based on the magnitudes of the
* columns of coefficients (a1,b1...), (a2,b2,...), etc. The M components
* of the required vector which are *not* set constant are the ones which
* have coefficient columns with the *largest* magnitude. This choice is
* made in order to minimise the risk of the remaining matrix of
* coefficients being singular (for instance, if a component of the
* required vector has a coefficient of zero in every supplied vector
* then the column magnitude will be zero and that component will be
* set to 1.0). After choosing the M largest columns, the largest
* remaining column is assigned a value of 1.0 in the required vector,
* and all other columns are assigned the value zero in the required
* vector. This means that the above equations becomes:
*
* a1*r1 + a2*r2 + a3*r3 + .... + aM*rM = -aM+1
* b1*r1 + b2*r2 + b3*r3 + .... + bM*rM = -bM+1
* ...
*
* Where the indices are now not direct indices into the supplied and
* returned vectors, but indices into an array of indices which have
* been sorted into column magnitude order. This is now a set of MxM
* simultaneous linear equations which we can solve using palDmat:
*
* MAT.R = V
*
* where MAT is the the matrix of columns (coefficients) on the left
* hand side of the above set of simultaneous equations, R is the
* required vector (just the components which have *not* been set
* constant), and V is a constant vector equal to the column of values
* on the right hand side in the above set of simultaneous equations.
* The palDmat function solves this equation to obtain R.
* Parameters:
* n
* The number of dimensions
* m
* The number of supplied vectors.
* in
* A pointer to an array with "m" elements, each element being a
* pointer to an array with "n" elements. Each of these "n" element
* array holds one of the supplied vectors.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The pointer to some newly allocated memory holding the returned N
* dimensional unit vector. The memory should be freed using astFree when
* no longer needed.
* Notes:
* - NULL is returned if an error occurs.
* - NULL is returned (without error) if the required vector cannot
* be found (.e.g becuase the supplied M vectors span less than M axes).
*/
/* Local Variables: */
double *colmag;
double *d;
double *e;
double *mat;
double *mel;
double *ret;
double *rhs;
double det;
double sl;
int *colperm;
int *iw;
int done;
int i;
int ih;
int ii;
int il;
int j;
int sing;
/* Initialise */
ret = NULL;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Return if any of the M supplied vectors are NULL. */
for( i = 0; i < m; i++ ) {
if( !in[ i ] ) return ret;
}
/* Allocate rquired memory. */
ret = astMalloc( sizeof( double )*(size_t) n );
rhs = astMalloc( sizeof( double )*(size_t) m );
mat = astMalloc( sizeof( double )*(size_t) m*m );
iw = astMalloc( sizeof( int )*(size_t) m );
colmag = astMalloc( sizeof( double )*(size_t) n );
colperm = astMalloc( sizeof( int )*(size_t) n );
/* Check memory can be used safely. */
if( astOK ) {
/* Find the magnitude of each column of coefficients in the full set of
simultaneous linear equations (before setting any components of the
required vector constant). Also initialise the column permutation array
to indicate that the columns are in their original order. The outer
loop loops through the columns and the inner loop loops through rows
(i.e. equations). */
for( i = 0; i < n; i++ ) {
colperm[ i ] = i;
colmag[ i ] = 0.0;
for( j = 0; j < m; j++ ) {
colmag[ i ] += in[ j ][ i ]*in[ j ][ i ];
}
}
/* Now re-arrange the column indices within the permutation array so that
they are in order of decreasing ciolumn magnitude (i.e. colperm[0] will
be left holding the index of the column with the largest magnitude). A
simple bubble sort is used. */
ii = 1;
done = 0;
while( !done ) {
done = 1;
for( i = ii; i < n; i++ ) {
ih = colperm[ i ];
il = colperm[ i - 1 ];
if( colmag[ ih ] > colmag[ il ] ) {
colperm[ i ] = il;
colperm[ i - 1 ] = ih;
done = 0;
}
}
ii++;
}
/* The first M elements in "colperm" now hold the indices of the
columns which are to be used within the MAT matrix, the next element
of "colperm" hold the index of the column which is to be included in the
V vector (other elements hold the indices of the columns which are
being ignored because they will be mutiplied by a value of zero - the
assumed value of the corresponding components of the returned vector). We
now copy the these values into arrays which can be passed to palDmat.
First, initialise a pointer used to step through the mat array. */
mel = mat;
/* Loop through all the supplied vectors. Get a pointer to the first
element of the vector. */
for( i = 0; i < m; i++ ) {
d = in[ i ];
/* Copy the required M elements of this supplied vector into the work array
which will be passed to palDmat. */
for( j = 0; j < m; j++ ) *(mel++) = d[ colperm[ j ] ];
/* Put the next right-hand side value into the "rhs" array. */
rhs[ i ] = -d[ colperm[ m ] ];
}
/* Use palDmat to find the first M elements of the returned array. These
are stored in "rhs", over-writing the original right-hand side values. */
palDmat( m, mat, rhs, &det, &sing, iw );
/* If the supplied vectors span fewer than M axes, the above call will fail.
In this case, annul the returned vector. */
if( sing != 0 ) {
ret = astFree( ret );
/* If succesful, copy the M elements of the solution vector into the
required M elements of the returned vector. Also find the squared length
of the vector. */
} else {
sl = 0.0;
e = rhs;
for( j = 0; j < m; j++ ) {
sl += (*e)*(*e);
ret[ colperm[ j ] ] = *(e++);
}
/* Put 1.0 into the next element of the returned vector. */
sl += 1.0;
ret[ colperm[ m ] ] = 1.0;
/* Fill up the rest of the returned vector with zeros. */
for( j = m + 1; j < n; j++ ) ret[ colperm[ j ] ] = 0.0;
/* Normalise the returned vector so that it is a unit vector.Also ensure
that any zeros are "+0.0" insteasd of "-0.0". */
e = ret;
sl = sqrt( sl );
for( j = 0; j < n; e++,j++ ) {
*e /= sl;
if( *e == 0.0 ) *e = 0.0;
}
}
}
/* Free workspace. */
rhs = astFree( rhs );
mat = astFree( mat );
iw = astFree( iw );
colmag = astFree( colmag );
colperm = astFree( colperm );
/* Free the returned vector if an error has occurred. */
if( !astOK ) ret = astFree( ret );
/* Return the answer. */
return ret;
}
static double **OrthVectorSet( int n, int m, double **in, int *status ){
/*
* Name:
* OrthVectorSet
* Purpose:
* Find a set of mutually orthogonal vectors.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* double **OrthVectorSet( int n, int m, double **in, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* A set of M vectors is supplied, each vector being N-dimensional.
* It is assumed that the supplied vectors span M axes within the
* N dimensional space. A pointer to a set of N vectors is returned.
* The first M returned vectors are copies of the M supplied vectors.
* The remaining returned vectors are unit vectors chosen to be
* orthogonal to all other vectors in the returned set.
* Parameters:
* n
* The number of dimensions
* m
* The number of supplied vectors.
* in
* A pointer to an array with "m" elements, each element being a
* pointer to an array with "n" elements. Each of these "n" element
* array holds one of the supplied vectors.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The pointer to some newly allocated memory holding the returned N
* vectors. The pointer locates an array of N elements, each of which
* is a pointer to an array holding the N elements of a single vector.
* The memory (including the inner pointers) should be freed using
* astFree when no longer needed.
* Notes:
* - NULL is returned if an error occurs.
* - NULL is returned (without error) if the required vectors cannot
* be found (e.g. becuase the supplied M vectors span less than M axes).
*/
/* Local Variables: */
double **ret;
int i;
int bad;
/* Initialise */
ret = NULL;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Allocate required memory. */
ret = astMalloc( sizeof( double * )*(size_t) n );
/* Check memory can be used safely. */
bad = 0;
if( astOK ) {
/* Copy the supplied vectors into the returned array. */
for( i = 0; i < m; i++ ) {
ret[ i ] = astStore( NULL, in[ i ], sizeof( double )*n );
}
/* For the remaining vectors, find a vector which is orthogonal to all
the vectors currently in the returned set. */
for( ; i < n; i++ ) {
ret[ i ] = OrthVector( n, i, ret, status );
if( !ret[ i ] ) bad = 1;
}
}
/* Free the returned vectors if an error has occurred. */
if( bad || !astOK ) {
for( i = 0; ret && i < n; i++ ) ret[ i ] = astFree( ret[ i ] );
ret = astFree( ret );
}
/* Return the answer. */
return ret;
}
static AstMapping *OtherAxes( AstFitsChan *this, AstFrameSet *fs, double *dim,
int *wperm, char s, FitsStore *store,
double *crvals, int *axis_done,
const char *method, const char *class,
int *status ){
/*
* Name:
* OtherAxes
* Purpose:
* Add values to a FitsStore describing unknown axes in a Frame.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstMapping *OtherAxes( AstFitsChan *this, AstFrameSet *fs, double *dim,
* int *wperm, char s, FitsStore *store,
* double *crvals, int *axis_done,
* const char *method, const char *class,
* int *status )
* Class Membership:
* FitsChan member function.
* Description:
* FITS WCS keyword values are added to the supplied FitsStore which
* describe any as yet undescribed axes in the supplied FrameSet. These
* axes are assumed to be linear and to follow the conventions
* of FITS-WCS paper I (if in fact they are not linear, then the
* grid->iwc mapping determined by MakeIntWorld will not be linear and
* so the axes will be rejected).
*
* Note, this function does not store values for keywords which define
* the transformation from pixel coords to Intermediate World Coords
* (CRPIX, PC and CDELT), but a Mapping is returned which embodies these
* values. This Mapping is from the current Frame in the FrameSet (WCS
* coords) to a Frame representing IWC. The IWC Frame has the same number
* of axes as the WCS Frame which may be greater than the number of base
* Frame (i.e. pixel) axes.
* Parameters:
* this
* Pointer to the FitsChan.
* fs
* Pointer to the FrameSet. The base Frame should represent FITS pixel
* coordinates, and the current Frame should represent FITS WCS
* coordinates. The number of base Frame axes should not exceed the
* number of current Frame axes.
* dim
* An array holding the image dimensions in pixels. AST__BAD can be
* supplied for any unknwon dimensions.
* wperm
* Pointer to an array of integers with one element for each axis of
* the current Frame. Each element holds the zero-based
* index of the FITS-WCS axis (i.e. the value of "i" in the keyword
* names "CTYPEi", "CRVALi", etc) which describes the Frame axis.
* s
* The co-ordinate version character. A space means the primary
* axis descriptions. Otherwise the supplied character should be
* an upper case alphabetical character ('A' to 'Z').
* store
* The FitsStore in which to store the FITS WCS keyword values.
* crvals
* Pointer to an array holding the default CRVAL value for each
* axis in the WCS Frame.
* axis_done
* An array of flags, one for each Frame axis, which indicate if a
* description of the corresponding axis has yet been stored in the
* FitsStore.
* 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:
* If any axis descriptions were added to the FitsStore, a Mapping from
* the current Frame of the supplied FrameSet, to the IWC Frame is returned.
* Otherwise, a UnitMap is returned. Note, the Mapping only defines the IWC
* transformation for the described axes. Any other (previously
* described) axes are passed unchanged by the returned Mapping.
*/
/* Local Variables: */
AstFitsTable *table; /* Pointer to structure holding -TAB table info */
AstFrame *wcsfrm; /* WCS Frame within FrameSet */
AstMapping *axmap; /* Mapping from WCS to IWC */
AstMapping *map; /* FITS pixel->WCS Mapping */
AstMapping *ret; /* Returned Mapping */
AstMapping *tmap0; /* Pointer to a temporary Mapping */
AstMapping *tmap1; /* Pointer to a temporary Mapping */
AstPermMap *pm; /* PermMap pointer */
AstPointSet *pset1; /* PointSet holding central pixel position */
AstPointSet *pset2; /* PointSet holding reference WCS position */
char buf[80]; /* Text buffer */
const char *lab; /* Pointer to axis Label */
const char *sym; /* Pointer to axis Symbol */
double **ptr1; /* Pointer to data for pset1 */
double **ptr2; /* Pointer to data for pset2 */
double *lbnd_p; /* Pointer to array of lower pixel bounds */
double *ubnd_p; /* Pointer to array of upper pixel bounds */
double crval; /* The value for the FITS CRVAL keyword */
int *inperm; /* Pointer to permutation array for input axes */
int *outperm; /* Pointer to permutation array for output axes */
int extver; /* Table version number for -TAB headers */
int fits_i; /* FITS WCS axis index */
int i; /* Loop count */
int iax; /* WCS Frame axis index */
int icolindex; /* Index of table column holding index vector */
int icolmain; /* Index of table column holding main coord array */
int interp; /* Interpolation method for look-up tables */
int log_axis; /* Is the axis logarithmically spaced? */
int nother; /* Number of axes still to be described */
int npix; /* Number of pixel axes */
int nwcs; /* Number of WCS axes */
int tab_axis; /* Can the axis be described by the -TAB algorithm? */
/* Initialise */
ret = NULL;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Get the number of WCS axes. */
nwcs = astGetNaxes( fs );
/* Count the number of WCS axes which have not yet been described. */
nother = 0;
for( iax = 0; iax < nwcs; iax++ ) {
if( ! axis_done[ iax ] ) nother++;
}
/* Only proceed if there are some axes to described. */
if( nother ) {
if( s == 'D' ) astShow( fs );
/* Get a pointer to the WCS Frame. */
wcsfrm = astGetFrame( fs, AST__CURRENT );
/* Get a pointer to the pixel->wcs Mapping. */
map = astGetMapping( fs, AST__BASE, AST__CURRENT );
/* Store the number of pixel and WCS axes. */
npix = astGetNin( fs );
nwcs = astGetNout( fs );
/* Store the upper and lower pixel bounds. */
lbnd_p = astMalloc( sizeof( double )*(size_t) npix );
ubnd_p = astMalloc( sizeof( double )*(size_t) npix );
if( astOK ) {
for( iax = 0; iax < npix; iax++ ) {
lbnd_p[ iax ] = 1.0;
ubnd_p[ iax ] = ( dim[ iax ] != AST__BAD ) ? dim[ iax ] : 500;
}
}
/* Transform the central pixel coords into WCS coords */
pset1 = astPointSet( 1, npix, "", status );
ptr1 = astGetPoints( pset1 );
pset2 = astPointSet( 1, nwcs, "", status );
ptr2 = astGetPoints( pset2 );
if( astOK ) {
for( iax = 0; iax < npix; iax++ ) {
ptr1[ iax ][ 0 ] = ( dim[ iax ] != AST__BAD ) ? floor( 0.5*dim[ iax ] ) : 1.0;
}
(void) astTransform( map, pset1, 1, pset2 );
}
/* Loop round all WCS axes, producing descriptions of any axes which have not
yet been described. */
for( iax = 0; iax < nwcs && astOK; iax++ ) {
if( ! axis_done[ iax ] ) {
/* Get the (one-based) FITS WCS axis index to use for this Frame axis. */
fits_i = wperm[ iax ];
/* Use the supplied default CRVAL value. If bad, use the WCS value
corresponding to the central pixel found above (if this value is bad,
abort). */
crval = crvals ? crvals[ iax ] : AST__BAD;
if( crval == AST__BAD ) crval = ptr2[ iax ][ 0 ];
if( crval == AST__BAD ) {
break;
} else {
SetItem( &(store->crval), fits_i, 0, s, crval, status );
}
/* Initialise flags indicating the type of the axis. */
log_axis = 0;
tab_axis = 0;
/* Get the table version number to use if we end up using the -TAB
algorithm. This is the set value of the TabOK attribute (if positive). */
extver = astGetTabOK( this );
/* See if the axis is linear. If so, create a ShiftMap which subtracts off
the CRVAL value. */
if( IsMapLinear( map, lbnd_p, ubnd_p, iax, status ) ) {
crval = -crval;
tmap0 = (AstMapping *) astShiftMap( 1, &crval, "", status );
axmap = AddUnitMaps( tmap0, iax, nwcs, status );
tmap0 = astAnnul( tmap0 );
crval = -crval;
/* If it is not linear, see if it is logarithmic. If the "log" algorithm is
appropriate (as defined in FITS-WCS paper III), the supplied Frame (s) is
related to pixel coordinate (p) by
s = Sr.EXP( a*p - b ). If this
is the case, the log of s will be linearly related to pixel coordinates.
Test this. If the test is passed a Mapping is returned from WCS to IWC. */
} else if( (axmap = LogAxis( map, iax, nwcs, lbnd_p, ubnd_p,
crval, status ) ) ) {
log_axis = 1;
/* If it is not linear or logarithmic, and the TabOK attribute is
non-zero, describe it using the -TAB algorithm. */
} else if( extver > 0 ){
/* Get any pre-existing FitsTable from the FitsStore. This is the table
in which the tabular data will be stored (if the Mapping can be expressed
in -TAB form). */
if( !astMapGet0A( store->tables, AST_TABEXTNAME, &table ) ) table = NULL;
/* See if the Mapping can be expressed in -TAB form. */
tmap0 = IsMapTab1D( map, 1.0, NULL, wcsfrm, dim, iax, fits_i,
&table, &icolmain, &icolindex, &interp,
status );
if( tmap0 ) {
tab_axis = 1;
/* The values stored in the table index vector are GRID coords. So we
need to ensure that IWC are equivalent to GRID coords. So set CRVAL
to zero. */
crval = 0.0;
/* Store TAB-specific values in the FitsStore. First the name of the
FITS binary table extension holding the coordinate info. */
SetItemC( &(store->ps), fits_i, 0, s, AST_TABEXTNAME, status );
/* Next the table version number. This is the set (positive) value for the
TabOK attribute. */
SetItem( &(store->pv), fits_i, 1, s, extver, status );
/* Also store the table version in the binary table header. */
astSetFitsI( table->header, "EXTVER", extver,
"Table version number", 0 );
/* Next the name of the table column containing the main coords array. */
SetItemC( &(store->ps), fits_i, 1, s,
astColumnName( table, icolmain ), status );
/* Next the name of the column containing the index array */
if( icolindex >= 0 ) SetItemC( &(store->ps), fits_i, 2, s,
astColumnName( table, icolindex ), status );
/* The interpolation method (an AST extension to the published -TAB
algorithm, communicated through the QVi_4a keyword). */
SetItem( &(store->pv), fits_i, 4, s, interp, status );
/* Also store the FitsTable itself in the FitsStore. */
astMapPut0A( store->tables, AST_TABEXTNAME, table, NULL );
/* Create the WCS -> IWC Mapping (AST uses grid coords as IWC coords for
the -TAB algorithm). First, get a Mapping that combines the TAB axis
Mapping( tmap0) in parallel with one or two UnitMaps in order to put
the TAB axis at the required index. */
tmap1 = AddUnitMaps( tmap0, iax, nwcs, status );
/* Now get a PermMap that permutes the WCS axes into the FITS axis order. */
inperm = astMalloc( sizeof( double )*nwcs );
outperm = astMalloc( sizeof( double )*nwcs );
if( astOK ) {
for( i = 0; i < nwcs; i++ ) {
inperm[ i ] = wperm[ i ];
outperm[ wperm[ i ] ] = i;
}
}
pm = astPermMap( nwcs, inperm, nwcs, outperm, NULL, "",
status );
/* Combine these two Mappings in series, to get the Mapping from WCS to
IWC. */
axmap = (AstMapping *) astCmpMap( pm, tmap1, 1, " ",
status );
/* Free resources. */
inperm = astFree( inperm );
outperm = astFree( outperm );
pm = astAnnul( pm );
tmap0 = astAnnul( tmap0 );
tmap1 = astAnnul( tmap1 );
}
if( table ) table = astAnnul( table );
}
/* If the axis cannot be described by any of the above methods, we
pretend it is linear. This will generate a non-linear PIXEL->IWC
mapping later (in MakeIntWorld) which will cause the write operation
to fail. */
if( !axmap ) {
crval = -crval;
tmap0 = (AstMapping *) astShiftMap( 1, &crval, "", status );
axmap = AddUnitMaps( tmap0, iax, nwcs, status );
tmap0 = astAnnul( tmap0 );
crval = -crval;
}
/* Combine the Mapping for this axis in series with those of earlier axes. */
if( ret ) {
tmap0 = (AstMapping *) astCmpMap( ret, axmap, 1, "", status );
(void) astAnnul( ret );
ret = tmap0;
} else {
ret = astClone( axmap );
}
/* Get axis label and symbol. */
sym = astGetSymbol( wcsfrm, iax );
lab = astGetLabel( wcsfrm, iax );
/* The axis symbols are taken as the CTYPE values. Append "-LOG" or "-TAB" if
the axis is logarithmic or tabular. */
if( sym && strlen( sym ) ) {
(void) sprintf( buf, "%s", sym );
} else {
(void) sprintf( buf, "AXIS%d", iax + 1 );
}
if( log_axis ) {
SetAlgCode( buf, "-LOG", status );
} else if( tab_axis ) {
SetAlgCode( buf, "-TAB", status );
}
SetItemC( &(store->ctype), fits_i, 0, s, buf, status );
/* The axis labels are taken as the comment for the CTYPE keywords and as
the CNAME keyword (but only if a label has been set and is different to
the symbol). */
if( lab && lab[ 0 ] && astTestLabel( wcsfrm, iax ) && strcmp( sym, lab ) ) {
SetItemC( &(store->ctype_com), fits_i, 0, s, (char *) lab, status );
SetItemC( &(store->cname), fits_i, 0, s, (char *) lab, status );
} else {
sprintf( buf, "Type of co-ordinate on axis %d", iax + 1 );
SetItemC( &(store->ctype_com), fits_i, 0, s, buf, status );
}
/* If a value has been set for the axis units, use it as CUNIT. */
if( astTestUnit( wcsfrm, iax ) ){
SetItemC( &(store->cunit), fits_i, 0, s, (char *) astGetUnit( wcsfrm, iax ), status );
}
/* Indicate this axis has now been described. */
axis_done[ iax ] = 1;
/* Release Resources. */
axmap = astAnnul( axmap );
}
}
/* Release Resources. */
wcsfrm = astAnnul( wcsfrm );
map = astAnnul( map );
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
lbnd_p = astFree( lbnd_p );
ubnd_p = astFree( ubnd_p );
}
/* If we have a Mapping to return, simplify it. Otherwise, create
a UnitMap to return. */
if( ret ) {
tmap0 = ret;
ret = astSimplify( tmap0 );
tmap0 = astAnnul( tmap0 );
} else {
ret = (AstMapping *) astUnitMap( nwcs, "", status );
}
/* Return the result. */
return ret;
}
static int PCFromStore( AstFitsChan *this, FitsStore *store,
const char *method, const char *class, int *status ){
/*
* Name:
* PCFromStore
* Purpose:
* Store WCS keywords in a FitsChan using FITS-PC encoding.
* Type:
* Private function.
* Synopsis:
* int PCFromStore( AstFitsChan *this, FitsStore *store,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* A FitsStore is a structure containing a generalised represention of
* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
* from a set of FITS header cards (using a specified encoding), or
* an AST FrameSet. In other words, a FitsStore is an encoding-
* independant intermediary staging post between a FITS header and
* an AST FrameSet.
*
* This function copies the WCS information stored in the supplied
* FitsStore into the supplied FitsChan, using FITS-PC encoding.
*
* Zero is returned if the primary axis descriptions cannot be produced.
* Whether or not secondary axis descriptions can be produced does not
* effect the returned value (i.e. failure to produce a specific set of
* secondary axes does not prevent other axis descriptions from being
* produced).
* Parameters:
* this
* Pointer to the FitsChan.
* store
* Pointer to the FitsStore.
* 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:
* A value of 1 is returned if succesfull, and zero is returned
* otherwise.
*/
/* Local Variables: */
char *comm; /* Pointer to comment string */
char *cval; /* Pointer to string keyword value */
char combuf[80]; /* Buffer for FITS card comment */
char keyname[10]; /* Buffer for keyword name string */
char primsys[20]; /* Buffer for primnary RADECSYS value */
char type[MXCTYPELEN];/* Buffer for CTYPE value */
char s; /* Co-ordinate version character */
char sign[2]; /* Fraction's sign character */
char sup; /* Upper limit on s */
double *c; /* Pointer to next array element */
double *d; /* Pointer to next array element */
double *matrix; /* Pointer to Frame PC/CD matrix */
double *primpc; /* Pointer to primary PC/CD matrix */
double fd; /* Fraction of a day */
double mjd99; /* MJD at start of 1999 */
double primdt; /* Primary mjd-obs value */
double primeq; /* Primary equinox value */
double primln; /* Primary lonpole value */
double primlt; /* Primary latpole value */
double primpv[10]; /* Primary projection parameter values */
double val; /* General purpose value */
int axlat; /* Index of latitude FITS WCS axis */
int axlon; /* Index of longitude FITS WCS axis */
int axspec; /* Index of spectral FITS WCS axis */
int i; /* Axis index */
int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */
int is; /* Co-ordinate version index */
int iymdf[ 4 ]; /* Year, month, date, fractional day */
int j; /* Axis index */
int jj; /* SlaLib status */
int m; /* Parameter index */
int maxm; /* Upper limit on m */
int naxis; /* No. of axes */
int nc; /* Length of string */
int ok; /* Frame written out succesfully? */
int prj; /* Projection type */
int ret; /* Returned value. */
/* Initialise */
ret = 0;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Find the number of co-ordinate versions in the FitsStore. FITS-PC
can only encode 10 axis descriptions (including primary). */
sup = GetMaxS( &(store->crval), status );
if( sup > 'I' ) return ret;
/* Initialise */
primdt = AST__BAD;
primeq = AST__BAD;
primln = AST__BAD;
primlt = AST__BAD;
/* Loop round all co-ordinate versions (0-9) */
primpc = NULL;
for( s = ' '; s <= sup && astOK; s++ ){
is = s - 'A' + 1;
/* Assume the Frame can be created succesfully. */
ok = 1;
/* Save the number of wcs axes */
val = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) {
naxis = (int) ( val + 0.5 );
SetValue( this, FormatKey( "WCSAXES", -1, -1, s, status ),
&naxis, AST__INT, "Number of WCS axes", status );
} else {
naxis = GetMaxJM( &(store->crpix), s, status ) + 1;
}
/* PC matrix:
--------- */
/* This encoding does not allow the PC matrix to be specified for each
version - instead they all share the primary PC matrix. Therefore we
need to check that all versions can use the primary PC matrix. Allocate
memory to hold the PC matrix for this version. */
matrix = (double *) astMalloc( sizeof(double)*naxis*naxis );
if( matrix ){
/* Fill these array with the values supplied in the FitsStore. */
c = matrix;
for( i = 0; i < naxis; i++ ){
for( j = 0; j < naxis; j++ ){
*c = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
if( *c == AST__BAD ) *c = ( i == j ) ? 1.0 : 0.0;
c++;
}
}
/* If we are currently processing the primary axis description, take
a copy of the PC matrix. */
if( s == ' ' ) {
primpc = (double *) astStore( NULL, (void *) matrix,
sizeof(double)*naxis*naxis );
/* Store each matrix element in turn. */
c = matrix;
for( i = 0; i < naxis; i++ ){
for( j = 0; j < naxis; j++ ){
/* Set the element bad if it takes its default value. */
val = *(c++);
if( i == j ){
if( EQUAL( val, 1.0 ) ) val = AST__BAD;
} else {
if( EQUAL( val, 0.0 ) ) val = AST__BAD;
}
/* Only store elements which do not take their default values. */
if( val != AST__BAD ){
sprintf( keyname, "PC%.3d%.3d", i + 1, j + 1 );
SetValue( this, keyname, &val, AST__FLOAT, NULL, status );
}
}
}
/* For secondary axis descriptions, a check is made that the PC values are
the same as the primary PC values stored earlier. If not, the current
Frame cannot be stored as a secondary axis description so continue on
to the next Frame. */
} else {
if( primpc ){
c = matrix;
d = primpc;
for( i = 0; i < naxis; i++ ){
for( j = 0; j < naxis; j++ ){
if( !EQUAL( *c, *d ) ){
ok = 0;
} else {
c++;
d++;
}
}
}
/* Continue with the next Frame if the PC matrix for this Frame is different
to the primary PC matrix. */
if( !ok ) goto next;
}
}
matrix = (double *) astFree( (void *) matrix );
}
/* CDELT:
------ */
for( i = 0; i < naxis; i++ ){
val = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
if( val == AST__BAD ) {
ok = 0;
goto next;
}
sprintf( combuf, "Pixel scale on axis %d", i + 1 );
if( s == ' ' ) {
sprintf( keyname, "CDELT%d", i + 1 );
} else {
sprintf( keyname, "C%dELT%d", is, i + 1 );
}
SetValue( this, keyname, &val, AST__FLOAT, combuf, status );
}
/* CRPIX:
------ */
for( j = 0; j < naxis; j++ ){
val = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status );
if( val == AST__BAD ) {
ok = 0;
goto next;
}
sprintf( combuf, "Reference pixel on axis %d", j + 1 );
if( s == ' ' ) {
sprintf( keyname, "CRPIX%d", j + 1 );
} else {
sprintf( keyname, "C%dPIX%d", is, j + 1 );
}
SetValue( this, keyname, &val, AST__FLOAT, combuf, status );
}
/* CRVAL:
------ */
for( i = 0; i < naxis; i++ ){
val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
if( val == AST__BAD ) {
ok = 0;
goto next;
}
sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 );
if( s == ' ' ) {
sprintf( keyname, "CRVAL%d", i + 1 );
} else {
sprintf( keyname, "C%dVAL%d", is, i + 1 );
}
SetValue( this, keyname, &val, AST__FLOAT, combuf, status );
}
/* CTYPE:
------ */
for( i = 0; i < naxis; i++ ){
cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
nc = strlen( cval );
if( !cval || ( nc > 4 && !strcmp( cval + 4, "-TAB" ) ) ) {
ok = 0;
goto next;
}
comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status );
if( !comm ) {
sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 );
comm = combuf;
}
if( s == ' ' ) {
sprintf( keyname, "CTYPE%d", i + 1 );
} else {
sprintf( keyname, "C%dYPE%d", is, i + 1 );
}
/* FITS-PC cannot handle celestial axes of type "xxLT" or "xxLN".
Neither can it handle the "-TAB". */
if( ( nc > 2 && !strncmp( cval + 2, "LT-", 3 ) ) ||
( nc > 2 && !strncmp( cval + 2, "LN-", 3 ) ) ||
( nc > 4 && !strncmp( cval + 4, "-TAB", 4 ) ) ){
ok = 0;
goto next;
}
/* Extract the projection type as specified by the last 4 characters
in the CTYPE keyword value. This will be AST__WCSBAD for non-celestial
axes. */
prj = astWcsPrjType( cval + 4 );
/* Change the new SFL projection code to to the older equivalent GLS */
if( prj == AST__SFL ) {
strcpy( type, cval );
(void) strcpy( type + 4, "-GLS" );
cval = type;
}
/* FITS-PC cannot handle the AST-specific TPN projection. */
if( prj == AST__TPN ) {
ok = 0;
goto next;
}
/* Store the CTYPE value */
SetValue( this, keyname, &cval, AST__STRING, comm, status );
}
/* Get and save CUNIT for all intermediate axes. These are NOT required, so
do not pass on if they are not available. */
for( i = 0; i < naxis; i++ ){
cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status );
if( cval ) {
sprintf( combuf, "Units for axis %d", i + 1 );
if( s == ' ' ) {
sprintf( keyname, "CUNIT%d", i + 1 );
} else {
sprintf( keyname, "C%dNIT%d", is, i + 1 );
}
SetValue( this, keyname, &cval, AST__STRING, combuf, status );
}
}
/* Get and save RADESYS. This is NOT required, so do not pass on if it is
not available. If RADECSYS is provided for a secondary axis, it must
be the same as the primary axis RADECSYS value. If it is not, pass on to
the next Frame. */
cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status );
if( cval ) {
if( s == ' ' ) {
strcpy( primsys, cval );
SetValue( this, "RADECSYS", &cval, AST__STRING,
"Reference frame for RA/DEC values", status );
} else if( strcmp( cval, primsys ) ) {
ok = 0;
goto next;
}
}
/* Reference equinox. This is NOT required, so do not pass on if it is
not available. If equinox is provided for a secondary axis, it must
be the same as the primary axis equinox value. If it is not, pass on to
the next Frame. */
val = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status );
if( s == ' ' ) {
primeq = val;
if( val != AST__BAD ) SetValue( this, "EQUINOX", &val, AST__FLOAT,
"Epoch of reference equinox", status );
} else if( !EQUAL( val, primeq ) ){
ok = 0;
goto next;
}
/* Latitude of native north pole. This is NOT required, so do not pass on
if it is not available. If latpole is provided for a secondary axis, it
must be the same as the primary axis value. If it is not, pass on to
the next Frame. */
val = GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status );
if( s == ' ' ) {
primlt = val;
if( val != AST__BAD ) SetValue( this, "LATPOLE", &val, AST__FLOAT,
"Latitude of native north pole", status );
} else if( !EQUALANG( val, primlt ) ){
ok = 0;
goto next;
}
/* Longitude of native north pole. This is NOT required, so do not pass on
if it is not available. If lonpole is provided for a secondary axis, it
must be the same as the primary axis value. If it is not, pass on to
the next Frame. */
val = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status );
if( s == ' ' ) {
primln = val;
if( val != AST__BAD ) SetValue( this, "LONGPOLE", &val, AST__FLOAT,
"Longitude of native north pole", status );
} else if( !EQUALANG( val, primln ) ){
ok = 0;
goto next;
}
/* Date of observation. This is NOT required, so do not pass on if it is
not available. If mjd-obs is provided for a secondary axis, it must be
the same as the primary axis value. If it is not, pass on to the next
Frame. */
val = GetItem( &(store->mjdobs), 0, 0, s, NULL, method, class, status );
if( s == ' ' ) {
primdt = val;
if( val != AST__BAD ) {
SetValue( this, "MJD-OBS", &val, AST__FLOAT,
"Modified Julian Date of observation", status );
/* The format used for the DATE-OBS keyword depends on the value of the
keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format.
Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */
palCaldj( 99, 1, 1, &mjd99, &jj );
if( val < mjd99 ) {
palDjcal( 0, val, iymdf, &jj );
sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ],
iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) );
} else {
palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj );
palDd2tf( 3, fd, sign, ihmsf );
sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d",
iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1],
ihmsf[2], ihmsf[3] );
}
/* Now store the formatted string in the FitsChan. */
cval = combuf;
SetValue( this, "DATE-OBS", &cval, AST__STRING,
"Date of observation", status );
}
} else if( !EQUAL( val, primdt ) ){
ok = 0;
goto next;
}
/* Look for the celestial and spectral axes. */
FindLonLatSpecAxes( store, s, &axlon, &axlat, &axspec, method, class, status );
/* If both longitude and latitude axes are present ...*/
if( axlon >= 0 && axlat >= 0 ) {
/* Get the CTYPE values for the latitude axis. */
cval = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status );
/* Extract the projection type as specified by the last 4 characters
in the CTYPE keyword value. */
prj = ( cval ) ? astWcsPrjType( cval + 4 ) : AST__WCSBAD;
/* Projection parameters. If provided for a secondary axis, they must be
the same as the primary axis value. If it is not, pass on to the next
Frame. PC encoding ignores parameters associated with the longitude
axis. The old PC TAN projection did not have any parameters.
Pass on if a TAN projection with parameters is found. The number of
parameters was limited to 10. Pass on if more than 10 are supplied. */
maxm = GetMaxJM( &(store->pv), ' ', status );
for( i = 0; i < naxis; i++ ){
if( i != axlon ) {
for( m = 0; m <= maxm; m++ ){
val = GetItem( &(store->pv), i, m, s, NULL, method, class, status );
if( s == ' ' ){
if( val != AST__BAD ) {
if( i != axlat || prj == AST__TAN || m >= 10 ){
ok = 0;
goto next;
} else {
SetValue( this, FormatKey( "PROJP", m, -1, ' ', status ), &val,
AST__FLOAT, "Projection parameter", status );
}
}
if( i == axlat && m < 10 ) primpv[m] = val;
} else {
if( ( ( i != axlat || m >= 10 ) && val != AST__BAD ) ||
( i == axlat && m < 10 && !EQUAL( val, primpv[m] ) ) ){
ok = 0;
goto next;
}
}
}
}
}
}
/* See if a Frame was sucessfully written to the FitsChan. */
next:
ok = ok && astOK;
/* If so, indicate we have something to return. */
if( ok ) ret = 1;
/* Clear any error status so we can continue to produce the next Frame.
Retain the error if the primary axes could not be produced. After the
primary axes, do the A axes. */
if( s != ' ' ) {
astClearStatus;
} else {
s = 'A' - 1;
}
/* Remove the secondary "new" flags from the FitsChan. This flag is
associated with cards which have been added to the FitsChan during
this pass through the main loop in this function. If the Frame was
written out succesfully, just clear the flags. If anything went wrong
with this Frame, remove the flagged cards from the FitsChan. */
FixNew( this, NEW2, !ok, method, class, status );
/* Set the current card so that it points to the last WCS-related keyword
in the FitsChan (whether previously read or not). */
FindWcs( this, 1, 1, 0, method, class, status );
}
/* Annul the array holding the primary PC matrix. */
primpc = (double *) astFree( (void *) primpc );
/* Return zero or ret depending on whether an error has occurred. */
return astOK ? ret : 0;
}
static void PreQuote( const char *value,
char string[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ], int *status ) {
/*
* Name:
* PreQuote
* Purpose:
* Pre-quote FITS character data.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void PreQuote( const char *value,
* char string[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ] )
* Class Membership:
* FitsChan member function.
* Description:
* This function processes a string value in such a way that it can
* be stored as a FITS character value (associated with a keyword)
* and later retrieved unchanged, except for possible truncation.
*
* This pre-processing is necessary because FITS does not regard
* trailing white space as significant, so it is lost. This
* function adds double quote (") characters around the string if
* it is necessary in order to prevent this loss. These quotes are
* also added to zero-length strings and to strings that are
* already quoted (so that the original quotes are not lost when
* they are later un-quoted).
*
* This function will silently truncate any string that is too long
* to be stored as a FITS character value, but will ensure that the
* maximum number of characters are retained, taking account of any
* quoting required.
* Parameters:
* value
* Pointer to a constant null-terminated string containing the
* input character data to be quoted. All white space is
* significant.
* string
* A character array into which the result string will be
* written, with a terminating null. The maximum number of
* characters from the input string that can be accommodated in
* this is (AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 4), but this
* will be reduced if quoting is necessary.
* Notes:
* - The UnPreQuote function should be used to reverse the effect
* of this function on a string (apart from any truncation).
*/
/* Local Variables: */
int dq; /* Number of double quotes needed */
int dquotes; /* Final number of double quotes */
int i; /* Loop counter for input characters */
int j; /* Counter for output characters */
int nc; /* Number of characters to be accommodated */
int sq; /* Number of single quotes needed */
/* Check the global error status. */
if ( !astOK ) return;
/* Initialise, setting the default number of double quotes (which
applies to a zero-length string) to 2. */
dquotes = 2;
nc = 0;
sq = 0;
/* Loop to consider each input character to see if it will fit into
the result string. */
for ( i = 0; value[ i ]; i++ ) {
/* If a single quote character is to be included, count it. When the
string is encoded as FITS character data, these quotes will be
doubled, so will increase the overall string length by one. */
if ( value[ i ] == '\'' ) sq++;
/* See how many double quotes are needed around the string (0 or
2). These are needed if there is trailing white space that needs
protecting (this is not significant in FITS and will be removed),
or if the string already has quotes at either end (in which case an
extra set is needed to prevent the original ones being removed when
it is later un-quoted). Note we do not need to double existing
double quote characters within the string, because the position of
the ends of the string are known (from the quoting supplied by
FITS) so only the first and last characters need be inspected when
un-quoting the string.
In assessing the number of double quotes, assume the string will be
truncated after the current character. */
dq = ( isspace( value[ i ] ) ||
( ( value[ 0 ] == '"' ) && ( value[ i ] == '"' ) ) ) ? 2 : 0;
/* See if the length of the resulting string, including the current
character and all necessary quotes, is too long. If so, give up
here. */
if ( ( nc + 1 + dq + sq ) >
( AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 4 ) ) break;
/* If the string is not too long, accept the character and note the
number of double quotes needed. */
nc = i + 1;
dquotes = dq;
}
/* If double quotes are needed, insert the opening quote into the
output string. */
j = 0;
if ( dquotes ) string[ j++ ] = '"';
/* Follow this with the maximum number of input string characters that
can be accommodated. */
for ( i = 0; i < nc; i++ ) string[ j++ ] = value[ i ];
/* Append the closing quote if necessary and terminate the output
string. */
if ( dquotes ) string[ j++ ] = '"';
string[ j ] = '\0';
}
static void PurgeWCS( AstFitsChan *this, int *status ){
/*
*++
* Name:
c astPurgeWCS
f AST_PURGEWCS
* Purpose:
* Delete all cards in the FitsChan describing WCS information.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c void astPurgeWCS( AstFitsChan *this )
f CALL AST_PURGEWCS( THIS, STATUS )
* Class Membership:
* FitsChan method.
* Description:
c This function
f This routine
* deletes all cards in a FitsChan that relate to any of the recognised
* WCS encodings. On exit, the current card is the first remaining card
* in the FitsChan.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
f STATUS = INTEGER (Given and Returned)
f The global status.
*--
*/
/* Local Variables: */
AstObject *obj;
int oldclean;
/* Check the global status. */
if( !astOK ) return;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Ensure the Clean attribute is set so that WCS keywords are removed
even if an error occurs. */
if( astTestClean( this ) ) {
oldclean = astGetClean( this );
astSetClean( this, 1 );
} else {
astSetClean( this, 1 );
oldclean = -1;
}
/* Loop round attempting to read AST objects form the FitsChan. This will
flag cards as used that are involved in the creation of these object
(including NATIVE encodings). Ignore any error that ocurs whilst doing
this. */
astClearCard( this );
if( astOK ) {
int oldreporting = astReporting( 0 );
obj = astRead( this );
while( obj ) {
obj = astAnnul( obj );
astClearCard( this );
obj = astRead( this );
}
if( !astOK ) astClearStatus;
astReporting( oldreporting );
}
/* We now loop round to remove any spurious WCS-related cards left in the
FitsChan that did not form part of a complete WCS encoding. Find the
first WCS-related card left in the FitsChan. */
FindWcs( this, 0, 0, 1, "DeleteWcs", "FitsChan", status );
/* Loop round marking each WCS-related card as used until none are left */
while( this->card && astOK ) {
/* Mark the current card as having been read. */
( (FitsCard*) this->card )->flags = USED;
/* Find the next WCS-related card. */
FindWcs( this, 0, 0, 0, "DeleteWcs", "FitsChan", status );
}
/* Rewind the FitsChan. */
astClearCard( this );
/* Reset the Clean attribute. */
if( oldclean == -1 ) {
astClearClean( this );
} else {
astSetClean( this, oldclean );
}
}
static void PutCards( AstFitsChan *this, const char *cards, int *status ) {
/*
*++
* Name:
c astPutCards
f AST_PUTCARDS
* Purpose:
* Store a set of FITS header cards in a FitsChan.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c void astPutCards( AstFitsChan *this, const char *cards )
f CALL AST_PUTCARDS( THIS, CARDS, STATUS )
* Class Membership:
* FitsChan method.
* Description:
c This function
f This routine
* stores a set of FITS header cards in a FitsChan. The cards are
* supplied concatenated together into a single character string.
* Any existing cards in the FitsChan are removed before the new cards
* are added. The FitsChan is "re-wound" on exit by clearing its Card
* attribute. This means that a subsequent invocation of
c astRead
f AST_READ
* can be made immediately without the need to re-wind the FitsChan
* first.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
c cards
f CARDS = CHARACTER * ( * ) (Given)
c Pointer to a null-terminated character string
f A character string
* containing the FITS cards to be stored. Each individual card
* should occupy 80 characters in this string, and there should be
* no delimiters, new lines, etc, between adjacent cards. The final
* card may be less than 80 characters long.
c This is the format produced by the fits_hdr2str function in the
c CFITSIO library.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Notes:
* - An error will result if the supplied string contains any cards
* which cannot be interpreted.
*--
*/
/* Local Variables: */
const char *a; /* Pointer to start of next card */
int clen; /* Length of supplied string */
int i; /* Card index */
int ncard; /* No. of cards supplied */
/* Check the global error status. */
if ( !astOK ) return;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Empty the FitsChan. */
astEmptyFits( this );
/* Loop round the supplied string in 80 character segments, inserting
each segment into the FitsChan as a header card. Allow the last card
to be less than 80 characters long. */
clen = strlen( cards );
ncard = clen/80;
if( ncard*80 < clen ) ncard++;
a = cards;
for( i = 0; i < ncard; i++, a += 80 ) astPutFits( this, a, 1 );
/* Rewind the FitsChan. */
astClearCard( this );
}
static void PutFits( AstFitsChan *this, const char card[ AST__FITSCHAN_FITSCARDLEN + 1 ],
int overwrite, int *status ){
/*
*++
* Name:
c astPutFits
f AST_PUTFITS
* Purpose:
* Store a FITS header card in a FitsChan.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c void astPutFits( AstFitsChan *this, const char card[ 80 ],
c int overwrite )
f CALL AST_PUTFITS( THIS, CARD, OVERWRITE, STATUS )
* Class Membership:
* FitsChan method.
* Description:
c This function stores a FITS header card in a FitsChan. The card
f This routine stores a FITS header card in a FitsChan. The card
* is either inserted before the current card (identified by the
* Card attribute), or over-writes the current card, as required.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
c card
f CARD = CHARACTER * ( 80 ) (Given)
c Pointer to a possibly null-terminated character string
c containing the FITS card to be stored. No more than 80
c characters will be used from this string (or fewer if a null
c occurs earlier).
f A character string string containing the FITS card to be
f stored. No more than 80 characters will be used from this
f string.
c overwrite
f OVERWRITE = LOGICAL (Given)
c If this value is zero, the new card is inserted in front of
f If this value is .FALSE., the new card is inserted in front of
* the current card in the FitsChan (as identified by the
c initial value of the Card attribute). If it is non-zero, the
f initial value of the Card attribute). If it is .TRUE., the
* new card replaces the current card. In either case, the Card
* attribute is then incremented by one so that it subsequently
* identifies the card following the one stored.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Notes:
* - If the Card attribute initially points at the "end-of-file"
* (i.e. exceeds the number of cards in the FitsChan), then the new
* card is appended as the last card in the FitsChan.
* - An error will result if the supplied string cannot be interpreted
* as a FITS header card.
*--
*/
/* Local Variables: */
char *comment; /* The keyword comment */
char *name; /* The keyword name */
char *value; /* The keyword value */
const char *class; /* Object class */
const char *method; /* Current method */
double cfval[2]; /* Complex floating point keyword value */
double fval; /* floating point keyword value */
int cival[2]; /* Complex integer keyword value */
int ival; /* Integer keyword value */
int len; /* No. of characters to read from the value string */
int nc; /* No. of characters read from value string */
int type; /* Keyword data type */
/* Check the global error status. */
if ( !astOK ) return;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Store the current method, and the class of the supplied object for use
in error messages.*/
method = "astPutFits";
class = astGetClass( this );
/* Split the supplied card up into name, value and commment strings, and
get pointers to local copies of them. The data type associated with the
keyword is returned. */
type = Split( this, card, &name, &value, &comment, method, class, status );
/* Check that the pointers can be used. */
if( astOK ){
/* Initialise the number of characters read from the value string. */
nc = 0;
/* Store the number of characters in the value string. */
len = strlen( value );
/* Read and store floating point values from the value string. NB, this
list is roughly in the order of descreasing frequency of use (i.e.
most FITS keywords are simple floating point values, the next most
common are strings, etc). */
if( type == AST__FLOAT ){
if( 1 == astSscanf( value, " %lf %n", &fval, &nc ) && nc >= len ){
astSetFitsF( this, name, fval, comment, overwrite );
} else {
astError( AST__BDFTS, "%s(%s): Unable to read a floating point "
"FITS keyword value.", status, method, class );
}
/* Read and store string values from the value string. */
} else if( type == AST__STRING ){
astSetFitsS( this, name, value, comment, overwrite );
/* Read and store string values from the value string. */
} else if( type == AST__CONTINUE ){
astSetFitsCN( this, name, value, comment, overwrite );
/* Store comment card. */
} else if( type == AST__COMMENT ){
astSetFitsCom( this, name, comment, overwrite );
/* Read and store integer values from the value string. */
} else if( type == AST__INT ){
if( 1 == astSscanf( value, " %d %n", &ival, &nc ) && nc >= len ){
astSetFitsI( this, name, ival, comment, overwrite );
} else {
astError( AST__BDFTS, "%s(%s): Unable to read an integer FITS "
"keyword value.", status, method, class );
}
/* Read and store logical values from the value string. */
} else if( type == AST__LOGICAL ){
astSetFitsL( this, name, (*value == 'T'), comment, overwrite );
/* Read and store undefined values from the value string. */
} else if( type == AST__UNDEF ){
astSetFitsU( this, name, comment, overwrite );
/* Read and store complex floating point values from the value string. */
} else if( type == AST__COMPLEXF ){
if( 2 == astSscanf( value, " %lf %lf %n", cfval, cfval + 1, &nc ) &&
nc >= len ){
astSetFitsCF( this, name, cfval, comment, overwrite );
} else {
astError( AST__BDFTS, "%s(%s): Unable to read a complex pair "
"of floating point FITS keyword values.", status, method, class );
}
/* Read and store complex integer values from the value string. */
} else if( type == AST__COMPLEXI ){
if( 2 == astSscanf( value, " %d %d %n", cival, cival + 1, &nc ) &&
nc >= len ){
astSetFitsCI( this, name, cival, comment, overwrite );
} else {
astError( AST__BDFTS, "%s(%s): Unable to read a complex pair "
"of integer FITS keyword values.", status, method, class );
}
/* Report an error for any other type. */
} else {
astError( AST__INTER, "%s: AST internal programming error - "
"FITS data-type '%d' not yet supported.", status, method, type );
}
/* Give a context message if an error occurred. */
if( !astOK ){
astError( astStatus, "%s(%s): Unable to store the following FITS "
"header card:\n%s\n", status, method, class, card );
}
}
/* Free the memory used to hold the keyword name, comment and value
strings. */
(void) astFree( (void *) name );
(void) astFree( (void *) comment );
(void) astFree( (void *) value );
}
static void PutTable( AstFitsChan *this, AstFitsTable *table,
const char *extnam, int *status ) {
/*
*++
* Name:
c astPutTable
f AST_PUTTABLE
* Purpose:
* Store a single FitsTable in a FitsChan.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c void astPutTable( AstFitsChan *this, AstFitsTable *table,
c const char *extnam )
f CALL AST_PUTTABLE( THIS, TABLE, EXTNAM, STATUS )
* Class Membership:
* FitsChan method.
* Description:
c This function
f This routine
* allows a representation of a single FITS binary table to be
* stored in a FitsChan. For instance, this may provide the coordinate
* look-up tables needed subequently when reading FITS-WCS headers
* for axes described using the "-TAB" algorithm. Since, in general,
* the calling application may not know which tables will be needed -
* if any - prior to calling
c astRead, the astTablesSource function
f AST_READ, the AST_TABLESOURCE routine
* provides an alternative mechanism in which a caller-supplied
* function is invoked to store a named table in the FitsChan.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
c table
f TABLE = INTEGER (Given)
* Pointer to a FitsTable to be added to the FitsChan. If a FitsTable
* with the associated extension name already exists in the FitsChan,
* it is replaced with the new one. A deep copy of the FitsTable is
* stored in the FitsChan, so any subsequent changes made to the
* FitsTable will have no effect on the behaviour of the FitsChan.
c extnam
f EXTNAM = CHARACTER * ( * ) (Given)
* The name of the FITS extension associated with the table.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Notes:
* - Tables stored in the FitsChan may be retrieved using
c astGetTables.
f AST_GETTABLES.
c - The astPutTables method can add multiple FitsTables in a single call.
f - The AST_PUTTABLES method can add multiple FitsTables in a single call.
*--
*/
/* Local Variables: */
AstObject *ft;
/* Check the global error status. */
if ( !astOK ) return;
/* Create a KeyMap to hold the tables within the FitsChan, if this has not
already been done. */
if( !this->tables ) this->tables = astKeyMap( " ", status );
/* Store a copy of the FitsTable in the FitsChan. */
ft = astCopy( table );
astMapPut0A( this->tables, extnam, ft, NULL );
ft = astAnnul( ft );
}
static void PutTables( AstFitsChan *this, AstKeyMap *tables, int *status ) {
/*
*++
* Name:
c astPutTables
f AST_PUTTABLES
* Purpose:
* Store one or more FitsTables in a FitsChan.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c void astPutTables( AstFitsChan *this, AstKeyMap *tables )
f CALL AST_PUTTABLES( THIS, TABLES, STATUS )
* Class Membership:
* FitsChan method.
* Description:
c This function
f This routine
* allows representations of one or more FITS binary tables to be
* stored in a FitsChan. For instance, these may provide the coordinate
* look-up tables needed subequently when reading FITS-WCS headers
* for axes described using the "-TAB" algorithm. Since, in general,
* the calling application may not know which tables will be needed -
* if any - prior to calling
c astRead, the astTablesSource function
f AST_READ, the AST_TABLESOURCE routine
* provides an alternative mechanism in which a caller-supplied
* function is invoked to store a named table in the FitsChan.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
c tables
f TABLES = INTEGER (Given)
* Pointer to a KeyMap holding the tables that are to be added
* to the FitsChan. Each entry should hold a scalar value which is a
* pointer to a FitsTable to be added to the FitsChan. Any unusable
* entries are ignored. The key associated with each entry should be
* the name of the FITS binary extension from which the table was
* read. If a FitsTable with the associated key already exists in the
* FitsChan, it is replaced with the new one. A deep copy of each
* usable FitsTable is stored in the FitsChan, so any subsequent
* changes made to the FitsTables will have no effect on the
* behaviour of the FitsChan.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Notes:
* - Tables stored in the FitsChan may be retrieved using
c astGetTables.
f AST_GETTABLES.
* - The tables in the supplied KeyMap are added to any tables already
* in the FitsChan.
c - The astPutTable
f - The AST_PUTTABLE
* method provides a simpler means of adding a single table to a FitsChan.
*--
*/
/* Local Variables: */
AstObject *obj;
const char *key;
int ientry;
int nentry;
/* Check the global error status. */
if ( !astOK ) return;
/* Loop through all entries in the supplied KeyMap. */
nentry = astMapSize( tables );
for( ientry = 0; ientry < nentry; ientry++ ) {
key = astMapKey( tables, ientry );
/* Ignore entries that do not contain AST Object pointers, or are not
scalar. */
if( astMapType( tables, key ) == AST__OBJECTTYPE &&
astMapLength( tables, key ) == 1 ) {
/* Get the pointer, amd ignore it if it is not a FitsTable. */
astMapGet0A( tables, key, &obj );
if( astIsAFitsTable( obj ) ) {
/* Store it in the FitsChan. */
astPutTable( this, (AstFitsTable *) obj, key );
}
/* Annul the Object pointer. */
obj = astAnnul( obj );
}
}
}
static AstObject *Read( AstChannel *this_channel, int *status ) {
/*
* Name:
* Read
* Purpose:
* Read an Object from a Channel.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstObject *Read( AstChannel *this_channel, int *status )
* Class Membership:
* FitsChan member function (over-rides the astRead method
* inherited from the Channel class).
* Description:
* This function reads an Object from a FitsChan.
* Parameters:
* this
* Pointer to the FitsChan.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the new Object. This will always be a FrameSet.
* Notes:
* - The pixel Frame is given a title of "Pixel Coordinates", and
* each axis in the pixel Frame is given a label of the form "Pixel
* axis ", where is the axis index (starting at one).
* - The FITS CTYPE keyword values are used to set the labels for any
* non-celestial axes in the physical coordinate Frames, and the FITS
* CUNIT keywords are used to set the corresponding units strings.
* - On exit, the pixel Frame is the base Frame, and the physical
* Frame derived from the primary axis descriptions is the current Frame.
* - Extra Frames are added to hold any secondary axis descriptions. All
* axes within such a Frame refer to the same coordinate version ('A',
* 'B', etc).
* - For foreign encodings, the first card in the FitsChan must be
* the current card on entry (otherwise a NULL pointer is returned),
* and the FitsChan is left at end-of-file on exit.
* - For the Native encoding, reading commences from the current card
* on entry (which need not be the first in the FitsChan), and the
* current Card on exit is the first card following the last one read
* (or end-of-file).
*/
/* Local Variables: */
AstObject *new; /* Pointer to returned Object */
AstFitsChan *this; /* Pointer to the FitsChan structure */
FitsStore *store; /* Intermediate storage for WCS information */
const char *method; /* Pointer to string holding calling method */
const char *class; /* Pointer to string holding object class */
int encoding; /* The encoding scheme */
int remove; /* Remove used cards? */
/* Initialise. */
new = NULL;
/* Check the global error status. */
if ( !astOK ) return new;
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_channel;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Store the calling method, and object class. */
method = "astRead";
class = astGetClass( this );
/* Get the encoding scheme used by the FitsChan. */
encoding = astGetEncoding( this );
/* If we are reading from a FitsChan in which AST objects are encoded using
native AST-specific keywords, use the Read method inherited from the
Channel class. */
if( encoding == NATIVE_ENCODING ){
new = (*parent_read)( this_channel, status );
/* Indicate that used cards should be removed from the FitsChan. */
remove = 1;
/* If we are reading from a FitsChan in which AST objects are encoded using
any of the other supported encodings, the header may only contain a
single FrameSet. */
} else {
remove = 0;
/* Only proceed if the FitsChan is at start-of-file. */
if( !astTestCard( this ) && astOK ){
/* Extract the required information from the FITS header into a standard
intermediary structure called a FitsStore. */
store = FitsToStore( this, encoding, method, class, status );
/* Now create a FrameSet from this FitsStore. */
new = FsetFromStore( this, store, method, class, status );
/* Release the resources used by the FitsStore. */
store = FreeStore( store, status );
/* Indicate that used cards should be retained in the FitsChan. */
remove = 0;
/* If no object is being returned, rewind the fitschan in order to
re-instate the original current Card. */
if( !new ) {
astClearCard( this );
/* Otherwise, ensure the current card is at "end-of-file". */
} else {
astSetCard( this, INT_MAX );
}
}
}
/* If an error occurred, clean up by deleting the new Object and
return a NULL pointer. */
if ( !astOK ) new = astDelete( new );
/* If no object is being returned, clear the "provisionally used" flags
associated with cards which were read. We do not do this if the user
wants to clean WCS cards from the FitsChan even if an error occurs. */
if( !new && !astGetClean( this ) ) {
FixUsed( this, 0, 0, 0, method, class, status );
/* Otherwise, indicate that all the "provisionally used" cards have been
"definitely used". If native encoding was used, these cards are
totally removed from the FitsChan. */
} else {
FixUsed( this, 0, 1, remove, method, class, status );
}
/* Return the pointer to the new Object. */
return new;
}
static double *ReadCrval( AstFitsChan *this, AstFrame *wcsfrm, char s,
const char *method, const char *class, int *status ){
/*
* Name:
* ReadCrval
* Purpose:
* Obtain the reference point from the supplied FitsChan in the
* supplied WCS Frame.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* double *ReadCrval( AstFitsChan *this, AstFrame *wcsfrm, char s,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* The original reference point in the "s" coordinate description is read
* from the CRVAL keywords in the supplied FitsChan, and the original
* FrameSet is re-read from the FitsChan. If possible, the reference
* position is then converted from the "s" coordinate description to the
* supplied WCS Frame, and a pointer to an array holding the axis
* values for the transformed reference point is returned.
* Parameters:
* this
* The FitsChan.
* wcsfrm
* The WCS Frame in the FitsChan being written to.
* s
* The co-ordinate version character. A space means the primary
* axis descriptions. Otherwise the supplied character should be
* an upper case alphabetical character ('A' to 'Z').
* 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:
* A pointer to a dynamically allocated array holding the reference
* point in the supplied WCS Frame. NULL is returned if is is not
* possible to determine the reference point for any reason (for
* instance, if the FitsChan does not contain values for the CRVAL
* keywords).
*/
/* Local Variables: */
AstFitsChan *fc; /* A copy of the supplied FitsChan */
AstFrame *tfrm; /* Temporary Frame pointer */
AstFrameSet *fs; /* The FITS FrameSet */
AstFrameSet *tfs; /* FrameSet connecting FITS and supplied WCS Frame */
const char *id; /* Pointer to Object "Id" string */
char buf[ 11 ]; /* FITS keyword template buffer */
double *crval; /* CRVAL keyword values in supplied FitsChan */
double *ret; /* Returned array */
int hii; /* Highest found FITS axis index */
int iax; /* Axis index (zero based) */
int ifr; /* Frames index */
int loi; /* Lowest found FITS axis index */
int nax; /* Axis count */
int nfr; /* No. of Frames in FITS FrameSet */
int ok; /* Were CRVAL values found? */
/* Initialise */
ret = NULL;
/* Check the inherited status. */
if( !astOK ) return ret;
/* We want to re-create the original FrameSet represented by the original
contents of the supplied FitsChan. Some of the contents of the
FitsChan will already have been marked as "having been read" and so
will be ignored if we attempt to read a FrameSet directly from the
supplied FitsChan. Therefore we take a deep copy of the supplied
FitsChan and clear all the "previusly read" flags in the copy. */
fc = astCopy( this );
astClearEncoding( fc );
FixUsed( fc, 1, 0, 0, method, class, status );
/* Copy the CRVAL values for the "s" axis descriptions into a dynamically
allocated array ("crval"). */
if( s == ' ' ) {
strcpy( buf, "CRVAL%d" );
} else {
sprintf( buf, "CRVAL%%d%c", s );
}
if( astKeyFields( fc, buf, 1, &hii, &loi ) > 0 ) {
crval = astMalloc( sizeof( double )*(size_t) hii );
ok = 1;
for( iax = 0; iax < hii; iax++ ){
ok = ok && GetValue( fc, FormatKey( "CRVAL", iax + 1, -1, s, status ),
AST__FLOAT, (void *) (crval + iax), 0, 0, method,
class, status );
}
} else {
crval = NULL;
ok = 0;
}
/* If the CRVAL values were obtained succesfully, attempt to read a FrameSet
from the FitsChan copy. Do it in a new error report context so that we
can annull any error when the FrameSet is read. */
if( ok ) {
int oldreporting = astReporting( 0 );
astClearCard( fc );
fs = astRead( fc );
if( fs ) {
/* We want to find a conversion from the Frame in this FrameSet which
represents the FITS-WCS "s" coordinate descriptions and the supplied WCS
Frame. So first find the Frame which has its Ident attribute set to
"s" and make it the current Frame. */
nfr = astGetNframe( fs );
for( ifr = 1; ifr <= nfr; ifr++ ) {
astSetCurrent( fs, ifr );
tfrm = astGetFrame( fs, ifr );
id = astTestIdent( tfrm ) ? astGetIdent( tfrm ) : NULL;
tfrm = astAnnul( tfrm );
if( id && strlen( id ) == 1 && id[ 0 ] == s ) break;
}
/* Check a Frame was found, and that we have CRVAL values for all axes in
the Frame. */
if( ifr <= nfr && astGetNaxes( fs ) == hii ) {
/* Attempt to find a conversion route from the Frame found above to the
supplied WCS Frame. */
tfs = astConvert( fs, wcsfrm, astGetDomain( wcsfrm ) );
if( tfs ) {
/* Allocate memory to hold the returned reference point. */
nax = astGetNaxes( wcsfrm );
ret = astMalloc( sizeof( double )*(size_t) nax );
/* Transform the original reference position from the "s" Frame to the
supplied WCS Frame using the Mapping returned by astConvert. */
astTranN( tfs, 1, hii, 1, crval, 1, nax, 1, ret );
/* Free resources. */
tfs = astAnnul( tfs );
}
}
/* Free resources. */
fs = astAnnul( fs );
/* Annul any error that occurred reading the FitsChan. */
} else if( !astOK ) {
astClearStatus;
}
/* Re-instate error reporting. */
astReporting( oldreporting );
}
/* Free resources. */
if( crval ) crval = astFree( crval );
fc = astAnnul( fc );
/* If an error occurred, free the returned array. */
if( !astOK ) ret = astFree( ret );
/* Return the result. */
return ret;
}
static void ReadFits( AstFitsChan *this, int *status ){
/*
*++
* Name:
c astReadFits
f AST_READFITS
* Purpose:
* Read cards into a FitsChan from the source function.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c void astReadFits( AstFitsChan *this )
f CALL AST_READFITS( THIS, STATUS )
* Class Membership:
* FitsChan method.
* Description:
c This function
f This routine
* reads cards from the source function that was specified when the
* FitsChan was created, and stores them in the FitsChan. This
* normally happens once-only, when the FitsChan is accessed for the
* first time.
c This function
f This routine
* provides a means of forcing a re-read of the external source, and
* may be useful if (say) new cards have been deposited into the
* external source. Any newcards read from the source are appended to
* the end of the current contents of the FitsChan.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Notes:
* - This function returns without action if no source function was
* specified when the FitsChan was created.
* - The SourceFile attribute is ignored by this
c function.
f routine.
* New cards are read from the source file whenever a new value is
* assigned to the SourceFile attribute.
*--
*/
/* Check the inherited status */
if( !astOK ) return;
/* If no source function is available, re-instate any saved source
function pointer. */
if( !this->source ) {
this->source = this->saved_source;
this->saved_source = NULL;
}
/* Call the source function. */
ReadFromSource( this, status );
}
static void ReadFromSource( AstFitsChan *this, int *status ){
/*
* Name:
* ReadFromSource
* Purpose:
* Fill the FitsChan by reading cards from the source function.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void ReadFromSource( AstFitsChan *this, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* The source function specified when the FitsChan was created is
* called repeatedly until it returns a NULL pointer. The string
* returned by each such call is assumed to be a FITS header card,
* and is stored in the FitsChan using astPutFits.
*
* If no source function was provided, the FitsChan is left as supplied.
* This is different to a standard Channel, which tries to read data
* from standard input if no source function is provided.
*
* This function should be called at the start of most public or protected
* FitsChan functions, and most private functions that are used to override
* methods inherited form the Channel class. Previously, this function
* was called only once, from the FitsChan initialiser (astInitFitschan).
* However, calling it from astInitFitsChan means that application code
* cannot use the astPutChannelData function with a FitsChan, since the
* source function would already have been called by the time the
* FitsChan constructor returned (and thus before astPutChannelData
* could have been called). In order to ensure that the source
* function is called only once, this function now nullifies the source
* function pointer after its first use.
* Parameters:
* this
* Pointer to the FitsChan.
* status
* Pointer to the inherited status variable.
* Notes:
* - The new cards are appended to the end of the FitsChan.
* - The first of the new cards is made the current card on exit. If no
* source function is supplied, the current card is left unchanged.
*/
/* Local Variables: */
const char *(* source)( void ); /* Pointer to source function */
const char *card; /* Pointer to externally-read header card */
int icard; /* Current card index on entry */
/* Check the global status. */
if( !astOK || !this ) return;
/* Only proceed if source function and wrapper were supplied when the FitsChan
was created and are still available. */
if( this->source && this->source_wrap ){
/* Save the source function pointer and then nullify the pointer in the
FitsChan structure. This avoids infinte loops. */
source = this->source;
this->source = NULL;
/* Save the source fubnction pointer in the FitsChan so that it can be
re-instated if required (e.g. by astReadFits). */
this->saved_source = source;
/* Ensure the FitsChan is at end-of-file. This will result in the
new cards being appended to the end of the FitsChan. */
astSetCard( this, INT_MAX );
/* Store the current card index. */
icard = astGetCard( this );
/* Obtain the first header card from the source function. This is an
externally supplied function which may not be thread-safe, so lock a
mutex first. Also store the channel data pointer in a global variable
so that it can be accessed in the source function using macro
astChannelData. */
astStoreChannelData( this );
LOCK_MUTEX2;
card = ( *this->source_wrap )( source, status );
UNLOCK_MUTEX2;
/* Loop until a NULL pointer is returned by the source function, or an
error occurs. */
while( card && astOK ){
/* Store the card in the FitsChan. */
astPutFits( this, card, 0 );
/* Free the memory holding the header card. */
card = (char *) astFree( (void *) card );
/* Obtain the next header card. Also store the channel data pointer in a
global variable so that it can be accessed in the source function using
macro astChannelData. */
astStoreChannelData( this );
LOCK_MUTEX2;
card = ( *this->source_wrap )( source, status );
UNLOCK_MUTEX2;
}
/* Set the current card index so that the first of the new cards will be the
next card to be read from the FitsChan. */
astSetCard( this, icard );
}
}
static void RemoveTables( AstFitsChan *this, const char *key, int *status ){
/*
*++
* Name:
c astRemoveTables
f AST_REMOVETABLES
* Purpose:
* Remove one or more tables from a FitsChan.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c void astRemoveTables( AstFitsChan *this, const char *key )
f CALL AST_REMOVETABLES( THIS, KEY, STATUS )
* Class Membership:
* FitsChan method.
* Description:
c This function
f This routine
* removes the named tables from the FitsChan, it they exist (no error
* is reported if any the tables do not exist).
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
c key
f KEY = CHARACTER * ( * ) (Given)
* The key indicating which tables to exist. A single key or a
* comma-separated list of keys can be supplied. If a blank string
* is supplied, all tables are removed.
f STATUS = INTEGER (Given and Returned)
f The global status.
*--
*/
/* Local variables: */
char **words;
int itable;
int ntable;
/* Return if the global error status has been set, or the FitsChan
contains no tables KeyMap. */
if( !astOK || !this->tables ) return;
/* If the string is blank, remove all tables. */
if( astChrLen( key ) == 0 ) {
ntable = astMapSize( this->tables );
for( itable = 0; itable < ntable; itable++ ) {
astMapRemove( this->tables, astMapKey( this->tables, itable ) );
}
/* Otherwise, split the supplied comma-separated string up into individual
items. */
} else {
words = astChrSplitC( key, ',', &ntable );
/* Attempt to remove each one, and then free the string. */
if( astOK ) {
for( itable = 0; itable < ntable; itable++ ) {
astMapRemove( this->tables, words[ itable ] );
words[ itable ] = astFree( words[ itable ] );
}
}
/* Free the list. */
words = astFree( words );
}
}
static void RetainFits( AstFitsChan *this, int *status ){
/*
*++
* Name:
c astRetainFits
f AST_RETAINFITS
* Purpose:
* Indicate that the current card in a FitsChan should be retained.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c void astRetainFits( AstFitsChan *this )
f CALL AST_RETAINFITS( THIS, STATUS )
* Class Membership:
* FitsChan method.
* Description:
c This function
f This routine
* stores a flag with the current card in the FitsChan indicating that
* the card should not be removed from the FitsChan when an Object is
* read from the FitsChan using
c astRead.
f AST_READ.
*
* Cards that have not been flagged in this way are removed when a
* read operation completes succesfully, but only if the card was used
* in the process of creating the returned AST Object. Any cards that
* are irrelevant to the creation of the AST Object are retained whether
* or not they are flagged.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Notes:
* - This function returns without action if the FitsChan is
* initially positioned at the "end-of-file" (i.e. if the Card
* attribute exceeds the number of cards in the FitsChan).
* - The current card is not changed by this function.
*--
*/
/* Local variables: */
int flags;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Return if the global error status has been set, or the current card
is not defined. */
if( !astOK || !this->card ) return;
/* Set the PROTECTED flag in the current card. */
flags = ( (FitsCard *) this->card )->flags;
( (FitsCard *) this->card )->flags = flags | PROTECTED;
}
static void RoundFString( char *text, int width, int *status ){
/*
* Name:
* RoundString
* Purpose:
* Modify a formatted floating point number to round out long
* sequences of zeros or nines.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void RoundFString( char *text, int width )
* Class Membership:
* FitsChan member function.
* Description:
* The supplied string is assumed to be a valid decimal representation of
* a floating point number. It is searched for sub-strings consisting
* of NSEQ or more adjacent zeros, or NSEQ or more adjacent nines. If found
* the string is modified to represent the result of rounding the
* number to remove the sequence of zeros or nines.
* Parameters:
* text
* The formatted number. Modified on exit to round out long
* sequences of zeros or nines. The returned string is right justified.
* width
* The minimum field width to use. The value is right justified in
* this field width. Ignored if zero.
*/
/* Local Constants: */
#define NSEQ 4 /* No. of adjacent 0's or 9's to produce rounding */
/* Local Variables: */
char *a;
char *c;
char *dot;
char *exp;
char *last;
char *start;
char *end;
int i;
int neg;
int nnine;
int nonzero;
int nzero;
int replace;
int started;
int len;
int bu;
int nls;
/* Check the inherited status. */
if( !astOK ) return;
/* Save the original length of the text. */
len = strlen( text );
/* Locate the start of any exponent string. */
exp = strpbrk( text, "dDeE" );
/* First check for long strings of adjacent zeros.
=============================================== */
/* Indicate that we have not yet found a decimal point in the string. */
dot = NULL;
/* The "started" flag controls whether *leading* zeros should be removed
if there are more than NSEQ of them. They are only removed if there is an
exponent. */
started = ( exp != NULL );
/* We are not currently replacing digits with zeros. */
replace = 0;
/* We have not yet found any adjacent zeros. */
nzero = 0;
/* We have no evidence yet that the number is non-zero. */
nonzero = 0;
/* Loop round the supplied text string. */
c = text;
while( *c && c != exp ){
/* If this is a zero, increment the number of adjacent zeros found, so
long as we have previously found a non-zero digit (or there is an
exponent). If this is the NSEQ'th adjacent zero, indicate that
subsequent digits should be replaced by zeros. */
if( *c == '0' ){
if( started && ++nzero >= NSEQ ) replace = 1;
/* Note if the number contains a decimal point. */
} else if( *c == '.' ){
dot = c;
/* If this character is a non-zero digit, indicate that we have found a
non-zero digit. If we have previously found a long string of adjacent
zeros, replace the digit by '0'. Otherwise, reset the count of
adjacent zeros, and indicate the final number is non-zero. */
} else if( *c != ' ' && *c != '+' && *c != '-' ){
started = 1;
if( replace ) {
*c = '0';
} else {
nzero = 0;
nonzero = 1;
}
}
/* Move on to the next character. */
c++;
}
/* If the final number is zero, just return the most simple decimal zero
value. */
if( !nonzero ) {
strcpy( text, "0.0" );
/* Otherwise, we remove any trailing zeros which occur to the right of a
decimal point. */
} else if( dot ) {
/* Find the last non-zero digit. */
while( c-- > text && *c == '0' );
/* If any trailing zeros were found... */
if( c > text ) {
/* Retain one trailing zero after a decimal point. */
if( *c == '.' ) c++;
/* We put a terminator following the last non-zero character. The
terminator is the exponent, if there was one, or a null character.
Remember to update the pointer to the start of the exponent. */
c++;
if( exp ) {
a = exp;
exp = c;
while( ( *(c++) = *(a++) ) );
} else {
*c = 0;
}
}
}
/* Next check for long strings of adjacent nines.
============================================= */
/* We have not yet found any adjacent nines. */
nnine = 0;
/* We have not yet found a non-nine digit. */
a = NULL;
/* We have not yet found a non-blank character */
start = NULL;
last = NULL;
/* Number is assumed positive. */
neg = 0;
/* Indicate that we have not yet found a decimal point in the string. */
dot = NULL;
/* Loop round the supplied text string. */
c = text;
while( *c && c != exp ){
/* Note the address of the first non-blank character. */
if( !start && *c != ' ' ) start = c;
/* If this is a nine, increment the number of adjacent nines found. */
if( *c == '9' ){
++nnine;
/* Note if the number contains a decimal point. */
} else if( *c == '.' ){
dot = c;
/* Note if the number is negative. */
} else if( *c == '-' ){
neg = 1;
/* If this character is a non-nine digit, and we have not had a long
sequence of 9's, reset the count of adjacent nines, and update a pointer
to "the last non-nine digit prior to a long string of nines". */
} else if( *c != ' ' && *c != '+' ){
if( nnine < NSEQ ) {
nnine = 0;
a = c;
}
}
/* Note the address of the last non-blank character. */
if( *c != ' ' ) last = c;
/* Move on to the next character. */
c++;
}
/* If a long string of adjacent nines was found... */
if( nnine >= NSEQ ) {
c = NULL;
/* If we found at least one non-nine digit. */
if( a ) {
/* "a" points to the last non-nine digit before the first of the group of 9's.
Increment this digit by 1. Since we know the digit is not a nine, there
is no danger of a carry. */
*a = *a + 1;
/* Fill with zeros up to the decimal point, or to the end if there is no
decimal point. */
c = a + 1;
if( dot ) {
while( c < dot ) *(c++) = '0';
} else {
while( *c ) *(c++) = '0';
}
/* Now make "c" point to the first character for the terminator. This is
usually the character following the last non-nine digit. However, if
the last non-nine digit appears immediately before a decimal point, then
we append ".0" to the string before appending the terminator. */
if( *c == '.' ) {
*(++c) = '0';
c++;
}
/* If all digits were nines, the rounded number will occupy one more
character than the supplied number. We can only do the rounding if there
is a spare character (i.e.a space) in the supplied string. */
} else if( last - start + 1 < len ) {
/* Put the modified text at the left of the available space. */
c = text;
/* Start with a minus sing if needed, followed by the leading "1" (caused
by the overflow from the long string of 9's). */
if( neg ) *(c++) = '-';
*(c++) = '1';
/* Now find the number of zeros to place after the leading "1". This is
the number of characters in front of the terminator marking the end of
the integer part of the number. */
if( dot ) {
nzero = dot - start;
} else if( exp ) {
nzero = exp - start;
} else {
nzero = last - start;
}
/* If the number is negative, the above count will include the leading
minus sign, which is not a digit. So reduce the count by one. */
if( neg ) nzero--;
/* Now put in the correct number of zeros. */
for( i = 0; i < nzero; i++ ) *(c++) = '0';
/* If the original string containsed a decimal point, make sure the
returned string also contains one. */
if( dot ) {
*(c++) = '.';
if( *c ) *(c++) = '0';
}
}
/* We put a terminator following the last non-zero character. The
terminator is the exponent, if there was one, or a null character. */
if( c ) {
if( exp ) {
while( ( *(c++) = *(exp++) ) );
} else {
*c = 0;
}
}
}
/* Right justify the returned string in the original field width. */
end = text + len;
c = text + strlen( text );
if( c != end ) {
while( c >= text ) *(end--) = *(c--);
while( end >= text ) *(end--) = ' ';
}
/* If a minimum field width was given, shunt the text to the left in
order to reduce the used field width to the specified value. This
requires there to be some leading spaces (because we do not want to
loose any non-blank characters from the left hand end of the string).
If there are insufficient leading spaces to allow the field width to
be reduced to the specified value, then reduce the field width as far
as possible. First find the number of spaces we would like to remove
from the front of the string (in order to reduce the used width to the
specified value). */
bu = len - width;
/* If we need to remove any leading spaces... */
if( width > 0 && bu > 0 ) {
/* Find the number of leading spaces which are available to be removed. */
c = text - 1;
while( *(++c) == ' ' );
nls = c - text;
/* If there are insufficient leading spaces, just use however many there
are. */
if( bu > nls ) bu = nls;
/* Shift the string. */
c = text;
a = c + bu;
while( ( *(c++) = *(a++) ) );
}
/* Undefine local constants. */
#undef NSEQ
}
static int SAOTrans( AstFitsChan *this, AstFitsChan *out, const char *method,
const char *class, int *status ){
/*
* Name:
* SAOTrans
* Purpose:
* Translate an SAO encoded header into a TPN encoded header.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int SAOTrans( AstFitsChan *this, AstFitsChan *out, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Search "this" for keywords that give a description of a distorted
* TAN projection using the SAO representation and, if found, write
* keywords to "out" that describe an equivalent projection using TPN
* representation. The definition of the SAO polynomial is taken from
* the platepos.c file included in Doug Mink's WCSTools.
* Parameters:
* this
* Pointer to the FitsChan to read.
* out
* Pointer to a FitsCHan in which to store translated keywords.
* 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:
* Non-zero if "this" contained an SAO encoded header. Zero otherwise.
*/
#define NC 13
/* Local Variables: */
char keyname[10];
double co[ 2 ][ NC ];
double pv;
int i;
int is_sao;
int m;
int ok;
int result;
/* Initialise */
result = 0;
/* Check the inherited status. */
if( !astOK ) return result;
/* Check there are exactly two CTYPE keywords in the header. */
if( 2 == astKeyFields( this, "CTYPE%d", 0, NULL, NULL ) ){
/* Initialise all cooefficients. */
memset( co, 0, sizeof( co ) );
/* Get the required SAO keywords. */
is_sao = 1;
ok = 1;
for( i = 0; i < 2 && ok && is_sao; i++ ) {
ok = 0;
for( m = 0; m < NC; m++ ) {
/* Get the value of the next "COi_j" keyword. If any of the first 3 values
are missing on either axis, we assume this is not an SAO header. */
sprintf( keyname, "CO%d_%d", i + 1, m + 1 );
if( !GetValue( this, keyname, AST__FLOAT, &co[ i ][ m ], 0, 1, method,
class, status ) ) {
if( m < 3 ) is_sao = 0;
break;
}
/* Check that we have at least one non-zero coefficient (excluding the
first constant term ). */
if( co[ i ][ m ] != 0.0 && m > 0 ) ok = 1;
}
}
/* If this is an SAO header.. */
if( is_sao ) {
/* Issue a warning if all coefficients for this axis are zero. */
if( !ok ) {
Warn( this, "badpv", "This FITS header describes an SAO encoded "
"distorted TAN projection, but all the distortion "
"coefficients for at least one axis are zero.", method, class,
status );
/* Otherwise, calculate and store the equivalent PV projection parameters. */
} else {
pv = co[ 0 ][ 0 ];
if( pv != AST__BAD ) SetValue( out, "PV1_0", &pv,
AST__FLOAT, NULL, status );
pv = co[ 0 ][ 1 ];
if( pv != AST__BAD ) SetValue( out, "PV1_1", &pv,
AST__FLOAT, NULL, status );
pv = co[ 0 ][ 2 ];
if( pv != AST__BAD ) SetValue( out, "PV1_2", &pv,
AST__FLOAT, NULL, status );
pv = 0.0;
if( co[ 0 ][ 3 ] != AST__BAD ) pv += co[ 0 ][ 3 ];
if( co[ 0 ][ 10 ] != AST__BAD ) pv += co[ 0 ][ 10 ];
if( pv != AST__BAD ) SetValue( out, "PV1_4", &pv,
AST__FLOAT, NULL, status );
pv = co[ 0 ][ 5 ];
if( pv != AST__BAD ) SetValue( out, "PV1_5", &pv,
AST__FLOAT, NULL, status );
pv = 0.0;
if( co[ 0 ][ 4 ] != AST__BAD ) pv += co[ 0 ][ 4 ];
if( co[ 0 ][ 10 ] != AST__BAD ) pv += co[ 0 ][ 10 ];
if( pv != AST__BAD ) SetValue( out, "PV1_6", &pv,
AST__FLOAT, NULL, status );
pv = 0.0;
if( co[ 0 ][ 6 ] != AST__BAD ) pv += co[ 0 ][ 6 ];
if( co[ 0 ][ 11 ] != AST__BAD ) pv += co[ 0 ][ 11 ];
if( pv != AST__BAD ) SetValue( out, "PV1_7", &pv,
AST__FLOAT, NULL, status );
pv = 0.0;
if( co[ 0 ][ 8 ] != AST__BAD ) pv += co[ 0 ][ 8 ];
if( co[ 0 ][ 12 ] != AST__BAD ) pv += co[ 0 ][ 12 ];
if( pv != AST__BAD ) SetValue( out, "PV1_8", &pv,
AST__FLOAT, NULL, status );
pv = 0.0;
if( co[ 0 ][ 9 ] != AST__BAD ) pv += co[ 0 ][ 9 ];
if( co[ 0 ][ 11 ] != AST__BAD ) pv += co[ 0 ][ 11 ];
if( pv != AST__BAD ) SetValue( out, "PV1_9", &pv,
AST__FLOAT, NULL, status );
pv = 0.0;
if( co[ 0 ][ 7 ] != AST__BAD ) pv += co[ 0 ][ 7 ];
if( co[ 0 ][ 12 ] != AST__BAD ) pv += co[ 0 ][ 12 ];
if( pv != AST__BAD ) SetValue( out, "PV1_10", &pv,
AST__FLOAT, NULL, status );
pv = co[ 1 ][ 0 ];
if( pv != AST__BAD ) SetValue( out, "PV2_0", &pv,
AST__FLOAT, NULL, status );
pv = co[ 1 ][ 2 ];
if( pv != AST__BAD ) SetValue( out, "PV2_1", &pv,
AST__FLOAT, NULL, status );
pv = co[ 1 ][ 1 ];
if( pv != AST__BAD ) SetValue( out, "PV2_2", &pv,
AST__FLOAT, NULL, status );
pv = 0.0;
if( co[ 1 ][ 4 ] != AST__BAD ) pv += co[ 1 ][ 4 ];
if( co[ 1 ][ 10 ] != AST__BAD ) pv += co[ 1 ][ 10 ];
if( pv != AST__BAD ) SetValue( out, "PV2_4", &pv,
AST__FLOAT, NULL, status );
pv = co[ 1 ][ 5 ];
if( pv != AST__BAD ) SetValue( out, "PV2_5", &pv,
AST__FLOAT, NULL, status );
pv = 0.0;
if( co[ 1 ][ 3 ] != AST__BAD ) pv += co[ 1 ][ 3 ];
if( co[ 1 ][ 10 ] != AST__BAD ) pv += co[ 1 ][ 10 ];
if( pv != AST__BAD ) SetValue( out, "PV2_6", &pv,
AST__FLOAT, NULL, status );
pv = 0.0;
if( co[ 1 ][ 7 ] != AST__BAD ) pv += co[ 1 ][ 7 ];
if( co[ 1 ][ 12 ] != AST__BAD ) pv += co[ 1 ][ 12 ];
if( pv != AST__BAD ) SetValue( out, "PV2_7", &pv,
AST__FLOAT, NULL, status );
pv = 0.0;
if( co[ 1 ][ 9 ] != AST__BAD ) pv += co[ 1 ][ 9 ];
if( co[ 1 ][ 11 ] != AST__BAD ) pv += co[ 1 ][ 11 ];
if( pv != AST__BAD ) SetValue( out, "PV2_8", &pv,
AST__FLOAT, NULL, status );
pv = 0.0;
if( co[ 1 ][ 8 ] != AST__BAD ) pv += co[ 1 ][ 8 ];
if( co[ 1 ][ 12 ] != AST__BAD ) pv += co[ 1 ][ 12 ];
if( pv != AST__BAD ) SetValue( out, "PV2_9", &pv,
AST__FLOAT, NULL, status );
pv = 0.0;
if( co[ 1 ][ 6 ] != AST__BAD ) pv += co[ 1 ][ 6 ];
if( co[ 1 ][ 11 ] != AST__BAD ) pv += co[ 1 ][ 11 ];
if( pv != AST__BAD ) SetValue( out, "PV2_10", &pv,
AST__FLOAT, NULL, status );
/* From an example header provided by Bill Joye, it seems that the SAO
polynomial includes the rotation and scaling effects of the CD matrix.
Therefore we mark as read all CDi_j, CDELT and CROTA values. Without
this, the rotation and scaling would be applied twice. First, mark the
original values as having been used, no matter which FitsChan they are
in. */
GetValue( this, "CD1_1", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( this, "CD1_2", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( this, "CD2_1", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( this, "CD2_2", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( this, "PC1_1", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( this, "PC1_2", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( this, "PC2_1", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( this, "PC2_2", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( this, "CDELT1", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( this, "CDELT2", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( this, "CROTA1", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( this, "CROTA2", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( out, "CD1_1", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( out, "CD1_2", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( out, "CD2_1", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( out, "CD2_2", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( out, "PC1_1", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( out, "PC1_2", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( out, "PC2_1", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( out, "PC2_2", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( out, "CDELT1", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( out, "CDELT2", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( out, "CROTA1", AST__FLOAT, &pv, 0, 1, method, class, status );
GetValue( out, "CROTA2", AST__FLOAT, &pv, 0, 1, method, class, status );
/* Now store new default values in the returned FitsChan. */
pv = 1.0;
SetValue( out, "PC1_1", &pv, AST__FLOAT, NULL,
status );
SetValue( out, "PC2_2", &pv, AST__FLOAT, NULL,
status );
SetValue( out, "CDELT1", &pv, AST__FLOAT, NULL,
status );
SetValue( out, "CDELT2", &pv, AST__FLOAT, NULL,
status );
pv = 0.0;
SetValue( out, "PC1_2", &pv, AST__FLOAT, NULL,
status );
SetValue( out, "PC2_1", &pv, AST__FLOAT, NULL,
status );
/* Indicate we have converted an SAO header. */
result = 1;
}
}
}
/* Return a flag indicating if an SAO header was found. */
return result;
}
#undef NC
static int SearchCard( AstFitsChan *this, const char *name,
const char *method, const char *class, int *status ){
/*
* Name:
* SearchCard
* Purpose:
* Search the whole FitsChan for a card refering to given keyword.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int SearchCard( AstFitsChan *this, const char *name,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* Searches the whole FitsChan for a card refering to the supplied keyword,
* and makes it the current card. The card following the current card is
* checked first. If this is not the required card, then a search is
* performed starting with the first keyword in the FitsChan.
* Parameters:
* this
* Pointer to the FitsChan.
* name
* Pointer to a string holding the keyword name.
* method
* Pointer to string holding name of calling method.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A value of 1 is returned if a card was found refering to the given
* keyword. Otherwise zero is returned.
* Notes:
* - If a NULL pointer is supplied for "name" then the current card
* is left unchanged.
* - The current card is set to NULL (end-of-file) if no card can be
* found for the supplied keyword.
*/
/* Local Variables: */
int ret; /* Was a card found? */
/* Check the global status, and supplied keyword name. */
if( !astOK || !name ) return 0;
/* Indicate that no card has been found yet. */
ret = 0;
/* The required card is very often the next card in the FitsChan, so check the
next card, and only search the entire FitsChan if the check fails. */
MoveCard( this, 1, method, class, status );
if( !astFitsEof( this ) &&
!Ustrncmp( CardName( this, status ), name, FITSNAMLEN, status ) ){
ret = 1;
/* If the next card is not the required card, rewind the FitsChan back to
the first card. */
} else {
astClearCard( this );
/* Attempt to find the supplied keyword, searching from the first card. */
ret = FindKeyCard( this, name, method, class, status );
}
/* Return. */
return ret;
}
static void SetAlgCode( char *buf, const char *algcode, int *status ){
/*
* Name:
* SetAlgCode
* Purpose:
* Create a non-linear CTYPE string from a system code and an algorithm
* code.
* Type:
* Private function.
* Synopsis:
* void SetAlgCode( char *buf, const char *algcode, int *status )
* Class Membership:
* FitsChan
* Description:
* FITS-WCS paper 1 says that non-linear axes must have a CTYPE of the
* form "4-3" (e.g. "VRAD-TAB"). This function handles the truncation
* of long system codes, or the padding of short system codes.
* Parameters:
* buf
* A buffer in which is stored the system code. Modified on exit to
* hold the combined CTYPE value. It should have a length long
* enough to hold the system code and the algorithm code.
* algcode
* Pointer to a string holding the algorithm code (with a leading
* "-", e.g. "-TAB").
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
int nc;
/* Check inherited status */
if( !astOK ) return;
/* Pad the supplied string to at least 4 characters using "-" characters. */
nc = strlen( buf );
while( nc < 4 ) buf[ nc++ ] = '-';
/* Insert the null-terminated code at position 4. */
strcpy( buf + 4, algcode );
}
static void SetAttrib( AstObject *this_object, const char *setting, int *status ) {
/*
* Name:
* SetAttrib
* Purpose:
* Set an attribute value for a FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void SetAttrib( AstObject *this, const char *setting )
* Class Membership:
* FitsChan member function (over-rides the astSetAttrib protected
* method inherited from the Channel class).
* Description:
* This function assigns an attribute value for a FitsChan, 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 FitsChan.
* setting
* Pointer to a null-terminated string specifying the new attribute
* value.
*/
/* Local Variables: */
AstFitsChan *this; /* Pointer to the FitsChan structure */
const char *class; /* Object class */
int ival; /* Integer attribute value */
int len; /* Length of setting string */
int nc; /* Number of characters read by astSscanf */
int offset; /* Offset of attribute string */
int warn; /* Offset of Warnings string */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_object;
/* Obtain the length of the setting string. */
len = (int) strlen( setting );
/* Obtain the object class. */
class = astGetClass( this );
/* Card. */
/* ----- */
if ( nc = 0,
( 1 == astSscanf( setting, "card= %d %n", &ival, &nc ) )
&& ( nc >= len ) ) {
astSetCard( this, ival );
/* Encoding. */
/* --------- */
} else if( nc = 0,
( 0 == astSscanf( setting, "encoding=%n%*[^\n]%n", &ival, &nc ) )
&& ( nc >= len ) ) {
nc = ChrLen( setting + ival, status );
if( !Ustrncmp( setting + ival, NATIVE_STRING, nc, status ) ){
astSetEncoding( this, NATIVE_ENCODING );
} else if( !Ustrncmp( setting + ival, FITSPC_STRING, nc, status ) ){
astSetEncoding( this, FITSPC_ENCODING );
} else if( !Ustrncmp( setting + ival, FITSPC_STRING2, nc, status ) ){
astSetEncoding( this, FITSPC_ENCODING );
} else if( !Ustrncmp( setting + ival, FITSWCS_STRING, nc, status ) ){
astSetEncoding( this, FITSWCS_ENCODING );
} else if( !Ustrncmp( setting + ival, FITSWCS_STRING2, nc, status ) ){
astSetEncoding( this, FITSWCS_ENCODING );
} else if( !Ustrncmp( setting + ival, FITSIRAF_STRING, nc, status ) ){
astSetEncoding( this, FITSIRAF_ENCODING );
} else if( !Ustrncmp( setting + ival, FITSIRAF_STRING2, nc, status ) ){
astSetEncoding( this, FITSIRAF_ENCODING );
} else if( !Ustrncmp( setting + ival, FITSAIPS_STRING, nc, status ) ){
astSetEncoding( this, FITSAIPS_ENCODING );
} else if( !Ustrncmp( setting + ival, FITSAIPS_STRING2, nc, status ) ){
astSetEncoding( this, FITSAIPS_ENCODING );
} else if( !Ustrncmp( setting + ival, FITSAIPSPP_STRING, nc, status ) ){
astSetEncoding( this, FITSAIPSPP_ENCODING );
} else if( !Ustrncmp( setting + ival, FITSAIPSPP_STRING2, nc, status ) ){
astSetEncoding( this, FITSAIPSPP_ENCODING );
} else if( !Ustrncmp( setting + ival, FITSCLASS_STRING, nc, status ) ){
astSetEncoding( this, FITSCLASS_ENCODING );
} else if( !Ustrncmp( setting + ival, FITSCLASS_STRING2, nc, status ) ){
astSetEncoding( this, FITSCLASS_ENCODING );
} else if( !Ustrncmp( setting + ival, DSS_STRING, nc, status ) ){
astSetEncoding( this, DSS_ENCODING );
} else {
astError( AST__BADAT, "astSet(%s): Unknown encoding system '%s' "
"requested for a %s.", status, class, setting + ival, class );
}
/* FitsDigits. */
/* ----------- */
} else if ( nc = 0,
( 1 == astSscanf( setting, "fitsdigits= %d %n", &ival, &nc ) )
&& ( nc >= len ) ) {
astSetFitsDigits( this, ival );
/* FitsAxisOrder. */
/* -------------- */
} else if ( nc = 0,
( 0 == astSscanf( setting, "fitsaxisorder=%n%*[^\n]%n",
&offset, &nc ) )
&& ( nc >= len ) ) {
astSetFitsAxisOrder( this, setting + offset );
/* CDMatrix */
/* -------- */
} else if ( nc = 0,
( 1 == astSscanf( setting, "cdmatrix= %d %n", &ival, &nc ) )
&& ( nc >= len ) ) {
astSetCDMatrix( this, ival );
/* DefB1950 */
/* -------- */
} else if ( nc = 0,
( 1 == astSscanf( setting, "defb1950= %d %n", &ival, &nc ) )
&& ( nc >= len ) ) {
astSetDefB1950( this, ival );
/* TabOK */
/* ----- */
} else if ( nc = 0,
( 1 == astSscanf( setting, "tabok= %d %n", &ival, &nc ) )
&& ( nc >= len ) ) {
astSetTabOK( this, ival );
/* CarLin */
/* ------ */
} else if ( nc = 0,
( 1 == astSscanf( setting, "carlin= %d %n", &ival, &nc ) )
&& ( nc >= len ) ) {
astSetCarLin( this, ival );
/* PolyTan */
/* ------- */
} else if ( nc = 0,
( 1 == astSscanf( setting, "polytan= %d %n", &ival, &nc ) )
&& ( nc >= len ) ) {
astSetPolyTan( this, ival );
/* Iwc */
/* --- */
} else if ( nc = 0,
( 1 == astSscanf( setting, "iwc= %d %n", &ival, &nc ) )
&& ( nc >= len ) ) {
astSetIwc( this, ival );
/* Clean */
/* ----- */
} else if ( nc = 0,
( 1 == astSscanf( setting, "clean= %d %n", &ival, &nc ) )
&& ( nc >= len ) ) {
astSetClean( this, ival );
/* Warnings. */
/* -------- */
} else if ( nc = 0,
( 0 == astSscanf( setting, "warnings=%n%*[^\n]%n", &warn, &nc ) )
&& ( nc >= len ) ) {
astSetWarnings( this, setting + warn );
/* Define a macro to see if the setting string matches any of the
read-only attributes of this class. */
#define MATCH(attrib) \
( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \
( nc >= len ) )
/* If the attribute was not recognised, use this macro to report an error
if a read-only attribute has been specified. */
} else if ( MATCH( "ncard" ) ||
MATCH( "cardtype" ) ||
MATCH( "cardcomm" ) ||
MATCH( "cardname" ) ||
MATCH( "nkey" ) ||
MATCH( "allwarnings" ) ){
astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status,
setting, astGetClass( this ) );
astError( AST__NOWRT, "This is a read-only attribute." , status);
/* 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 SetCard( AstFitsChan *this, int icard, int *status ){
/*
*+
* Name:
* astSetCard
* Purpose:
* Set the value of the Card attribute.
* Type:
* Protected virtual function.
* Synopsis:
* #include "fitschan.h"
* void astSetCard( AstFitsChan *this, int icard )
* Class Membership:
* FitsChan method.
* Description:
* This function sets the value of the Card attribute for the supplied
* FitsChan. This is the index of the next card to be read from the
* FitsChan. If a value of 1 or less is supplied, the first card in
* the FitsChan will be read next. If a value greater than the number
* of cards in the FitsChan is supplied, the FitsChan will be left in an
* "end-of-file" condition, in which no further read operations can be
* performed.
* Parameters:
* this
* Pointer to the FitsChan.
* icard
* The index of the next card to read.
* Notes:
* - This function attempts to execute even if an error has occurred.
*-
*/
/* Check the supplied object. */
if ( !this ) return;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Rewind the FitsChan. */
astClearCard( this );
/* Move forward the requested number of cards. */
MoveCard( this, icard - 1, "astSetCard", astGetClass( this ), status );
/* Return. */
return;
}
static void SetItem( double ****item, int i, int jm, char s, double val, int *status ){
/*
* Name:
* SetItem
* Purpose:
* Store a value for a axis keyword value in a FitStore structure.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void SetItem( double ****item, int i, int jm, char s, double val, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* The supplied keyword value is stored in the specified array,
* at a position indicated by the axis and co-ordinate version.
* The array is created or extended as necessary to make room for
* the new value. Any old value is over-written.
* Parameters:
* item
* The address of the pointer within the FitsStore which locates the
* arrays of values for the required keyword (eg &(store->crval) ).
* The array located by the supplied pointer contains a vector of
* pointers. Each of these pointers is associated with a particular
* co-ordinate version (s), and locates an array of pointers for that
* co-ordinate version. Each such array of pointers has an element
* for each intermediate axis number (i), and the pointer locates an
* array of axis keyword values. These arrays of keyword values have
* one element for every pixel axis (j) or projection parameter (m).
* i
* The zero based intermediate axis index in the range 0 to 98. Set
* this to zero for keywords (e.g. CRPIX) which are not indexed by
* intermediate axis number.
* jm
* The zero based pixel axis index (in the range 0 to 98) or parameter
* index (in the range 0 to WCSLIB__MXPAR-1). Set this to zero for
* keywords (e.g. CRVAL) which are not indexed by either pixel axis or
* parameter number.
* val
* The keyword value to store.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
int el; /* Array index */
int nel; /* Number of elements in array */
int si; /* Integer co-ordinate version index */
/* Check the inherited status. */
if( !astOK ) return;
/* Convert the character co-ordinate version into an integer index, and
check it is within range. The primary axis description (s=' ') is
given index zero. 'A' is 1, 'B' is 2, etc. */
if( s == ' ' ) {
si = 0;
} else if( islower(s) ){
si = (int) ( s - 'a' ) + 1;
} else {
si = (int) ( s - 'A' ) + 1;
}
if( si < 0 || si > 26 ) {
astError( AST__INTER, "SetItem(fitschan): AST internal error; "
"co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s );
/* Check the intermediate axis index is within range. */
} else if( i < 0 || i > 98 ) {
astError( AST__INTER, "SetItem(fitschan): AST internal error; "
"intermediate axis index %d is invalid.", status, i );
/* Check the pixel axis or parameter index is within range. */
} else if( jm < 0 || jm > 99 ) {
astError( AST__INTER, "SetItem(fitschan): AST internal error; "
"pixel axis or parameter index %d is invalid.", status, jm );
/* Otherwise proceed... */
} else {
/* Store the current number of coordinate versions in the supplied array */
nel = astSizeOf( (void *) *item )/sizeof(double **);
/* If required, extend the array located by the supplied pointer so that
it is long enough to hold the specified co-ordinate version. */
if( nel < si + 1 ){
*item = (double ***) astGrow( (void *) *item, si + 1,
sizeof(double **) );
/* Check the pointer can be used. */
if( astOK ){
/* Initialise the new elements to hold NULL. Note, astGrow may add more
elements to the array than is actually needed, so use the actual current
size of the array as implied by astSize rather than the index si. */
for( el = nel;
el < astSizeOf( (void *) *item )/sizeof(double **);
el++ ) (*item)[el] = NULL;
}
}
/* If the above went OK... */
if( astOK ){
/* Store the currrent number of intermediate axes in the supplied array */
nel = astSizeOf( (void *) (*item)[si] )/sizeof(double *);
/* If required, extend the array so that it is long enough to hold the
specified intermediate axis. */
if( nel < i + 1 ){
(*item)[si] = (double **) astGrow( (void *) (*item)[si], i + 1,
sizeof(double *) );
/* Check the pointer can be used. */
if( astOK ){
/* Initialise the new elements to hold NULL. */
for( el = nel;
el < astSizeOf( (void *) (*item)[si] )/sizeof(double *);
el++ ) (*item)[si][el] = NULL;
}
}
/* If the above went OK... */
if( astOK ){
/* Store the current number of pixel axis or parameter values in the array. */
nel = astSizeOf( (void *) (*item)[si][i] )/sizeof(double);
/* If required, extend the array so that it is long enough to hold the
specified axis. */
if( nel < jm + 1 ){
(*item)[si][i] = (double *) astGrow( (void *) (*item)[si][i],
jm + 1, sizeof(double) );
/* Check the pointer can be used. */
if( astOK ){
/* Initialise the new elements to hold AST__BAD. */
for( el = nel;
el < astSizeOf( (void *) (*item)[si][i] )/sizeof(double);
el++ ) (*item)[si][i][el] = AST__BAD;
}
}
/* If the above went OK, store the supplied keyword value. */
if( astOK ) (*item)[si][i][jm] = val;
}
}
}
}
static void SetItemC( char *****item, int i, int jm, char s, const char *val,
int *status ){
/*
* Name:
* SetItemC
* Purpose:
* Store a character string for an axis keyword value in a FitStore
* structure.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void SetItemC( char *****item, int i, int jm, char s, const char *val,
* int *status )
* Class Membership:
* FitsChan member function.
* Description:
* The supplied keyword string value is stored in the specified array,
* at a position indicated by the axis and co-ordinate version.
* The array is created or extended as necessary to make room for
* the new value. Any old value is over-written.
* Parameters:
* item
* The address of the pointer within the FitsStore which locates the
* arrays of values for the required keyword (eg &(store->ctype) ).
* The array located by the supplied pointer contains a vector of
* pointers. Each of these pointers is associated with a particular
* co-ordinate version (s), and locates an array of pointers for that
* co-ordinate version. Each such array of pointers has an element
* for each intermediate axis number (i), and the pointer locates an
* array of axis keyword string pointers. These arrays of keyword
* string pointers have one element for every pixel axis (j) or
* projection parameter (m).
* i
* The zero based intermediate axis index in the range 0 to 98. Set
* this to zero for keywords (e.g. RADESYS) which are not indexed by
* intermediate axis number.
* jm
* The zero based pixel axis index (in the range 0 to 98) or parameter
* index (in the range 0 to WCSLIB__MXPAR-1). Set this to zero for
* keywords (e.g. CTYPE) which are not indexed by either pixel axis or
* parameter number.
* val
* The keyword string value to store. A copy of the supplied string
* is taken.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
int el; /* Array index */
int nel; /* Number of elements in array */
int si; /* Integer co-ordinate version index */
/* Check the inherited status and the supplied pointer. */
if( !astOK || !val ) return;
/* Convert the character co-ordinate version into an integer index, and
check it is within range. The primary axis description (s=' ') is
given index zero. 'A' is 1, 'B' is 2, etc. */
if( s == ' ' ) {
si = 0;
} else if( islower(s) ){
si = (int) ( s - 'a' ) + 1;
} else {
si = (int) ( s - 'A' ) + 1;
}
if( si < 0 || si > 26 ) {
astError( AST__INTER, "SetItemC(fitschan): AST internal error; "
"co-ordinate version '%c' ( char(%d) ) is invalid.", status, s, s );
/* Check the intermediate axis index is within range. */
} else if( i < 0 || i > 98 ) {
astError( AST__INTER, "SetItemC(fitschan): AST internal error; "
"intermediate axis index %d is invalid.", status, i );
/* Check the pixel axis or parameter index is within range. */
} else if( jm < 0 || jm > 99 ) {
astError( AST__INTER, "SetItemC(fitschan): AST internal error; "
"pixel axis or parameter index %d is invalid.", status, jm );
/* Otherwise proceed... */
} else {
/* Store the current number of coordinate versions in the supplied array */
nel = astSizeOf( (void *) *item )/sizeof(char ***);
/* If required, extend the array located by the supplied pointer so that
it is long enough to hold the specified co-ordinate version. */
if( nel < si + 1 ){
*item = (char ****) astGrow( (void *) *item, si + 1,
sizeof(char ***) );
/* Check the pointer can be used. */
if( astOK ){
/* Initialise the new elements to hold NULL. Note, astGrow may add more
elements to the array than is actually needed, so use the actual current
size of the array as implied by astSize rather than the index si. */
for( el = nel;
el < astSizeOf( (void *) *item )/sizeof(char ***);
el++ ) (*item)[el] = NULL;
}
}
/* If the above went OK... */
if( astOK ){
/* Store the currrent number of intermediate axes in the supplied array */
nel = astSizeOf( (void *) (*item)[si] )/sizeof(char **);
/* If required, extend the array so that it is long enough to hold the
specified intermediate axis. */
if( nel < i + 1 ){
(*item)[si] = (char ***) astGrow( (void *) (*item)[si], i + 1,
sizeof(char **) );
/* Check the pointer can be used. */
if( astOK ){
/* Initialise the new elements to hold NULL. */
for( el = nel;
el < astSizeOf( (void *) (*item)[si] )/sizeof(char **);
el++ ) (*item)[si][el] = NULL;
}
}
/* If the above went OK... */
if( astOK ){
/* Store the current number of pixel axis or parameter values in the array. */
nel = astSizeOf( (void *) (*item)[si][i] )/sizeof(char *);
/* If required, extend the array so that it is long enough to hold the
specified axis. */
if( nel < jm + 1 ){
(*item)[si][i] = (char **) astGrow( (void *) (*item)[si][i],
jm + 1, sizeof(char *) );
/* Check the pointer can be used. */
if( astOK ){
/* Initialise the new elements to hold NULL. */
for( el = nel;
el < astSizeOf( (void *) (*item)[si][i] )/sizeof(char *);
el++ ) (*item)[si][i][el] = NULL;
}
}
/* If the above went OK... */
if( astOK ){
/* Store a copy of the supplied string, using any pre-allocated memory. */
(*item)[si][i][jm] = (char *) astStore( (void *) (*item)[si][i][jm],
(void *) val,
strlen( val ) + 1 );
}
}
}
}
}
static void SetSourceFile( AstChannel *this_channel, const char *source_file,
int *status ) {
/*
* Name:
* SetSourceFile
* Purpose:
* Set a new value for the SourceFile attribute.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void SetSourceFile( AstChannel *this, const char *source_file,
* int *status )
* Class Membership:
* FitsChan member function (over-rides the astSetSourceFile
* method inherited from the Channel class).
* Description:
* This function stores the supplied string as the new value for the
* SourceFile attribute. In addition, it also attempts to open the
* file, read FITS headers from it and append them to the end of the
* FitsChan. It then closes the SourceFile.
* Parameters:
* this
* Pointer to the FitsChan.
* source_file
* The new attribute value. Should be the path to an existing text
* file, holding FITS headers (one per line)
* status
* Inherited status pointer.
*/
/* Local Constants: */
#define ERRBUF_LEN 80
/* Local Variables: */
AstFitsChan *this; /* Pointer to the FitsChan structure */
FILE *fd; /* Descriptor for source file */
char *errstat; /* Pointer for system error message */
char card[ AST__FITSCHAN_FITSCARDLEN + 2 ]; /* Buffer for source line */
char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_channel;
/* Invoke the parent astSetSourceFile method to store the supplied
string in the Channel structure. */
(*parent_setsourcefile)( this_channel, source_file, status );
/* Attempt to open the file. */
fd = NULL;
if( astOK ) {
fd = fopen( source_file, "r" );
if( !fd ) {
if ( errno ) {
#if HAVE_STRERROR_R
strerror_r( errno, errbuf, ERRBUF_LEN );
errstat = errbuf;
#else
errstat = strerror( errno );
#endif
astError( AST__RDERR, "astSetSourceFile(%s): Failed to open input "
"SourceFile '%s' - %s.", status, astGetClass( this ),
source_file, errstat );
} else {
astError( AST__RDERR, "astSetSourceFile(%s): Failed to open input "
"SourceFile '%s'.", status, astGetClass( this ),
source_file );
}
}
}
/* Move the FitsChan to EOF */
astSetCard( this, INT_MAX );
/* Read each line from the file, remove trailing space, and append to the
FitsChan. */
while( astOK && fgets( card, AST__FITSCHAN_FITSCARDLEN + 2, fd ) ) {
card[ astChrLen( card ) ] = 0;
astPutFits( this, card, 0 );
}
/* Close the source file. */
if( fd ) fclose( fd );
}
static void SetTableSource( AstFitsChan *this,
void (*tabsource)( void ),
void (*tabsource_wrap)( void (*)( void ),
AstFitsChan *, const char *,
int, int, int * ),
int *status ){
/*
*+
* Name:
* astSetTableSource
* Purpose:
* Register source and wrapper function for accessing tables in FITS files.
* Type:
* Protected function.
* Synopsis:
* #include "fitschan.h"
* void astSetTableSource( AstFitsChan *this,
* void (*tabsource)( void ),
* void (*tabsource_wrap)( void (*)( void ),
* AstFitsChan *, const char *,
* int, int, int * ),
* int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function registers a table source function and its wrapper. A
* wrapper function exists to adapt the API of the table source
* function to the needs of different languages. The wrapper is called
* from the FitsChan code. The wrapper then adjusts the arguments as
* required and then calls the actualy table source function.
* Parameters:
* this
* Pointer to the FitsChan.
* tabsource
* Pointer to the table source function. The API for this function
* will depend on the language, and so is cast to void here. It
* should be cast to the required form within the wrapper function.
* tabsource_wrap
* The wrapper function.
*-
*/
/* Local Variables: */
/* Check the global error status. */
if ( !astOK ) return;
this->tabsource = tabsource;
this->tabsource_wrap = tabsource_wrap;
}
static void SetValue( AstFitsChan *this, const char *keyname, void *value,
int type, const char *comment, int *status ){
/*
* Name:
* SetValue
* Purpose:
* Save a FITS keyword value, over-writing any existing keyword value.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void SetValue( AstFitsChan *this, char *keyname, void *value,
* int type, const char *comment, int *status )
* Class Membership:
* FitsChan
* Description:
* This function saves a keyword value as a card in the supplied
* FitsChan. Comment cards are always inserted in-front of the current
* card. If the keyword is not a comment card, any existing value
* for the keyword is over-written with the new value (even if it is
* marked as having been read). Otherwise, (i.e. if it is not a comment
* card, and no previous value exists) it is inserted in front
* of the current card.
* Parameters:
* this
* A pointer to the FitsChan.
* keyname
* A pointer to a string holding the keyword name.
* value
* A pointer to a buffer holding the keyword value. For strings,
* the buffer should hold a pointer to the character string.
* type
* The FITS data type of the supplied keyword value.
* comment
* A comment to store with the keyword.
* status
* Pointer to the inherited status variable.
* Notes:
* - Nothing is stored if a NULL pointer is supplied for "value".
* - If the keyword has a value of AST__BAD then nothing is stored,
* and an error is reported.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
FitsCard *card; /* Pointer to original current card */
const char *class; /* Class name to include in error messages */
const char *method; /* Method name to include in error messages */
int newcard; /* Has the original current card been deleted? */
int old_ignore_used; /* Original setting of external ignore_used variable */
int stored; /* Has the keyword been stored? */
/* Check the status and supplied value pointer. */
if( !astOK || !value ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this);
/* Set up the method and class names for inclusion in error mesages. */
method = "astWrite";
class = astGetClass( this );
/* Comment card are always inserted in-front of the current card. */
if ( type == AST__COMMENT ) {
SetFits( this, keyname, value, type, comment, 0, status );
/* Otherwise... */
} else {
/* Report an error if a bad value is stored for a keyword. */
if( type == AST__FLOAT ){
if( *( (double *) value ) == AST__BAD && astOK ) {
astError( AST__BDFTS, "%s(%s): The required FITS keyword "
"\"%s\" is indeterminate.", status, method, class, keyname );
}
}
/* Save a pointer to the current card. */
card = (FitsCard *) this->card;
/* Indicate that we should not skip over cards marked as having been
read. */
old_ignore_used = ignore_used;
ignore_used = 0;
/* Indicate that we have not yet stored the keyword value. */
stored = 0;
/* Attempt to find a card refering to the supplied keyword. If one is
found, it becomes the current card. */
if( SearchCard( this, keyname, "astWrite", astGetClass( this ), status ) ){
/* If the card which was current on entry to this function will be
over-written, we will need to take account of this when re-instating the
original current card. Make a note of this. */
newcard = ( card == (FitsCard *) this->card );
/* Replace the current card with a card holding the supplied information. */
SetFits( this, keyname, value, type, comment, 1, status );
stored = 1;
/* If we have just replaced the original current card, back up a card
so that the replacement card becomes the current card. */
if( newcard ) {
MoveCard( this, -1, "astWrite", astGetClass( this ), status );
/* Otherwise, re-instate the original current card. */
} else {
this->card = (void *) card;
}
}
/* If the keyword has not yet been stored (i.e. if it did not exist in the
FitsChan), re-instate the original current card and insert the new card
before the original current card, leaving the current card unchanged. */
if( !stored ) {
this->card = (void *) card;
SetFits( this, keyname, value, type, comment, 0, status );
}
/* Re-instate the original flag indicating if cards marked as having been
read should be skipped over. */
ignore_used = old_ignore_used;
}
}
static void Shpc1( double xmin, double xmax, int n, double *d, double *w,
int *status ){
/*
* Name:
* Shpc1
* Purpose:
* Modifies a one-dimensional polynomial to scale the polynomial argument.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void Shpc1( double xmin, double xmax, int n, double *d, double *w,
* int *status )
* Description:
* Given the coefficients of a one-dimensional polynomial P(u) defined on a
* unit interval (i.e. -1 <= u <= +1 ), find the coefficients of another
* one-dimensional polynomial Q(x) where:
*
* Q(x) = P(u)
* u = ( 2*x - ( xmax + xmin ) ) / ( xmax - xmin )
*
* That is, u is a scaled version of x, such that the unit interval in u
* maps onto (xmin:xmax) in x.
* Parameters:
* xmin
* X value corresponding to u = -1
* xmax
* X value corresponding to u = +1
* n
* One more than the maximum power of u within P.
* d
* An array of n elements supplied holding the coefficients of P such
* that the coefficient of (u^i) is held in element (i).
* w
* An array of n elements returned holding the coefficients of Q such
* that the coefficient of (x^i) is held in element (i).
* status
* Pointer to the inherited status variable.
* Notes:
* - Vaguely inspired by the Numerical Recipes routine "pcshft". But the
* original had bugs, so I wrote this new version from first principles.
*/
/* Local Variables: */
double b;
double a;
int j;
int i;
/* Check inherited status */
if( !astOK ) return;
/* Get the scale and shift terms so that u = a*x + b */
a = 2.0/( xmax - xmin );
b = ( xmin + xmax )/( xmin - xmax );
/* Initialise the returned coeffs */
for( i = 0; i < n; i++ ) w[ i ] = 0.0;
/* The supplied Polynomial is
P(u) = d0 + d1*u + d2*u^2 + ...
= d0 + u*( d1 + u*( d2 + ... u*( d{n-1} ) ) ) . . . . . (1)
= d0 + (a*x+b)*( d1 + (a*x+b)*( d2 + ... (a*x+b)*( d[n-1] ) ) )
The inner-most parenthesised expression is a polynomial of order zero
(a constant - d[n-1]). Store the coefficients of this zeroth order
polynomial in the returned array. The "w" array is used to hold the
coefficients of Q, i.e. coefficients of powers of "x", not "u", but
since the inner-most polynomial is a constant, it makes no difference
(x^0 == u^0 == 1). */
w[ 0 ] = d[ n - 1 ];
/* Now loop through each remaining level of parenthetic nesting in (1). At
each level, the parenthesised expression represents a polynomial of order
"i". At the end of each pass though this loop, the returned array "w"
holds the coefficients of this "i"th order polynomial. So on the last
loop, i = n-1, "w" holds the required coefficients of Q. */
for( i = 1; i < n; i++ ) {
/* If "R" is the polynomial at the "i-1"th level of nesting (the
coefficiemts of which are currently held in "w"), and "S" is the
polynomial at the "i"th level of nesting, we can see from (1) that:
S = d[ n - 1 - i ] + u*R
Substituting for "u", this becomes
S = d[ n - 1 - i ] + ( a*x + b )*R
= d[ n - 1 - i ] + a*R*x + b*R
Looking at each of these three terms in reverse order:
1) The "b*R" term is implemented by simply scaling the current contents
of the "w" array by "b"; in the "a*R*x" term.
2) In "a*R*x", the effect of multiplying by "x" is to move the existing
coefficients in "w" up one element. We then multiply the shifted
coefficients by "a" and add them onto the coefficients produced at
step 1) above.
We know that "w" still contains the initial zeros at indices higher than
"i" so we only need to scale the bottom "i" elements. We do not do the
zeroth term in this loop since there is no lower term to shift up into
it. */
for( j = i; j > 0; j-- ){
w[ j ] = b*w[ j ] + a*w[ j - 1 ];
}
/* Now do the zeroth term. Scale the existing zeroth term by "b" as
required by step 1) and add on the first term, the constant
"d[ n - 1 - i ]". Step 2) is a no-op, since in effect the value of
"w[-1]" is zero. */
w[ 0 ] = d[ n - i - 1 ] + b*w[ 0 ];
}
}
static void ShowFits( AstFitsChan *this, int *status ){
/*
*++
* Name:
c astShowFits
f AST_SHOWFITS
* Purpose:
* Display the contents of a FitsChan on standard output.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c void astShowFits( AstFitsChan *this )
f CALL AST_SHOWFITS( THIS, STATUS )
* Class Membership:
* FitsChan method.
* Description:
c This function
f This routine
* formats and displays all the cards in a FitsChan on standard output.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
f STATUS = INTEGER (Given and Returned)
f The global status.
*--
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
char card[ AST__FITSCHAN_FITSCARDLEN + 1]; /* Buffer for header card */
int icard; /* Current card index on entry */
int old_ignore_used; /* Original value of external variable ignore_used */
/* Check the global status. */
if( !astOK ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this);
/* Store the current card index. */
icard = astGetCard( this );
/* Indicate that cards which have been read into an AST object should skipped
over by the functions which navigate the linked list of cards. */
old_ignore_used = ignore_used;
ignore_used = 1;
/* Ensure that the first card in the FitsChan will be the next one to be
read. */
astSetCard( this, 1 );
/* Loop round obtaining and writing out each card, until all cards have been
processed. */
while( !astFitsEof( this ) && astOK ){
/* Get the current card, and display it. The call to astFindFits increments
the current card. */
if( astFindFits( this, "%f", card, 1 ) ) printf( "%s\n", card );
}
/* Re-instate the original flag indicating if cards marked as having been
read should be skipped over. */
ignore_used = old_ignore_used;
/* Set the current card index back to what it was on entry. */
astSetCard( this, icard );
}
static int Similar( const char *str1, const char *str2, int *status ){
/*
* Name:
* Similar
* Purpose:
* Are two string effectively the same to human readers?
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void Similar( const char *str1, const char *str2, int *status )
* Class Membership:
* FitsChan
* Description:
* This function returns a non-zero value if the two supplied strings
* are equivalent to a human reader. This is assumed to be the case if
* the strings are equal apart from leading and trailing white space,
* multiple embedded space, and case.
* Parameters:
* str1
* The first string
* str2
* The second string
* status
* Pointer to the inherited status variable.
* Returned Value:
* Non-zero if the two supplied strings are equivalent, and zero
* otherwise.
*/
/* Local Variables: */
const char *ea; /* Pointer to end of string a */
const char *eb; /* Pointer to end of string b */
const char *a; /* Pointer to next character in string a */
const char *b; /* Pointer to next character in string b */
int result; /* Are the two strings equivalent? */
int ss; /* Skip subsequent spaces? */
/* Initialise */
result = 0;
/* Check the status and supplied value pointer. */
if( !astOK ) return result;
/* Initialise pointers into the two strings. */
a = str1;
b = str2;
/* Get a pointer to the character following the last non-blank character in
each string. */
ea = a + ChrLen( a, status ) - 1;
eb = b + ChrLen( b, status ) - 1;
/* Set a flag indicating that spaces before the next non-blank character
should be ignored. */
ss = 1;
/* Compare the strings. */
while( 1 ){
/* Move on to the next significant character in both strings. */
while( a < ea && *a == ' ' && ss ) a++;
while( b < eb && *b == ' ' && ss ) b++;
/* If one string has been exhausted but the other has not, the strings
are not equivalent. */
if( ( a < ea && b == eb ) || ( a == ea && b < eb ) ) {
break;
/* If both strings have been exhausted simultaneously, the strings
are equivalent. */
} else if( b == eb && a == ea ) {
result = 1;
break;
/* If neither string has been exhausted, compare the current character
for equality, ignoring case. Break if they are different. */
} else if( tolower( *a ) != tolower( *b ) ){
break;
/* If the two characters are both spaces, indicate that subsequent spaces
should be skipped. */
} else if( *a == ' ' ) {
ss = 1;
/* If the two characters are not spaces, indicate that subsequent spaces
should not be skipped. */
} else {
ss = 0;
}
/* Move on to the next characters. */
a++;
b++;
}
/* Return the result. */
return result;
}
static void SinkWrap( void (* sink)( const char * ), const char *line, int *status ) {
/*
* Name:
* SinkWrap
* Purpose:
* Wrapper function to invoke a C FitsChan sink function.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void SinkWrap( void (* sink)( const char * ), const char *line, int *status )
* Class Membership:
* FitsChan 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 FitsChan 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 AstMapping *SIPMapping( double *dim, FitsStore *store, char s,
int naxes, const char *method,
const char *class, int *status ){
/*
* Name:
* SIPMapping
* Purpose:
* Create a Mapping descriping "-SIP" (Spitzer) distortion.
* Type:
* Private function.
* Synopsis:
* AstMapping *SIPMapping( double *dim, FitsStore *store, char s, int naxes,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* This function uses the values in the supplied FitsStore to create a
* Mapping which implements the "-SIP" distortion code. This is the
* code used by the Spitzer project and is described in:
*
* http://irsa.ipac.caltech.edu/data/SPITZER/docs/files/spitzer/shupeADASS.pdf
*
* SIP distortion can only be applied to axes 0 and 1. Other axes are
* passed unchanged by the returned Mapping.
* Parameters:
* dim
* The dimensions of the array in pixels. AST__BAD is stored for
* each value if dimensions are not known.
* store
* A structure containing information about the requested axis
* descriptions derived from a FITS header.
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* naxes
* The number of intermediate world coordinate axes (WCSAXES).
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the Mapping.
*/
/* Local Variables: */
AstMapping *ret; /* Pointer to the returned Mapping */
AstPolyMap *pmap; /* PolyMap describing the distortion */
AstPolyMap *pmap2; /* New PolyMap describing the distortion */
double ****item; /* Address of FitsStore item to use */
double *c; /* Pointer to start of coefficient description */
double *coeff_f; /* Array of coeffs. for forward transformation */
double *coeff_i; /* Array of coeffs. for inverse transformation */
double cof; /* Coefficient value */
double lbnd[ 2 ]; /* Lower bounds of fitted region */
double ubnd[ 2 ]; /* Upper bounds of fitted region */
int def; /* Is transformation defined? */
int iin; /* Input (u or v) index */
int iout; /* Output (U or V) index */
int ncoeff_f; /* No. of coeffs. for forward transformation */
int ncoeff_i; /* No. of coeffs. for inverse transformation */
int p; /* Power of u or U */
int pmax; /* Max power of u or U */
int q; /* Power of v or V */
int qmax; /* Max power of v or V */
/* Initialise the pointer to the returned Mapping. */
ret = NULL;
/* Check the global status. */
if ( !astOK ) return ret;
/* Store coefficients of the forward transformation:
================================================ */
/* Indicate that we have as yet no coefficients for the forward polynomials. */
ncoeff_f = 0;
/* Indicate that we do not yet have any evidence that the forward
transformation is defined. */
def = 0;
/* Allocate workspace to hold descriptions of (initially) 20 coefficients used
within the forward polynomials. */
coeff_f = astMalloc( sizeof( double )*20 );
/* Store the coefficients of the polynomial which produces each output
axis (U or V) in turn. */
for( iout = 0; iout < 2; iout++ ){
/* Get a pointer to the FitsStore item holding the values defining this
output. */
item = ( iout == 0 ) ? &(store->asip) : &(store->bsip);
/* Get the largest powers used of u and v. */
pmax = GetMaxI( item, s, status );
qmax = GetMaxJM( item, s, status );
/* Loop round all combination of powers. */
for( p = 0; p <= pmax; p++ ){
for( q = 0; q <= qmax; q++ ){
/* Get the polynomial coefficient for this combination of powers. */
cof = GetItem( item, p, q, s, NULL, method, class, status );
/* If there is no coefficient for this combination of powers, use a value
of zero. Otherwise indicate we have found at least one coefficient. */
if( cof == AST__BAD ) {
cof = 0.0;
} else {
def = 1;
}
/* The distortion polynomial gives a correction to be added on to the
input value. On the other hand, the returned Mapping is a direct
transformation from input to output. Therefore increment the coefficient
value by 1 for the term which corresponds to the current output axis. */
if( p == ( 1 - iout ) && q == iout ) cof += 1.0;
/* If the coefficient is not zero, store it in the array of coefficient
descriptions. */
if( cof != 0.0 ) {
/* Increment the number of coefficients for the forward polynomials. */
ncoeff_f++;
/* Ensure the "coeff_f" array is large enough to hold the new coefficient. */
coeff_f = astGrow( coeff_f, sizeof( double )*4, ncoeff_f );
if( astOK ) {
/* Store it. Each coefficient is described by 4 values (since we have 2
inputs to the Mapping). The first is the coefficient value, the second
is the (1-based) index of the output to which the coefficient relates.
The next is the power of input 0, and the last one is the power of input 1. */
c = coeff_f + 4*( ncoeff_f - 1 );
c[ 0 ] = cof;
c[ 1 ] = iout + 1;
c[ 2 ] = p;
c[ 3 ] = q;
}
}
}
}
}
/* If no coefficients were supplied in the FitsStore, the forward
transformation is undefined. */
if( !def ) ncoeff_f = 0;
/* Store coefficients of the inverse transformation:
================================================ */
/* Indicate that we have as yet no coefficients for the inverse polynomials. */
ncoeff_i = 0;
/* Indicate that we do not yet have any evidence that the forward
transformation is defined. */
def = 0;
/* Allocate workspace to hold descriptions of (initially) 20 coefficients used
within the inverse polynomials. */
coeff_i = astMalloc( sizeof( double )*20 );
/* Store the coefficients of the polynomial which produces each input
axis (u or v) in turn. */
for( iin = 0; iin < 2; iin++ ){
/* Get a pointer to the FitsStore item holding the values defining this
output. */
item = ( iin == 0 ) ? &(store->apsip) : &(store->bpsip);
/* Get the largest powers used of U and V. */
pmax = GetMaxI( item, s, status );
qmax = GetMaxJM( item, s, status );
/* Loop round all combination of powers. */
for( p = 0; p <= pmax; p++ ){
for( q = 0; q <= qmax; q++ ){
/* Get the polynomial coefficient for this combination of powers. */
cof = GetItem( item, p, q, s, NULL, method, class, status );
/* If there is no coefficient for this combination of powers, use a value
of zero. Otherwise indicate we have found at least one coefficient. */
if( cof == AST__BAD ) {
cof = 0.0;
} else {
def = 1;
}
/* The distortion polynomial gives a correction to be added on to the
output value. On the other hand, the returned Mapping is a direct
transformation from output to input. Therefore increment the coefficient
value by 1 for the term which corresponds to the current input axis. */
if( p == ( 1 - iin ) && q == iin ) cof += 1.0;
/* If the coefficient is not zero, store it in the array of coefficient
descriptions. */
if( cof != 0.0 ) {
/* Increment the number of coefficients for the inverse polynomials. */
ncoeff_i++;
/* Ensure the "coeff_i" array is large enough to hold the new coefficient. */
coeff_i = astGrow( coeff_i, sizeof( double )*4, ncoeff_i );
if( astOK ) {
/* Store it. Each coefficient is described by 4 values (since we have 2
outputs to the Mapping). The first is the coefficient value, the second
is the (1-based) index of the input to which the coefficient relates. The
next is the power of output 0, and the last one is the power of output 1. */
c = coeff_i + 4*( ncoeff_i - 1 );
c[ 0 ] = cof;
c[ 1 ] = iin + 1;
c[ 2 ] = p;
c[ 3 ] = q;
}
}
}
}
}
/* If no coefficients were supplied in the FitsStore, the forward
transformation is undefined. */
if( !def ) ncoeff_i = 0;
/* Create the returned Mapping:
============================ */
/* If neither transformation is defined, create a UnitMap. */
if( ncoeff_f == 0 && ncoeff_i == 0 ){
ret = (AstMapping *) astUnitMap( naxes, "", status );
/* Otherwise, create a PolyMap to describe axes 0 and 1. */
} else {
pmap = astPolyMap( 2, 2, ncoeff_f, coeff_f, ncoeff_i, coeff_i, "", status );
/* The inverse transformations supplied within SIP headers are often
inaccurate. So replace any existing inverse by sampling the supplied
transformation, and fitting a polynomial to the sampled positions. If
the fit fails to reach 0.01 pixel accuracy, forget it and rely on the
(slower) iterative inverse provided by the PolyMap class. Do the fit
over an area three times the size of the image to provide accurate
values outside the image.*/
lbnd[ 0 ] = ( dim[ 0 ] != AST__BAD ) ? -dim[ 0 ] : -1000.0;
lbnd[ 1 ] = ( dim[ 1 ] != AST__BAD ) ? -dim[ 1 ] : -1000.0;
ubnd[ 0 ] = ( dim[ 0 ] != AST__BAD ) ? 2*dim[ 0 ] : 2000.0;
ubnd[ 1 ] = ( dim[ 1 ] != AST__BAD ) ? 2*dim[ 1 ] : 2000.0;
pmap2 = astPolyTran( pmap, (ncoeff_f == 0), 0.0001, 0.01, 7, lbnd,
ubnd );
if( pmap2 ) {
(void) astAnnul( pmap );
pmap = pmap2;
} else {
astSet( pmap, "IterInverse=1,NiterInverse=6,TolInverse=1.0E-8",
status );
}
/* Add the above Mapping in parallel with a UnitMap which passes any
other axes unchanged. */
ret = AddUnitMaps( (AstMapping *) pmap, 0, naxes, status );
pmap = astAnnul( pmap );
}
/* Free resources. */
coeff_f = astFree( coeff_f );
coeff_i = astFree( coeff_i );
/* Return the result. */
return ret;
}
static void SkyPole( AstWcsMap *map2, AstMapping *map3, int ilon, int ilat,
int *wperm, char s, FitsStore *store, const char *method,
const char *class, int *status ){
/*
* Name:
* SkyPole
* Purpose:
* Put values for FITS keywords LONPOLE and LATPOLE into a FitsStore.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void SkyPole( AstWcsMap *map2, AstMapping *map3, int ilon, int ilat,
* int *wperm, char s, FitsStore *store, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function calculates values for the LONPOLE and LATPOLE FITS
* keywords and stores them in the supplied FitsStore. LONPOLE and
* LATPOLE are the longitude and latitude of the celestial north pole
* in native spherical coordinates.
* Parameters:
* map2
* Pointer to the Mapping from Intermediate World Coordinates to Native
* Spherical Coordinates.
* map3
* Pointer to the Mapping from Native Spherical Coordinates to celestial
* coordinates.
* ilon
* Zero-based index of longitude output from "map3".
* ilat
* Zero-based index of latitude output from "map3".
* wperm
* Pointer to an array of integers with one element for each axis of
* the current Frame. Each element holds the zero-based
* index of the FITS-WCS axis (i.e. the value of "i" in the keyword
* names "CTYPEi", "CRVALi", etc) which describes the Frame axis.
* s
* The co-ordinate version character. A space means the primary
* axis descriptions. Otherwise the supplied character should be
* an upper case alphabetical character ('A' to 'Z').
* store
* The FitsStore in which to store the FITS WCS keyword values.
* 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.
*/
/* Local Variables: */
AstPointSet *pset1; /* PointSet holding intermediate wcs coords */
AstPointSet *pset2; /* PointSet holding final WCS coords */
double **ptr1; /* Pointer to coordinate data */
double **ptr2; /* Pointer to coordinate data */
double alpha0; /* Long. of fiducial point in standard system */
double alphap; /* Celestial longitude of native north pole */
double deflonpole; /* Default value for lonpole */
double delta0; /* Lat. of fiducial point in standard system */
double latpole; /* Native latitude of celestial north pole */
double lonpole; /* Native longitude of celestial north pole */
double phi0; /* Native longitude at fiducial point */
double theta0; /* Native latitude at fiducial point */
int axlat; /* Index of latitude output from "map2" */
int axlon; /* Index of longitude output from "map2" */
int fits_ilat; /* FITS WCS axis index for latitude axis */
int fits_ilon; /* FITS WCS axis index for longitude axis */
int iax; /* Axis index */
int nax; /* Number of IWC axes */
int nax2; /* Number of WCS axes */
/* Check the inherited status. */
if( !astOK ) return;
/* Store the indices of the native longitude and latitude outputs of the
WcsMap. */
axlon = astGetWcsAxis( map2, 0 );
axlat = astGetWcsAxis( map2, 1 );
/* Store the indices of the FITS WCS axes for longitude and latitude */
fits_ilon = wperm[ ilon ];
fits_ilat = wperm[ ilat ];
/* To find the longitude and latitude of the celestial north pole in native
spherical coordinates, we will transform the coords of the celestial north
pole into spherical cords using the inverse of "map2", and if the resulting
native spherical coords differ from the default values of LONPOLE and
LATPOLE, we store them in the FitsStore. However, for zenithal projections,
any value can be used simply by introducing an extra rotation into the
(X,Y) projection plane. If values have been set in the WcsMap (as
projection parameters PVi_3 and PVi_4 for longitude axis "i") uses
them. Otherwise, set the values bad to indicate that the default values
should be used. Note, these projection parameters are used for other
purposes in a TPN projection. */
lonpole = AST__BAD;
latpole = AST__BAD;
if( astIsZenithal( map2 ) ) {
if( astGetWcsType( map2 ) != AST__TPN ) {
lonpole = astTestPV( map2, axlon, 3 ) ? astGetPV( map2, axlon, 3 )
: AST__BAD;
latpole = astTestPV( map2, axlon, 4 ) ? astGetPV( map2, axlon, 4 )
: AST__BAD;
}
/* For non-zenithal projections, do the full calculation. */
} else {
/* Allocate resources. */
nax = astGetNin( map2 );
pset1 = astPointSet( 1, nax, "", status );
ptr1 = astGetPoints( pset1 );
nax2 = astGetNout( map3 );
pset2 = astPointSet( 1, nax2, "", status );
ptr2 = astGetPoints( pset2 );
if( astOK ) {
/* Calculate the longitude and latitude of the celestial north pole
in native spherical coordinates (using the inverse of map3). These
values correspond to the LONPOLE and LATPOLE keywords. */
for( iax = 0; iax < nax2; iax++ ) ptr2[ iax ][ 0 ] = 0.0;
ptr2[ ilat ][ 0 ] = AST__DPIBY2;
(void) astTransform( map3, pset2, 0, pset1 );
/* Retrieve the latitude and longitude (in the standard system) of the
fiducial point (i.e. CRVAL), in radians. */
delta0 = GetItem( &(store->crval), fits_ilat, 0, s, NULL, method, class, status );
if( delta0 == AST__BAD ) delta0 = 0.0;
delta0 *= AST__DD2R;
alpha0 = GetItem( &(store->crval), fits_ilon, 0, s, NULL, method, class, status );
if( alpha0 == AST__BAD ) alpha0 = 0.0;
alpha0 *= AST__DD2R;
/* The default value of the LATPOLE is defined by equation 8 of FITS-WCS
paper II (taking the +ve signs). Find this value. */
if( WcsNatPole( NULL, map2, alpha0, delta0, 999.0, ptr1[ axlon ],
&alphap, &latpole, status ) ){
/* If the default value is defined, compare it to the latitude of the
north pole found above. If they are equal use a bad value instead to
prevent an explicit keyword from being added to the FitsChan. */
if( EQUALANG( ptr1[ axlat ][ 0 ], latpole ) ) {
latpole = AST__BAD;
} else {
latpole = ptr1[ axlat ][ 0 ];
}
/* If the default value is not defined, always store an explicit LATPOLE
value. */
} else {
latpole = ptr1[ axlat ][ 0 ];
}
/* The default LONPOLE value is zero if the celestial latitude at the
fiducial point is greater than or equal to the native latitude at the
fiducial point. Otherwise, the default is (+ or -) 180 degrees. If LONPOLE
takes the default value, replace it with AST__BAD to prevent an explicit
keyword being stored in the FitsChan. */
GetFiducialNSC( map2, &phi0, &theta0, status );
lonpole = palDranrm( ptr1[ axlon ][ 0 ] );
if( delta0 >= theta0 ){
deflonpole = 0.0;
} else {
deflonpole = AST__DPI;
}
if( EQUALANG( lonpole, deflonpole ) ) lonpole = AST__BAD;
}
/* Convert from radians to degrees. */
if( lonpole != AST__BAD ) lonpole *= AST__DR2D;
if( latpole != AST__BAD ) latpole *= AST__DR2D;
/* Free resources. */
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
}
/* Store these values. */
SetItem( &(store->lonpole), 0, 0, s, lonpole, status );
SetItem( &(store->latpole), 0, 0, s, latpole, status );
/* FITS-WCS paper 2 recommends putting a copy of LONPOLE and LATPOLE in
projection parameters 3 and 4 associated with the longitude axis. Only do
this if the projection is not TPN (since this projection uses these
parameters for other purposes). */
if( astGetWcsType( map2 ) != AST__TPN ) {
SetItem( &(store->pv), fits_ilon, 3, s, lonpole, status );
SetItem( &(store->pv), fits_ilon, 4, s, latpole, status );
}
}
static int SkySys( AstFitsChan *this, AstSkyFrame *skyfrm, int wcstype,
int wcsproj, FitsStore *store, int axlon, int axlat, char s,
int isoff, const char *method, const char *class, int *status ){
/*
* Name:
* SkySys
* Purpose:
* Return FITS-WCS values describing a sky coordinate system.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int SkySys( AstFitsChan *this, AstSkyFrame *skyfrm, int wcstype,
* int wcsproj, FitsStore *store, int axlon, int axlat, char s,
* int isoff, const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* This function sets values for the following FITS-WCS keywords
* within the supplied FitsStore structure: CTYPE, CNAME, RADESYS, EQUINOX,
* MJDOBS, CUNIT, OBSGEO-X/Y/Z. The values are derived from the supplied
* SkyFrame and WcsMap.
* Parameters:
* this
* Pointer to the FitsChan.
* skyfrm
* A pointer to the SkyFrame to be described.
* wcstype
* The type of WCS: 0 = TAB, 1 = WcsMap projection.
* wcsproj
* An identifier for the type of WCS projection to use. Should be
* one of the values defined by the WcsMap class. Only used if "wcstype"
* is 1.
* store
* A pointer to the FitsStore structure in which to store the
* results.
* axlon
* The index of the FITS WCS longitude axis (i.e. the value of "i"
* in "CTYPEi").
* axlat
* The index of the FITS WCS latitude axis (i.e. the value of "i"
* in "CTYPEi").
* s
* Co-ordinate version character.
* isoff
* If greater than zero, the description to add to the FitsStore
* should describe offset coordinates. If less than zero, the
* description to add to the FitsStore should describe absolute
* coordinates but should include the SkyRefIs, SkyRef and SkyRefP
* attributes. If zero, ignore all offset coordinate info. The
* absolute value indicates the nature of the reference point:
* 1 == "pole", 2 == "origin", otherwise "ignored".
* 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:
* Are the keywords values in the FitsStore usable?
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
char *label; /* Pointer to axis label string */
char attr[20]; /* Buffer for AST attribute name */
char com[80]; /* Buffer for keyword comment */
char lattype[MXCTYPELEN];/* Latitude axis CTYPE value */
char lontype[MXCTYPELEN];/* Longitude axis CTYPE value */
const char *latsym; /* SkyFrame latitude axis symbol */
const char *lonsym; /* SkyFrame longitude axis symbol */
const char *prj_name; /* Pointer to projection name string */
const char *skyref; /* Formatted SkyRef position */
const char *skyrefis; /* SkyRefIs value */
const char *sys; /* Celestal coordinate system */
const char *timesys; /* Timescale specified in FitsChan */
double ep; /* Epoch of observation in required timescale (MJD) */
double ep_tdb; /* Epoch of observation in TDB timescale (MJD) */
double ep_utc; /* Epoch of observation in UTC timescale (MJD) */
double eq; /* Epoch of reference equinox (MJD) */
double geolat; /* Geodetic latitude of observer (radians) */
double geolon; /* Geodetic longitude of observer (radians) */
double h; /* Geodetic altitude of observer (metres) */
double skyref_lat; /* SkyRef latitude value (rads) */
double skyrefp_lat; /* SkyRefP latitude value (rads) */
double skyref_lon; /* SkyRef longitude value (rads) */
double skyrefp_lon; /* SkyRefP longitude value (rads) */
double xyz[3]; /* Geocentric position vector (in m) */
int defdate; /* Can the date keywords be defaulted? */
int i; /* Character count */
int isys; /* Celestial coordinate system */
int latax; /* Index of latitude axis in SkyFrame */
int lonax; /* Index of longitude axis in SkyFrame */
int ok; /* Do axis symbols conform to FITS-WCS CTYPE form? */
int old_ignore_used; /* Original setting of external ignore_used variable */
int ret; /* Returned flag */
/* Check the status. */
if( !astOK ) return 0;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this);
/* Check we have a SkyFrame. */
if( !IsASkyFrame( skyfrm ) ) return 0;
/* Initialise */
ret = 1;
/* Get the equinox, epoch of observation, and system of the SkyFrame. The epoch
is in TDB. It is assumed the Equinox is in UTC. */
eq = astGetEquinox( skyfrm );
sys = astGetC( skyfrm, "system" );
ep_tdb = astTestEpoch( skyfrm ) ? astGetEpoch( skyfrm ) : AST__BAD;
/* Convert the epoch to UTC. */
ep_utc = TDBConv( ep_tdb, AST__UTC, 1, method, class, status );
/* See if the FitsChan contains a value for the TIMESYS keyword (include
previously used cards in the search). If so, and if it is not UTC, convert
the epoch to the specified time scale, and store a TIMESYS value in the
FitsStore. */
old_ignore_used = ignore_used;
ignore_used = 0;
if( GetValue( this, "TIMESYS", AST__STRING, (void *) ×ys, 0, 0, method,
class, status ) && strcmp( timesys, "UTC" ) ) {
ep = TDBConv( ep_tdb, TimeSysToAst( this, timesys, method, class,
status ),
1, method, class, status );
SetItemC( &(store->timesys), 0, 0, s, timesys, status );
/* If no TIMESYS keyword was found in the FitsChan, or the timesys was
UTC, we use the UTC epoch value found above. In this case no TIMESYS value
need be stored in the FitsSTore since UTC is the default for TIMESYS. */
} else {
ep = ep_utc;
}
/* Reinstate the original value for the flag that indicates whether keywords
in the FitsChan that have been used previously should be ignored. */
ignore_used = old_ignore_used;
/* The MJD-OBS and DATE-OBS keywords default to the epoch of the
reference equinox if not supplied. Therefore MJD-OBS and DATE-OBS do
not need to be stored in the FitsChan if the epoch of observation is
the same as the epoch of the reference equinox. This can avoid
producing FITS headers which say unlikely things like
DATE-OBS = "01/01/50". Set a flag indicating if MJD-OBS and DATE-OBS
can be defaulted. */
defdate = EQUAL( ep_utc, eq );
/* Convert the equinox to a Julian or Besselian epoch. Also get the
reference frame and standard system. */
if( !Ustrcmp( sys, "FK4", status ) ){
eq = palEpb( eq );
isys = RADEC;
SetItemC( &(store->radesys), 0, 0, s, "FK4", status );
} else if( !Ustrcmp( sys, "FK4_NO_E", status ) || !Ustrcmp( sys, "FK4-NO-E", status ) ){
eq = palEpb( eq );
isys = RADEC;
SetItemC( &(store->radesys), 0, 0, s, "FK4-NO-E", status );
} else if( !Ustrcmp( sys, "FK5", status ) ){
eq = palEpj( eq );
isys = RADEC;
SetItemC( &(store->radesys), 0, 0, s, "FK5", status );
} else if( !Ustrcmp( sys, "ICRS", status ) ){
eq = AST__BAD;
isys = RADEC;
SetItemC( &(store->radesys), 0, 0, s, "ICRS", status );
} else if( !Ustrcmp( sys, "GAPPT", status ) ||
!Ustrcmp( sys, "Apparent", status ) ||
!Ustrcmp( sys, "Geocentric", status ) ){
eq = AST__BAD;
isys = RADEC;
SetItemC( &(store->radesys), 0, 0, s, "GAPPT", status );
} else if( !Ustrcmp( sys, "Helioecliptic", status ) ){
eq = AST__BAD;
isys = HECLIP;
} else if( !Ustrcmp( sys, "Galactic", status ) ){
eq = AST__BAD;
isys = GALAC;
} else if( !Ustrcmp( sys, "Supergalactic", status ) ){
eq = AST__BAD;
isys = SUPER;
} else if( !Ustrcmp( sys, "AzEl", status ) ){
eq = AST__BAD;
isys = AZEL;
} else {
eq = AST__BAD;
isys = NOCEL;
}
/* Store these values. Only store the date if it does not take its
default value. */
SetItem( &(store->equinox), 0, 0, s, eq, status );
if( !defdate ) SetItem( &(store->mjdobs), 0, 0, ' ', ep, status );
/* Only proceed if we have usable values */
if( astOK ) {
/* Get the indices of the latitude and longitude axes within the
SkyFrame. */
latax = astGetLatAxis( skyfrm );
lonax = 1 - latax;
/* The first 4 characters in CTYPE are determined by the celestial coordinate
system and the second 4 by the projection type. If we are describing
offset coordinates, then use "OFLN" and "OFLT. Otherwise use the
standard FITS-WCS name of the system. */
if( isoff > 0 ){
strcpy( lontype, "OFLN" );
strcpy( lattype, "OFLT" );
} else if( isys == RADEC ){
strcpy( lontype, "RA--" );
strcpy( lattype, "DEC-" );
} else if( isys == ECLIP ){
strcpy( lontype, "ELON" );
strcpy( lattype, "ELAT" );
} else if( isys == HECLIP ){
strcpy( lontype, "HLON" );
strcpy( lattype, "HLAT" );
} else if( isys == GALAC ){
strcpy( lontype, "GLON" );
strcpy( lattype, "GLAT" );
} else if( isys == SUPER ){
strcpy( lontype, "SLON" );
strcpy( lattype, "SLAT" );
} else if( isys == AZEL ){
strcpy( lontype, "AZ--" );
strcpy( lattype, "EL--" );
/* For unknown systems, use the axis symbols within CTYPE if they conform
to the requirement of FITS-WCS (i.e. "xxLN/xxLT" or "xLON/xLAT") or use
"UNLN/UNLT" otherwise. */
} else {
latsym = astGetSymbol( skyfrm, latax );
lonsym = astGetSymbol( skyfrm, lonax );
if( astOK ) {
ok = 0;
if( strlen( latsym ) == 4 && strlen( lonsym ) == 4 ) {
if( !strcmp( latsym + 2, "LT" ) &&
!strcmp( lonsym + 2, "LN" ) &&
!strncmp( latsym, lonsym, 2 ) ) {
ok = 1;
} else if( !strcmp( latsym + 1, "LAT" ) &&
!strcmp( lonsym + 1, "LON" ) &&
!strncmp( latsym, lonsym, 1 ) ) {
ok = 1;
}
}
if( !ok ) {
latsym = "UNLT";
lonsym = "UNLN";
}
strncpy( lontype, lonsym, 4 );
for( i = strlen( lonsym ); i < 4; i++ ) {
lontype[ i ] = '-';
}
strncpy( lattype, latsym, 4 );
for( i = strlen( latsym ); i < 4; i++ ) {
lattype[ i ] = '-';
}
}
}
/* Store the projection strings. */
prj_name = ( wcstype == 0 ) ? "-TAB" : astWcsPrjName( wcsproj );
if( astOK ) {
strcpy( lontype + 4, prj_name );
strcpy( lattype + 4, prj_name );
}
/* Store the total CTYPE strings */
SetItemC( &(store->ctype), axlon, 0, s, lontype, status );
SetItemC( &(store->ctype), axlat, 0, s, lattype, status );
/* Store offset coord information. */
if( isoff ) {
/* If the description is for offset coords store suitable comments for
the CTYPE keywords. */
if( isoff > 0 ) {
skyref = astGetC( skyfrm, "SkyRef" );
sprintf( attr, "Symbol(%d)", axlon + 1 );
sprintf( com, "%s offset from %s",astGetC( skyfrm, attr )+1, skyref );
SetItemC( &(store->ctype_com), axlon, 0, s, com, status );
sprintf( attr, "Symbol(%d)", axlat + 1 );
sprintf( com, "%s offset from %s",astGetC( skyfrm, attr )+1, skyref );
SetItemC( &(store->ctype_com), axlat, 0, s, com, status );
/* If the description is for absolute coords store the SkyFrame attribute
values in AST-specific keywords. */
} else {
sprintf( attr, "SkyRef(%d)", axlon + 1 );
skyref_lon = astGetD( skyfrm, attr );
sprintf( attr, "SkyRef(%d)", axlat + 1 );
skyref_lat = astGetD( skyfrm, attr );
sprintf( attr, "SkyRefP(%d)", axlon + 1 );
skyrefp_lon = astGetD( skyfrm, attr );
sprintf( attr, "SkyRefP(%d)", axlat + 1 );
skyrefp_lat = astGetD( skyfrm, attr );
skyrefis = (isoff < -2) ? "IGNORED" :
( (isoff < -1) ? "ORIGIN" : "POLE" );
SetItemC( &(store->skyrefis), 0, 0, s, skyrefis, status );
if( astTest( skyfrm, "SkyRef(1)" ) ) {
SetItem( &(store->skyref), axlon, 0, s, skyref_lon, status );
SetItem( &(store->skyref), axlat, 0, s, skyref_lat, status );
}
if( astTest( skyfrm, "SkyRefP(1)" ) ) {
SetItem( &(store->skyrefp), axlon, 0, s, skyrefp_lon, status );
SetItem( &(store->skyrefp), axlat, 0, s, skyrefp_lat, status );
}
}
}
/* If the Label attribute has been set for an axis, use it as the CTYPE
comment and CNAME value. */
if( astTestLabel( skyfrm, latax ) ) {
label = (char *) astGetLabel( skyfrm, latax );
SetItemC( &(store->ctype_com), axlat, 0, s, label, status );
SetItemC( &(store->cname), axlat, 0, s, label, status );
}
if( astTestLabel( skyfrm, lonax ) ) {
label = (char *) astGetLabel( skyfrm, lonax );
SetItemC( &(store->ctype_com), axlon, 0, s, label, status );
SetItemC( &(store->cname), axlon, 0, s, label, status );
}
/* Nullify any CUNITS strings for the longitude and latitude axes (they
always take the default value of degrees). */
SetItemC( &(store->cunit), axlat, 0, s, NULL, status );
SetItemC( &(store->cunit), axlon, 0, s, NULL, status );
}
/* Store the Domain name as the WCSNAME keyword (if set). */
if( astTestDomain( skyfrm ) ) {
SetItemC( &(store->wcsname), 0, 0, s, (char *) astGetDomain( skyfrm ), status );
}
/* Store the observer's position if set (needed for definition of AzEl
systems). */
if( astTestObsLon( skyfrm ) && astTestObsLat( skyfrm ) && s == ' ' ) {
geolon = astGetObsLon( skyfrm );
geolat = astGetObsLat( skyfrm );
h = astGetObsAlt( skyfrm );
if( geolat != AST__BAD && geolon != AST__BAD && h != AST__BAD ) {
eraGd2gc( 1, geolon, geolat, h, xyz );
SetItem( &(store->obsgeox), 0, 0, ' ', xyz[0], status );
SetItem( &(store->obsgeoy), 0, 0, ' ', xyz[1], status );
SetItem( &(store->obsgeoz), 0, 0, ' ', xyz[2], status );
}
}
if( !astOK ) ret = 0;
return ret;
}
static char *SourceWrap( const char *(* source)( void ), int *status ) {
/*
* Name:
* SourceWrap
* Purpose:
* Wrapper function to invoke a C FitsChan source function.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* char *SourceWrap( const char *, int *status(* source)( void ) )
* Class Membership:
* FitsChan 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 FitsChan
* 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 AstMapping *SpectralAxes( AstFitsChan *this, AstFrameSet *fs,
double *dim, int *wperm,
char s, FitsStore *store, double *crvals,
int *axis_done, const char *method,
const char *class, int *status ){
/*
* Name:
* SpectralAxes
* Purpose:
* Add values to a FitsStore describing spectral axes in a Frame.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstMapping *SpectralAxes( AstFitsChan *this, AstFrameSet *fs,
* double *dim, int *wperm,
* char s, FitsStore *store, double *crvals,
* int *axis_done, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* The current Frame of the supplied FrameSet is searched for spectral
* axes. If any are found, FITS WCS keyword values describing the axis
* are added to the supplied FitsStore, if possible (the conventions
* of FITS-WCS paper III are used). Note, this function does not store
* values for keywords which define the transformation from pixel
* coords to Intermediate World Coords (CRPIX, PC and CDELT), but a
* Mapping is returned which embodies these values. This Mapping is
* from the current Frame in the FrameSet (WCS coords) to a Frame
* representing IWC. The IWC Frame has the same number of axes as the
* WCS Frame which may be greater than the number of base Frame (i.e.
* pixel) axes.
*
* If a spectral axis is found, the RafRA and RefDec attributes of the
* SpecFrame describing the axis are ignored: it is assumed that the
* WCS Frame also contains a pair of celestial axes which will result
* in appropriate celestial reference values being stored in the
* FitsStore (this asumption should be enforced by calling function
* MakeFitsFrameSet prior to calling this function).
* Parameters:
* this
* Pointer to the FitsChan.
* fs
* Pointer to the FrameSet. The base Frame should represent FITS pixel
* coordinates, and the current Frame should represent FITS WCS
* coordinates. The number of base Frame axes should not exceed the
* number of current Frame axes. The spectral Unit in the returned
* FrameSet will always be linearly related to the default Units for
* the spectral System in use by the axis. If this requires a
* change to the existing spectral Unit, the integrity of the
* FrameSet will be maintained by suitable adjustments to the Mappings
* within the FrameSet.
* dim
* An array holding the image dimensions in pixels. AST__BAD can be
* supplied for any unknwon dimensions.
* wperm
* Pointer to an array of integers with one element for each axis of
* the current Frame. Each element holds the zero-based
* index of the FITS-WCS axis (i.e. one les than the value of "i" in
* the keyword names "CTYPEi", "CRVALi", etc) which describes the
* Frame axis.
* s
* The co-ordinate version character. A space means the primary
* axis descriptions. Otherwise the supplied character should be
* an upper case alphabetical character ('A' to 'Z').
* store
* The FitsStore in which to store the FITS WCS keyword values.
* crvals
* Pointer to an array holding the default CRVAL value for each
* axis in the WCS Frame.
* axis_done
* An array of flags, one for each Frame axis, which indicate if a
* description of the corresponding axis has yet been stored in the
* FitsStore.
* 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:
* If a spectral axis was found which can be described using the
* conventions of FITS-WCS paper III, then a Mapping from the current Frame
* of the supplied FrameSet, to the IWC Frame is returned. Otherwise,
* a UnitMap is returned. Note, the Mapping only defines the IWC
* transformation for spectral axes. Any non-spectral axes are passed
* unchanged by the returned Mapping.
*/
/* Local Variables: */
AstFitsTable *table; /* Pointer to structure holding -TAB table info */
AstFrame *pframe; /* Primary Frame containing current WCS axis*/
AstFrame *tfrm1; /* A temporary Frame */
AstFrame *tfrm; /* A temporary Frame */
AstFrame *wcsfrm; /* WCS Frame within FrameSet */
AstFrameSet *tfs; /* A temporary FrameSet */
AstGrismMap *gmap; /* GrismMap defining the spectral axis */
AstMapping *axmap; /* Mapping from WCS to IWC */
AstMapping *map; /* Pixel -> WCS mapping */
AstMapping *ret; /* Returned Mapping */
AstMapping *tmap0; /* A temporary Mapping */
AstMapping *tmap1; /* A temporary Mapping */
AstMapping *tmap2; /* A temporary Mapping */
AstMapping *tmap3; /* A temporary Mapping */
AstMapping *tmap4; /* A temporary Mapping */
AstMapping *tmap5; /* A temporary Mapping */
AstMapping *tmap6; /* A temporary Mapping */
AstPermMap *pm; /* PermMap pointer */
AstSpecFrame *specfrm; /* The SpecFrame defining current WCS axis */
char *cname; /* Pointer to CNAME value */
char ctype[ MXCTYPELEN ]; /* The value for the FITS CTYPE keyword */
char lin_unit[ 20 ]; /* Linear spectral Units being used */
char orig_system[ 40 ]; /* Value of System attribute for current WCS axis */
char system_attr[ 10 ]; /* Name of System attribute for current WCS axis */
char unit_attr[ 10 ]; /* Name of Unit attribute for current WCS axis */
const char *cval; /* Pointer to temporary character string */
const char *x_sys[ 4 ]; /* Basic spectral systems */
double *lbnd_p; /* Pointer to array of lower pixel bounds */
double *ubnd_p; /* Pointer to array of upper pixel bounds */
double crval; /* The value for the FITS CRVAL keyword */
double dgbyds; /* Rate of change of grism parameter wrt "S" at ref. point */
double dsbydx; /* Rate of change of "S" wrt "X" at ref. point */
double geolat; /* Geodetic latitude of observer (radians) */
double geolon; /* Geodetic longitude of observer (radians) */
double gval; /* Value of grism parameter at reference point */
double h; /* Geodetic altitude of observer (metres) */
double imagfreq; /* Image sideband equivalent to the rest frequency (Hz) */
double lbnd_s; /* Lower bound on spectral axis */
double pv; /* Value of projection parameter */
double restfreq; /* Rest frequency (Hz) */
double ubnd_s; /* Upper bound on spectral axis */
double vsource; /* Rel.vel. of source (m/s) */
double xval; /* Value of "X" system at reference point */
double xyz[3]; /* Geocentric position vector (in m) */
double zsource; /* Redshift of source */
int *inperm; /* Pointer to permutation array for input axes */
int *outperm; /* Pointer to permutation array for output axes */
int extver; /* Table version number for -TAB headers */
int fits_i; /* FITS WCS axis index for current WCS axis */
int iax; /* Axis index */
int icolindex; /* Index of table column holding index vector */
int icolmain; /* Index of table column holding main coord array */
int interp; /* INterpolation method for look-up tables */
int ix; /* System index */
int j; /* Loop count */
int npix; /* Number of pixel axes */
int nwcs; /* Number of WCS axes */
int paxis; /* Axis index within primary Frame */
int sourcevrf; /* Rest Frame in which SourceVel is accesed */
/* Initialise */
ret = NULL;
/* Check the inherited status. */
if( !astOK ) return ret;
/* Every supported spectral system is linearly related to one of the
following four systems. */
x_sys[ 0 ] = "FREQ";
x_sys[ 1 ] = "WAVE";
x_sys[ 2 ] = "AWAV";
x_sys[ 3 ] = "VELO";
/* Get a pointer to the WCS Frame. */
wcsfrm = astGetFrame( fs, AST__CURRENT );
/* Store the number of pixel and WCS axes. */
npix = astGetNin( fs );
nwcs = astGetNout( fs );
/* Store the upper and lower pixel bounds. */
lbnd_p = astMalloc( sizeof( double )*(size_t) npix );
ubnd_p = astMalloc( sizeof( double )*(size_t) npix );
if( astOK ) {
for( iax = 0; iax < npix; iax++ ) {
lbnd_p[ iax ] = 1.0;
ubnd_p[ iax ] = ( dim[ iax ] != AST__BAD ) ? dim[ iax ] : 500;
}
}
/* Check each axis in the WCS Frame to see if it is a spectral axis. */
axmap = NULL;
for( iax = 0; iax < nwcs; iax++ ) {
/* Obtain a pointer to the primary Frame containing the current WCS axis. */
astPrimaryFrame( wcsfrm, iax, &pframe, &paxis );
/* If the current axis belongs to a SpecFrame, we have found a spectral
axis. */
if( astIsASpecFrame( pframe ) ) {
specfrm = (AstSpecFrame *) pframe;
/* Note the (zero-based) FITS WCS axis index to be used for the current
Frame axis. */
fits_i = wperm[ iax ];
/* Note the name and original value of the System attribute for the spectral
axis within the FrameSet current Frame. */
sprintf( system_attr, "System(%d)", iax + 1 );
cval = astGetC( wcsfrm, system_attr );
if( cval ) strcpy( orig_system, cval );
/* Note the name of the Unit attribute for the spectral axis within the
FrameSet current Frame. */
sprintf( unit_attr, "Unit(%d)", iax + 1 );
/* Get a pointer to the Mapping from FITS pixel coordinates to SpecFrame. */
map = astGetMapping( fs, AST__BASE, AST__CURRENT );
/* Find the bounds of the Spectral axis over the volume of the pixel grid. */
astMapBox( map, lbnd_p, ubnd_p, 1, iax, &lbnd_s, &ubnd_s,
NULL, NULL );
/* The Unit attribute of a SpecFrame can be set to arbitrary non-linear
functions of standard linear spectral units. FITS-WCS paper III requires
CRVAL etc to be given in linear units. So first we ensure that we have a
SpecFrame with linear Units. Create a copy of the SpecFrame and clear
its Unit attribute (this ensures the copy has the default linear units).
Then find a Mapping from the original spectral units to the default
linear units. If the conversion is possible, see if the Mapping
between the units is linear. If it is, then the original Unit attribute
of the SpecFrame is OK (i.e. the units are linear). If not, clear
the Unit attribute of the spectral axis in the FrameSet so that it
uses the default linear units (retaining the original value so that it
can be re-instated later). Using the clear method on the FrameSet
pointer rather than the SpecFrame pointer causes the SpecFrame to be
re-mapped within the FrameSet to maintain its correct relationship with
the other Frames in the FrameSet. Also update the pixel->spectrum
Mapping to take account of the change in units and re-calculate the new
bounds on the spectral axis. Also update any supplied CRVAL value for
the spectral axis. */
tfrm = astCopy( specfrm );
astClearUnit( tfrm, 0 );
tfs = astConvert( specfrm, tfrm, "" );
tfrm = astAnnul( tfrm );
if( tfs ) {
crval = crvals ? crvals[ iax ] : AST__BAD;
tmap1 = astGetMapping( tfs, AST__BASE, AST__CURRENT );
tfs = astAnnul( tfs );
if( !IsMapLinear( tmap1, &lbnd_s, &ubnd_s, 0, status ) ) {
astClear( fs, unit_attr );
(void) astAnnul( map );
map = astGetMapping( fs, AST__BASE, AST__CURRENT );
astMapBox( map, lbnd_p, ubnd_p, 1, iax, &lbnd_s, &ubnd_s,
NULL, NULL );
astTran1( tmap1, 1, &crval, 1, &crval );
}
tmap1 = astAnnul( tmap1 );
/* Note the linear spectral Unit currently in use. */
cval = astGetUnit( specfrm, 0 );
if( cval ) strcpy( lin_unit, cval );
/* For some of the algorithms, the reference value CRVAL is arbitrary.
For these algorithms we choose to use the supplied default CRVAL value.
If no default CRVAL value was suppllied, we use the mid spectral value
if the size of the spectral axis was given, or the lower bound (i.e.
pixel 1) if the size of the spectral axis was not given. */
if( crval == AST__BAD ) {
if( dim[ iax ] != AST__BAD ) {
crval = 0.5*( lbnd_s + ubnd_s );
} else {
crval = lbnd_s;
}
}
/* Modify this crval value so that it correpsonds to an integer pixel
coordinate. */
crval = NearestPix( map, crval, iax, status );
/* We now check to see if the Mapping from pixel coords -> linear spectral
coords corresponds to one of the algorithms supported in FITS-WCS paper
III. First check for the "linear" algorithm in which the linear spectral
coordinate given by the SpecFrame is related linearly to the pixel
coords. */
ctype[ 0 ] = 0;
if( IsMapLinear( map, lbnd_p, ubnd_p, iax, status ) ) {
/* The CTYPE value is just the spectral system. */
strcpy( ctype, orig_system );
/* Create the Mapping which defines the spectral IWC axis. This is
initially the Mapping from WCS to IWCS - it subtracts the CRVAL value
from the spectral WCS value to get the spectral IWC value (other
non-spectral axes are left unchanged by this Mapping). This results
in the spectral IWC axis having the same axis index as the spectral
WCS axis. */
crval = -crval;
tmap0 = (AstMapping *) astShiftMap( 1, &crval, "", status );
crval = -crval;
axmap = AddUnitMaps( tmap0, iax, nwcs, status );
tmap0 = astAnnul( tmap0 );
}
/* If the "linear" algorithm above is inappropriate, see if the "non-linear"
algorithm defined in FITS-WCS paper III can be used, in which pixel
coords are linearly related to some spectral system (called "X") other
than the one represented by the supplied SpecFrame (called "S"). */
if( !ctype[ 0 ] ) {
/* Loop round each of the 4 allowed X systems. All other spectral systems
are linearly related to one of these 4 systems and so do not need to be
tested. */
for( ix = 0; ix < 4 && !ctype[ 0 ]; ix++ ) {
/* Set the system of the spectral WCS axis to the new trial X system. Clear
the Unit attribute to ensure we are using the default linear units.
Using the FrameSet pointer "fs" ensures that the Mappings within the
FrameSet are modified to maintain the correct inter-Frame relationships. */
astSetC( fs, system_attr, x_sys[ ix ] );
astClear( fs, unit_attr );
/* Now we check to see if the current X system is linearly related to
pixel coordinates. */
tmap3 = astGetMapping( fs, AST__BASE, AST__CURRENT );
if( IsMapLinear( tmap3, lbnd_p, ubnd_p, iax, status ) ) {
/* CTYPE: First 4 characters specify the "S" system. */
strcpy( ctype, orig_system );
/* The non-linear algorithm code to be appended to the "S" system is of the
form "-X2P" ("P" is the system which is linearly related to "S"). */
if( !strcmp( x_sys[ ix ], "FREQ" ) ) {
strcpy( ctype + 4, "-F2" );
} else if( !strcmp( x_sys[ ix ], "WAVE" ) ) {
strcpy( ctype + 4, "-W2" );
} else if( !strcmp( x_sys[ ix ], "AWAV" ) ) {
strcpy( ctype + 4, "-A2" );
} else {
strcpy( ctype + 4, "-V2" );
}
if( !strcmp( orig_system, "FREQ" ) ||
!strcmp( orig_system, "ENER" ) ||
!strcmp( orig_system, "WAVN" ) ||
!strcmp( orig_system, "VRAD" ) ) {
strcpy( ctype + 7, "F" );
} else if( !strcmp( orig_system, "WAVE" ) ||
!strcmp( orig_system, "VOPT" ) ||
!strcmp( orig_system, "ZOPT" ) ) {
strcpy( ctype + 7, "W" );
} else if( !strcmp( orig_system, "AWAV" ) ) {
strcpy( ctype + 7, "A" );
} else {
strcpy( ctype + 7, "V" );
}
/* Create a Mapping which gives S as a function of X. */
tfrm = astCopy( specfrm );
astSetC( tfrm, "System(1)", orig_system );
astSetC( tfrm, "Unit(1)", lin_unit );
tfs = astConvert( specfrm, tfrm, "" );
tmap5 = astGetMapping( tfs, AST__BASE, AST__CURRENT );
tfs = astAnnul( tfs );
tfrm = astAnnul( tfrm );
/* Use the inverse of this Mapping to get the X value at the reference S
value. */
astTran1( tmap5, 1, &crval, 0, &xval );
/* Also use it to get the rate of change of S with respect to X at the
reference point. */
dsbydx = astRate( tmap5, &xval, 0, 0 );
/* Create the Mapping which defines the spectral IWC axis. This is the
Mapping from WCS to IWC - it first converts from S to X, then subtracts
the X reference value value, and then scales the axis to ensure that
the rate of change of S with respect to IWC is unity (as required by
FITS-WCS paper III). Other non-spectral axes are left unchanged by
the Mapping. The spectral IWC axis has the same axis index as the
spectral WCS axis. */
xval = -xval;
tmap2 = (AstMapping *) astShiftMap( 1, &xval, "", status );
astInvert( tmap5 );
tmap0 = (AstMapping *) astCmpMap( tmap5, tmap2, 1, "", status );
tmap5 = astAnnul( tmap5 );
tmap2 = astAnnul( tmap2 );
tmap2 = (AstMapping *) astZoomMap( 1, dsbydx, "", status );
tmap1 = (AstMapping *) astCmpMap( tmap0, tmap2, 1, "", status );
tmap0 = astAnnul( tmap0 );
tmap2 = astAnnul( tmap2 );
axmap = AddUnitMaps( tmap1, iax, nwcs, status );
tmap1 = astAnnul( tmap1 );
}
tmap3 = astAnnul( tmap3 );
/* Re-instate the original system and unit attributes for the spectral axis. */
astSetC( fs, system_attr, orig_system );
astSetC( fs, unit_attr, lin_unit );
}
}
/* If the "non-linear" algorithm above is inappropriate, see if the
"log-linear" algorithm defined in FITS-WCS paper III can be used, in
which the spectral axis is logarithmically spaced in the spectral
system given by the SpecFrame. */
if( !ctype[ 0 ] ) {
/* If the "log-linear" algorithm is appropriate, the supplied SpecFrame (s)
is related to pixel coordinate (p) by s = Sr.EXP( a*p - b ). If this
is the case, then the log of s will be linearly related to pixel
coordinates. Test this. If the test is passed a Mapping is returned from
WCS to IWC. */
axmap = LogAxis( map, iax, nwcs, lbnd_p, ubnd_p, crval, status );
/* If the axis is logarithmic... */
if( axmap ) {
/* CTYPE: First 4 characters specify the "S" system. */
strcpy( ctype, orig_system );
/* The rest is "-LOG". */
strcpy( ctype + 4, "-LOG" );
}
}
/* If the "log-linear" algorithm above is inappropriate, see if the "grism"
algorithm defined in FITS-WCS paper III can be used, in which pixel
coords are related to wavelength using a grism dispersion function,
implemented in AST by a GrismMap. GrismMaps produce either vacuum
wavelength or air wavelength as output. Temporarily set the SpecFrame
to these two systems in turn before we do the check for a GrismMap. */
for( ix = 0; ix < 2 && !ctype[ 0 ]; ix++ ) {
astSetC( fs, system_attr, ( ix == 0 ) ? "WAVE" : "AWAV" );
astSetC( fs, unit_attr, "m" );
/* Get the simplified Mapping from pixel to wavelength. If the Mapping is
a CmpMap containing a GrismMap, and if the output of the GrismMap is
scaled by a neighbouring ZoomMap (e.g. into different wavelength units),
then the GrismMap will be modified to incorporate the effect of the
ZoomMap, and the ZoomMap will be removed. */
tmap2 = astGetMapping( fs, AST__BASE, AST__CURRENT );
tmap1 = astSimplify( tmap2 );
tmap2 = astAnnul( tmap2 );
/* Analyse this Mapping to see if the iax'th output is created diretcly by a
GrismMap (i.e. the output of theGrismMap must not subsequently be
modified by some other Mapping). If so, ExtractGrismMap returns a pointer
to the GrismMap as its function value, and also returns "tmap2" as a copy
of tmap1 in which the GrismMap has been replaced by a UnitMap. */
gmap = ExtractGrismMap( tmap1, iax, &tmap2, status );
if( gmap ) {
/* The Mapping without the GrismMap must be linear on the spectral axis. */
if( IsMapLinear( tmap2, lbnd_p, ubnd_p, iax, status ) ) {
/* Get the reference wavelength (in "m") stored in the GrismMap. */
crval = astGetGrismWaveR( gmap );
/* Save a copy of the current Wavelength (in "m") SpecFrame. */
tfrm1 = astCopy( specfrm );
/* Re-instate the original System and Unit attributes for the SpecFrame. */
astSetC( fs, system_attr, orig_system );
astSetC( fs, unit_attr, lin_unit );
/* Find the Mapping from the original "S" system to wavelength (in "m"). */
tfs = astConvert( specfrm, tfrm1, "" );
tfrm1 = astAnnul( tfrm1 );
if( tfs ) {
tmap3 = astGetMapping( tfs, AST__BASE, AST__CURRENT );
tfs = astAnnul( tfs );
/* Use the inverse of this Mapping to convert the reference value from
wavelength to the "S" system. */
astTran1( tmap3, 1, &crval, 0, &crval );
/* Concatenate the "S"->wavelength Mapping with the inverse GrismMap (from
wavelength to grism parameter), to get the "S" -> "grism parameter"
Mapping. */
astInvert( gmap );
tmap4 = (AstMapping *) astCmpMap( tmap3, gmap, 1, "", status );
tmap3 = astAnnul( tmap3 );
/* Use this Mapping to find the grism parameter at the reference point. */
astTran1( tmap4, 1, &crval, 1, &gval );
/* Also use it to find the rate of change of grism parameter with respect
to "S" at the reference point. */
dgbyds = astRate( tmap4, &crval, 0, 0 );
/* FITS-WCS paper III required ds/dw to be unity at the reference point.
Therefore the rate of change of grism parameter with respect to IWC ("w")
is equal to the rate of change of grism parameter with respect to "S"
(at the reference point). The mapping from "w" to grism parameter is a
ZoomMap which scales "w" by "dgbyds" followed by a ShiftMap which adds
on "gval". */
tmap5 = (AstMapping *) astZoomMap( 1, dgbyds, "", status );
tmap6 = (AstMapping *) astShiftMap( 1, &gval, "", status );
tmap3 = (AstMapping *) astCmpMap( tmap5, tmap6, 1, "", status );
tmap5 = astAnnul( tmap5 );
tmap6 = astAnnul( tmap6 );
/* Create the Mapping which defines the spectral IWC axis. This is the
Mapping from WCS "S" to IWCS "w", formed by combining the Mapping from
"S" to grism parameter (tmap4), with the Mapping from grism parameter to
"w" (inverse of tmap3). Other non-spectral axes are left unchanged by the
Mapping. The spectral IWC axis has the same axis index as the spectral
WCS axis. */
astInvert( tmap3 );
tmap5 = (AstMapping *) astCmpMap( tmap4, tmap3, 1, "", status );
tmap3 = astAnnul( tmap3 );
tmap4 = astAnnul( tmap4 );
axmap = AddUnitMaps( tmap5, iax, nwcs, status );
tmap5 = astAnnul( tmap5 );
/* CTYPE: First 4 characters specify the "S" system. */
strcpy( ctype, orig_system );
/* Last 4 characters are "-GRA" or "-GRI". */
strcpy( ctype + 4, ( ix == 0 ) ? "-GRI" : "-GRA" );
/* Store values for the projection parameters in the FitsStore. Ignore
parameters which are set to the default values defined in FITS-WCS
paper III. */
pv = astGetGrismG( gmap );
if( pv != 0 ) SetItem( &(store->pv), fits_i, 0, s, pv, status );
pv = (double) astGetGrismM( gmap );
if( pv != 0 ) SetItem( &(store->pv), fits_i, 1, s, pv, status );
pv = astGetGrismAlpha( gmap );
if( pv != 0 ) SetItem( &(store->pv), fits_i, 2, s, pv*AST__DR2D, status );
pv = astGetGrismNR( gmap );
if( pv != 1.0 ) SetItem( &(store->pv), fits_i, 3, s, pv, status );
pv = astGetGrismNRP( gmap );
if( pv != 0 ) SetItem( &(store->pv), fits_i, 4, s, pv, status );
pv = astGetGrismEps( gmap );
if( pv != 0 ) SetItem( &(store->pv), fits_i, 5, s, pv*AST__DR2D, status );
pv = astGetGrismTheta( gmap );
if( pv != 0 ) SetItem( &(store->pv), fits_i, 6, s, pv*AST__DR2D, status );
}
}
/* Release resources. */
tmap2 = astAnnul( tmap2 );
gmap = astAnnul( gmap );
}
/* Release resources. */
tmap1 = astAnnul( tmap1 );
/* Re-instate the original System and Unit attributes for the SpecFrame. */
astSetC( fs, system_attr, orig_system );
astSetC( fs, unit_attr, lin_unit );
}
/* If none of the above algorithms are appropriate, we must resort to
using the -TAB algorithm, in which the Mapping is defined by a look-up
table. Check the TabOK attribute to see -TAB is to be supported. */
extver = astGetTabOK( this );
if( !ctype[ 0 ] && extver > 0 ) {
/* Get any pre-existing FitsTable from the FitsStore. This is the table
in which the tabular data will be stored (if the Mapping can be expressed
in -TAB form). */
if( !astMapGet0A( store->tables, AST_TABEXTNAME, &table ) ) table = NULL;
/* See if the Mapping can be expressed in -TAB form. */
tmap0 = IsMapTab1D( map, 1.0, NULL, wcsfrm, dim, iax, fits_i, &table,
&icolmain, &icolindex, &interp, status );
if( tmap0 ) {
/* CTYPE: First 4 characters specify the "S" system. Last 4 characters are
"-TAB". */
strcpy( ctype, orig_system );
strcpy( ctype + 4, "-TAB" );
/* The values stored in the table index vector are GRID coords. So we
need to ensure that IWC are equivalent to GRID coords. So set CRVAL
to zero. First store the original CRVAL value (which gives the
observation centre) in AXREF. */
SetItem( &(store->axref), fits_i, 0, s, crval, status );
crval = 0.0;
/* Store TAB-specific values in the FitsStore. First the name of the
FITS binary table extension holding the coordinate info. */
SetItemC( &(store->ps), fits_i, 0, s, AST_TABEXTNAME, status );
/* Next the table version number. This is the set (positive) value for the
TabOK attribute. */
SetItem( &(store->pv), fits_i, 1, s, extver, status );
/* Also store the table version in the binary table header. */
astSetFitsI( table->header, "EXTVER", extver,
"Table version number", 0 );
/* Next the name of the table column containing the main coords array. */
SetItemC( &(store->ps), fits_i, 1, s,
astColumnName( table, icolmain ), status );
/* Next the name of the column containing the index array */
if( icolindex >= 0 ) SetItemC( &(store->ps), fits_i, 2, s,
astColumnName( table, icolindex ), status );
/* The interpolation method (an AST extension to the published -TAB
algorithm, communicated through the QVi_4a keyword). */
SetItem( &(store->pv), fits_i, 4, s, interp, status );
/* Also store the FitsTable itself in the FitsStore. */
astMapPut0A( store->tables, AST_TABEXTNAME, table, NULL );
/* Create the WCS -> IWC Mapping (AST uses grid coords as IWC coords for
the -TAB algorithm). First, get a Mapping that combines the TAB axis
Mapping( tmap0) in parallel with one or two UnitMaps in order to put
the TAB axis at the required index. */
tmap1 = AddUnitMaps( tmap0, iax, nwcs, status );
/* Now get a PermMap that permutes the WCS axes into the FITS axis order. */
inperm = astMalloc( sizeof( double )*nwcs );
outperm = astMalloc( sizeof( double )*nwcs );
if( astOK ) {
for( j = 0; j < nwcs; j++ ) {
inperm[ j ] = wperm[ j ];
outperm[ wperm[ j ] ] = j;
}
}
pm = astPermMap( nwcs, inperm, nwcs, outperm, NULL, "",
status );
/* Combine these two Mappings in series, to get the Mapping from WCS to
IWC. */
axmap = (AstMapping *) astCmpMap( pm, tmap1, 1, " ",
status );
/* Free resources. */
inperm = astFree( inperm );
outperm = astFree( outperm );
pm = astAnnul( pm );
tmap0 = astAnnul( tmap0 );
tmap1 = astAnnul( tmap1 );
}
if( table ) table = astAnnul( table );
}
/* If this axis is a usable spectral axis... */
if( ctype[ 0 ] ) {
/* Add the Mapping for this axis in series with any existing result Mapping. */
if( ret ) {
tmap0 = (AstMapping *) astCmpMap( ret, axmap, 1, "", status );
(void) astAnnul( ret );
ret = tmap0;
} else {
ret = astClone( axmap );
}
axmap = astAnnul( axmap );
/* Store values for CTYPE, CRVAL and CUNIT in the FitsStore. */
SetItemC( &(store->ctype), fits_i, 0, s, ctype, status );
SetItem( &(store->crval), fits_i, 0, s, crval, status );
SetItemC( &(store->cunit), fits_i, 0, s, lin_unit, status );
/* If the axis label has been set, use it as the CTYPE comment and CNAME
value. */
if( astTestLabel( specfrm, 0 ) ) {
cname = (char *) astGetLabel( specfrm, 0 );
SetItemC( &(store->ctype_com), fits_i, 0, s, cname, status );
SetItemC( &(store->cname), fits_i, 0, s, cname, status );
}
/* Store values for the other FITS-WCS keywords which describe the
spectral system. Only store values which have been explicitly set in
the SpecFrame, which are different to the default values defined by
FITS-WCS paper III (if any), and which are not bad. */
if( astTestObsLon( specfrm ) && astTestObsLat( specfrm ) &&
s == ' ' ) {
geolon = astGetObsLon( specfrm );
geolat = astGetObsLat( specfrm );
h = astGetObsAlt( specfrm );
if( geolat != AST__BAD && geolon != AST__BAD && h != AST__BAD ) {
eraGd2gc( 1, geolon, geolat, h, xyz );
SetItem( &(store->obsgeox), 0, 0, ' ', xyz[0], status );
SetItem( &(store->obsgeoy), 0, 0, ' ', xyz[1], status );
SetItem( &(store->obsgeoz), 0, 0, ' ', xyz[2], status );
}
}
if( astTestRestFreq( specfrm ) ) {
restfreq = astGetRestFreq( specfrm );
if( restfreq != AST__BAD ) {
if( !strcmp( orig_system, "WAVE" ) ||
!strcmp( orig_system, "VOPT" ) ||
!strcmp( orig_system, "ZOPT" ) ||
!strcmp( orig_system, "AWAV" ) ) {
SetItem( &(store->restwav), 0, 0, s, AST__C/restfreq, status );
} else {
SetItem( &(store->restfrq), 0, 0, s, restfreq, status );
}
}
if( astIsADSBSpecFrame( specfrm ) ) {
imagfreq = astGetImagFreq( (AstDSBSpecFrame *) specfrm );
if( imagfreq != AST__BAD ) {
SetItem( &(store->imagfreq), 0, 0, s, imagfreq, status );
}
}
}
cval = GetFitsSor( astGetC( specfrm, "StdOfRest" ), status );
if( cval ) SetItemC( &(store->specsys), 0, 0, s, cval, status );
if( astTestSourceVel( specfrm ) ) {
vsource = astGetSourceVel( specfrm );
if( vsource != AST__BAD && fabs( vsource ) < AST__C ) {
zsource = sqrt( (AST__C + vsource)/
(AST__C - vsource) ) - 1.0;
SetItem( &(store->zsource), 0, 0, s, zsource, status );
cval = GetFitsSor( astGetC( specfrm, "SourceVRF" ), status );
if( cval ) SetItemC( &(store->ssyssrc), 0, 0, s, cval, status );
}
} else {
vsource = AST__BAD;
}
/* Store the VELOSYS value (not strictly needed since it can be
determined from the other values, but FITS-WCS paper III says it can be
useful). We temporarily change the source velocity to be zero m/s
in the main rest frame (StdOfRest) (unless the main rest frame is
already the source rest frame). We then change the source rest
frame to topocentric and get the source velocity (i.e. the velocity of
the main rest Frame) in the topocentric system. We then re-instate the
original attribute values if they were set. */
if( astGetStdOfRest( specfrm ) != AST__SCSOR ) {
sourcevrf = astGetSourceVRF( specfrm );
astSetSourceVRF( specfrm, astGetStdOfRest( specfrm ) );
astSetSourceVel( specfrm, 0.0 );
} else {
vsource = AST__BAD;
sourcevrf = AST__NOSOR;
}
astSetSourceVRF( specfrm, AST__TPSOR );
SetItem( &(store->velosys), 0, 0, s,
astGetSourceVel( specfrm ), status );
if( vsource != AST__BAD ){
astSetSourceVRF( specfrm, sourcevrf );
astSetSourceVel( specfrm, vsource );
}
/* Indicate that this axis has been described. */
axis_done[ iax ] = 1;
}
/* Release resources. */
map = astAnnul( map );
}
}
pframe = astAnnul( pframe );
}
/* Release resources. */
lbnd_p = astFree( lbnd_p );
ubnd_p = astFree( ubnd_p );
wcsfrm = astAnnul( wcsfrm );
/* If we have a Mapping to return, simplify it. Otherwise, create
a UnitMap to return. */
if( ret ) {
tmap0 = ret;
ret = astSimplify( tmap0 );
tmap0 = astAnnul( tmap0 );
} else {
ret = (AstMapping *) astUnitMap( nwcs, "", status );
}
/* Return the result. */
return ret;
}
static AstFitsChan *SpecTrans( AstFitsChan *this, int encoding,
const char *method, const char *class, int *status ){
/*
* Name:
* SpecTrans
* Purpose:
* Translated non-standard WCS FITS headers into equivalent standard
* ones.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstFitsChan *SpecTrans( AstFitsChan *this, int encoding,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function checks the supplied FitsChan for selected
* non-standard WCS keywords and, if found, stores equivalent
* standard keywords in a newly created FitsChan which is returned as
* the function value. All the original keywords are marked
* as having been used, so that they are not written out when the
* FitsChan is deleted.
*
* At the moment, the non-standard keywords checked for are:
*
* 1) RADECSYS is renamed as RADESYS
*
* 2) LONGPOLE is renamed as LONPOLE
*
* 3) CDjjjiii and CDj_i are converted to PCi_j (with unit CDELT)
*
* 4) CROTAj are converted to PCi_j
*
* 5) PROJPi are converted to PV_i
*
* 6) CmVALi are converted to CRVALis (s=A,B,,, for m=1,2...). This
* is also done for CmPIXi, CmYPEi, and CmNITi. CmELTi is converted
* to a CDj_is array.
*
* 7) EQUINOX keywords with string values equal to a date preceded
* by the letter B or J (eg "B1995.0"). These are converted to the
* corresponding Julian floating point value without any epoch
* specifier.
*
* 8) EPOCH values are converted into Julian EQUINOX values (but only
* if the FitsChan does not already contain an EQUINOX value).
*
* 9) DATE-OBS values are converted into MJD-OBS values (but only
* if the FitsChan does not already contain an MJD-OBS value).
*
* 10) EQUINOX or EPOCH keywords with value zero are converted to
* B1950.
*
* 11) The AIPS NCP and GLS projections are converted into equivalent SIN
* or SFL projections.
*
* 12) The IRAF "ZPX" projection. If the last 4 chacaters of CTYPEi
* (i = 1, naxis) are "-ZPX", then:
* - "ZPX" is replaced by "ZPN" within the CTYPEi value
* - A distortion code of "-ZPX" is appended to the end of the CTYPEi
* value (this is used later by the DistortMaps function).
* - If the FitsChan contains no PROJP keywords, then projection
* parameter valus are read from any WATi_nnn keywords, and
* corresponding PV keywords are added to the FitsChan.
*
* 13) The IRAF "TNX" projection. If the last 4 chacaters of CTYPEi
* (i = 1, naxis) are "-TNX", then:
* - "TNX" is replaced by "TAN" within the CTYPEi value (the distorted
* TAN projection included in a pre-final version of FITS-WCS is still
* supported by AST using the WcsMap AST__TPN projection).
* - If the FitsChan contains no PROJP keywords, then projection
* parameter valus are read from any WATi_nnn keywords, and
* corresponding PV keywords are added to the FitsChan.
* - If the TNX projection cannot be converted exactly into a TAN
* projection, ASTWARN keywords are added to the FitsChan
* containing a warning message. The calling application can (if it
* wants to) check for this keyword, and report its contents to the
* user.
*
* 14) Keywords relating to the IRAF "mini-WCS" system are removed.
* This is the IRAF equivalent of the AST native encoding. Mini-WCS
* keywords are removed in order to avoid confusion arising between
* potentially inconsistent encodings.
*
* 15) "QV" parameters for TAN projections (as produced by AUTOASTROM)
* or "-TAB" (as produced by FitsChan) are renamed to "PV".
*
* 16) RESTFREQ is converted to RESTFRQ.
*
* 17) the "-WAV", "-FRQ" and "-VEL" CTYPE algorithms included in an
* early draft of FITS-WCS paper III are translated to the
* corresponding modern "-X2P" form.
*
* 18) AIPS spectral CTYPE values are translated to FITS-WCS paper III
* equivalents.
*
* 19) AIPS spectral keywords OBSRA and OBSDEC are used to create a
* pair of celestial axes with reference point at the specified
* (OBSRA,OBSDEC) position. This is only done if the header does not
* already contain a pair of celestial axes.
*
* 20) Common case insensitive CUNIT values: "Hz", "Angstrom", "km/s",
* "M/S"
*
* 21) Various translations specific to the FITS-CLASS encoding.
*
* 22) SAO distorted TAN projections (uses COi_j keywords to store
* polynomial coefficients) are converted to TPN projections.
* 23) CTYPE == "LAMBDA" changed to CTYPE = "WAVE"
*
* 24) if the projection is TAN and the PolyTan attribute is non-zero,
* or if the projection is TPV (produced by SCAMP), the projection is
* changed to TPN (the AST code for the draft FITS-WCS paper II
* conventions for a distorted TAN projection).
* Parameters:
* this
* Pointer to the FitsChan.
* encoding
* The FitsChan encoding in use.
* method
* Pointer to string holding name of calling method.
* class
* Pointer to a string holding the name of the supplied object class.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the new FitsChan containing the keywords which
* constitute the standard equivalents to any non-standard keywords in
* the supplied FitsChan. A NULL pointer is returned if there are no
* non-standard keywords in the supplied FitsChan.
*/
/* Local Variables: */
AstFitsChan *ret; /* The returned FitsChan */
char *assys; /* AIPS standad of rest type */
char *astype; /* AIPS spectral type */
char *comm; /* Pointer to comment string */
char *cval; /* Pointer to character string */
char *start; /* Pointer to start of projp term */
char *watmem; /* Pointer to total WAT string */
char bj; /* Besselian/Julian indicator */
char format[ 50 ]; /* scanf format string */
char keyname[ FITSNAMLEN + 5 ];/* General keyword name + formats */
char lattype[MXCTYPELEN]; /* CTYPE value for latitude axis */
char lontype[MXCTYPELEN]; /* CTYPE value for longitude axis */
char prj[6]; /* Spatial projection string */
char s; /* Co-ordinate version character */
char spectype[MXCTYPELEN]; /* CTYPE value for spectral axis */
char sprj[6]; /* Spectral projection string */
char ss; /* Co-ordinate version character */
char template[ FITSNAMLEN + 1 ];/* General keyword name template */
double *cvals; /* PVi_m values for TPN projection */
double cdelti; /* CDELT for longitude axis */
double cdeltj; /* CDELT for latitude axis */
double cosrota; /* Cos( CROTA ) */
double crota; /* CROTA Value */
double dval; /* General floating value */
double lambda; /* Ratio of CDELTs */
double projp; /* Projection parameter value */
double rowsum2; /* Sum of squared CDi_j row elements */
double sinrota; /* Sin( CROTA ) */
double sinval; /* Sin( dec ref ) */
int *mvals; /* "m" index of each PVi_m value */
int axlat; /* Index of latitude axis */
int axlon; /* Index of longitude axis */
int diag; /* Sign of diagonal CDi_j element */
int dim; /* Length of pixel axis */
int gotpcij; /* Does FitsChan contain any PCi_j keywords? */
int i,j; /* Indices */
int iaxis; /* Axis index */
int icoeff; /* Index of next PVi_m value */
int iproj; /* Projection parameter index */
int jhi; /* Highest axis index */
int jlo; /* Lowest axis index */
int lbnd[ 2 ]; /* Lower index bounds */
int m; /* Co-ordinate version index */
int naxis; /* Number of axes */
int nc; /* Length of string */
int ncoeff; /* Number of PVi_m values */
int ok; /* Can projection be represented in FITS-WCS?*/
int shifted; /* Non-zero if there is an origin shift */
int tlbnd[ 2 ]; /* Lower index bounds */
int tubnd[ 2 ]; /* Upper index bounds */
int ubnd[ 2 ]; /* Upper index bounds */
int use_projp; /* Use PROJP keywors in favour of PV keywords? */
size_t size; /* Length of string value */
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Initialise to avoid compiler warnings. */
size = 0;
prj[ 0 ] = 0;
/* Create the returned FitsChan. */
ret = astFitsChan( NULL, NULL, "", status );
/* Loop round all axis descriptions, starting with primary (' '). */
for( s = 'A' - 1; s <= 'Z' && astOK; s++ ){
if( s == 'A' - 1 ) s = ' ';
/* Find the number of axes by finding the highest axis number in any
CRPIXi keyword name. Pass on if there are no axes for this axis
description. */
if( s != ' ' ) {
sprintf( template, "CRPIX%%d%c", s );
} else {
strcpy( template, "CRPIX%d" );
}
if( !astKeyFields( this, template, 1, &naxis, lbnd ) ) {
if( s == ' ' ) s = 'A' - 1;
continue;
}
/* Find the longitude and latitude axes by examining the CTYPE values.
They are marked as read. Such markings are only provisional, and they
can be read again any number of times until the current astRead
operation is completed. Also note the projection type. */
j = 0;
axlon = -1;
axlat = -1;
while( j < naxis && astOK ){
if( GetValue2( ret, this, FormatKey( "CTYPE", j + 1, -1, s, status ),
AST__STRING, (void *) &cval, 0, method,
class, status ) ){
nc = strlen( cval );
if( !strncmp( cval, "RA--", 4 ) ||
!strncmp( cval, "AZ--", 4 ) ||
( nc > 1 && !strncmp( cval + 1, "LON", 3 ) ) ||
( nc > 2 && !strncmp( cval + 2, "LN", 2 ) ) ) {
axlon = j;
strncpy( prj, cval + 4, 4 );
strncpy( lontype, cval, 10 );
prj[ 4 ] = 0;
} else if( !strncmp( cval, "DEC-", 4 ) ||
!strncmp( cval, "EL--", 4 ) ||
( nc > 1 && !strncmp( cval + 1, "LAT", 3 ) ) ||
( nc > 2 && !strncmp( cval + 2, "LT", 2 ) ) ) {
axlat = j;
strncpy( prj, cval + 4, 4 );
strncpy( lattype, cval, 10 );
prj[ 4 ] = 0;
/* Check for spectral algorithms from early drafts of paper III */
} else {
sprj[ 0 ] = '-';
if( ( nc > 4 && !strncmp( cval + 4, "-WAV", 4 ) ) ) {
sprj[ 1 ] = 'W';
} else if( ( nc > 4 && !strncmp( cval + 4, "-FRQ", 4 ) ) ) {
sprj[ 1 ] = 'F';
} else if( ( nc > 4 && !strncmp( cval + 4, "-VEL", 4 ) ) ) {
sprj[ 1 ] = 'V';
} else {
sprj[ 0 ] = 0;
}
if( *sprj ) {
sprj[ 2 ] = '2';
if( !strncmp( cval, "WAVE", 4 ) ) {
sprj[ 3 ] = 'W';
} else if( !strncmp( cval, "FREQ", 4 ) ) {
sprj[ 3 ] = 'F';
} else if( !strncmp( cval, "VELO", 4 ) ) {
sprj[ 3 ] = 'V';
} else if( !strncmp( cval, "VRAD", 4 ) ) {
sprj[ 3 ] = 'F';
} else if( !strncmp( cval, "VOPT", 4 ) ) {
sprj[ 3 ] = 'W';
} else if( !strncmp( cval, "ZOPT", 4 ) ) {
sprj[ 3 ] = 'W';
} else if( !strncmp( cval, "ENER", 4 ) ) {
sprj[ 3 ] = 'F';
} else if( !strncmp( cval, "WAVN", 4 ) ) {
sprj[ 3 ] = 'F';
} else if( !strncmp( cval, "BETA", 4 ) ) {
sprj[ 3 ] = 'V';
} else {
sprj[ 0 ] = 0;
}
}
if( *sprj ) {
strcpy( spectype, cval );
if( sprj[ 1 ] == sprj[ 3 ] ) {
strcpy( sprj, strlen( cval ) > 8 ? "----" : " " );
} else {
sprj[ 4 ] = 0;
}
strncpy( spectype + 4, sprj, 4 );
cval = spectype;
SetValue( ret, FormatKey( "CTYPE", j + 1, -1, s, status ),
(void *) &cval, AST__STRING, NULL, status );
}
}
j++;
} else {
break;
}
}
/* RADECSYS keywords
----------------- */
if( s == ' ' ) {
if( GetValue2( ret, this, "RADECSYS", AST__STRING, (void *) &cval, 0, method,
class, status ) ){
if( encoding == FITSPC_ENCODING || encoding == FITSIRAF_ENCODING ){
SetValue( ret, "RADESYS", (void *) &cval, AST__STRING,
CardComm( this, status ), status );
}
}
/* LONGPOLE keywords
----------------- */
if( GetValue2( ret, this, "LONGPOLE", AST__FLOAT, (void *) &dval, 0, method,
class, status ) ){
if( encoding == FITSPC_ENCODING || encoding == FITSIRAF_ENCODING ){
SetValue( ret, "LONPOLE", (void *) &dval, AST__FLOAT,
CardComm( this, status ), status );
}
}
}
/* Zero CDELT values.
----------------- */
/* Check there are some CDELT keywords... */
if( s != ' ' ) {
sprintf( template, "CDELT%%d%c", s );
} else {
strcpy( template, "CDELT%d" );
}
if( astKeyFields( this, template, 0, NULL, NULL ) ){
/* Do each row in the matrix. */
for( j = 0; j < naxis; j++ ){
/* Get the CDELT value for this row. */
GetValue2( ret, this, FormatKey( "CDELT", j + 1, -1, s, status ), AST__FLOAT,
(void *) &cdeltj, 0, method, class, status );
/* If CDELT is zero, use 1.0E-6 of the corresponding CRVAL value
instead, or 1.0 if CRVAL is zero. Otherwise, the zeros could cause the
matrix to be non-invertable. The Mapping could then not be simplified
or used by a Plot. CDELT values of zero are usually used to indicate
"redundant" axes. For instance, a 2D image may be stored as a 3D cube
with a single plane with the "redundant" 3rd axis used to specify the
wavelength of the filter. The actual value used for CDELT shouldn't
matter since the axis only spans a single pixel anyway. */
if( cdeltj == 0.0 ){
GetValue2( ret, this, FormatKey( "CDELT", j + 1, -1, s, status ), AST__FLOAT,
(void *) &dval, 1, method, class, status );
cdeltj = 1.0E-6*dval;
if( cdeltj == 0.0 ) cdeltj = 1.0;
SetValue( ret, FormatKey( "CDELT", j + 1, -1, s, status ), (void *) &cdeltj,
AST__FLOAT, NULL, status );
}
}
}
/* Following conversions produce PCi_j keywords. Only do them if there
are currently no PCi_j keywords in the header. */
if( s != ' ' ) {
sprintf( template, "PC%%d_%%d%c", s );
} else {
strcpy( template, "PC%d_%d" );
}
gotpcij = astKeyFields( this, template, 0, NULL, NULL );
if( !gotpcij ){
/* CDjjjiii
-------- */
if( s == ' ' && astKeyFields( this, "CD%3d%3d", 0, NULL, NULL ) ){
/* Do each row in the matrix. */
for( j = 0; j < naxis; j++ ){
/* Do each column in the matrix. */
for( i = 0; i < naxis; i++ ){
/* Get the CDjjjiii matrix element */
sprintf( keyname, "CD%.3d%.3d", j + 1, i + 1 );
if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0,
method, class, status ) ){
/* If found, save it with name PCj_i, and ensure the default value of 1.0
is used for CDELT. */
if( encoding == FITSIRAF_ENCODING ){
SetValue( ret, FormatKey( "PC", j + 1, i + 1, ' ', status ),
(void *) &dval, AST__FLOAT, NULL, status );
dval = 1.0;
SetValue( ret, FormatKey( "CDELT", j + 1, -1, s, status ),
(void *) &dval, AST__FLOAT, NULL, status );
gotpcij = 1;
}
}
}
}
}
/* CDj_i
---- */
if( s != ' ' ) {
sprintf( template, "CD%%d_%%d%c", s );
} else {
strcpy( template, "CD%d_%d" );
}
if( !gotpcij && astKeyFields( this, template, 0, NULL, NULL ) ){
/* Do each row in the matrix. */
for( j = 0; j < naxis; j++ ){
/* First find the sum of the squared elements in the row. and note the
sign of the diagonal element. */
rowsum2 = 0.0;
diag = +1;
for( i = 0; i < naxis; i++ ){
if( GetValue2( ret, this, FormatKey( "CD", j + 1, i + 1, s, status ),
AST__FLOAT, (void *) &dval, 0, method, class, status ) ){
rowsum2 += dval*dval;
if( i == j ) diag = ( dval >= 0.0 ) ? +1 : -1;
}
}
/* The CDELT value for this row will be the length of the row vector. This means that
each row will be a unit vector when converted to PCi_j form, and the CDELT will
give a real indication of the pixel size. Ensure that the diagonal
PCi+j element has a positive sign. */
cdelti = sqrt( rowsum2 )*diag;
SetValue( ret, FormatKey( "CDELT", j + 1, -1, s, status ),
(void *) &cdelti, AST__FLOAT, NULL, status );
/* Do each column in the matrix. */
for( i = 0; i < naxis; i++ ){
/* Get the CDj_i matrix element (note default value for all CD elements
is zero (even diagonal elements!). */
if( !GetValue2( ret, this, FormatKey( "CD", j + 1, i + 1, s, status ),
AST__FLOAT, (void *) &dval, 0, method, class, status ) ){
dval = 0.0;
}
/* Divide by the rows cdelt value and save it with name PCj_i. */
if( cdelti != 0.0 ) dval /= cdelti;
SetValue( ret, FormatKey( "PC", j + 1, i + 1, s, status ),
(void *) &dval, AST__FLOAT, NULL, status );
gotpcij = 1;
}
}
}
/* PCjjjiii and CROTAi keywords
---------------------------- */
/* Check there are some CDELT keywords... */
if( s != ' ' ) {
sprintf( template, "CDELT%%d%c", s );
} else {
strcpy( template, "CDELT%d" );
}
if( !gotpcij && astKeyFields( this, template, 0, NULL, NULL ) ){
/* See if there is a CROTA keyword. Try to read values for both axes
since they are sometimes both included. This ensures they will not be
included in the output when the FitsChan is deleted. Read the latitude
axis second in order to give it priority in cases where both are
present. */
crota = AST__BAD;
GetValue2( ret, this, FormatKey( "CROTA", axlon + 1, -1, s, status ),
AST__FLOAT, (void *) &crota, 0, method, class, status );
GetValue2( ret, this, FormatKey( "CROTA", axlat + 1, -1, s, status ),
AST__FLOAT, (void *) &crota, 0, method, class, status );
/* If there are any PCjjjiii keywords, rename them as PCj_i. */
if( s == ' ' && astKeyFields( this, "PC%3d%3d", 0, NULL, NULL ) ){
/* Do each row in the matrix. */
for( j = 0; j < naxis; j++ ){
/* Do each column in the matrix. */
for( i = 0; i < naxis; i++ ){
/* Get the PCiiijjj matrix element */
sprintf( keyname, "PC%.3d%.3d", j + 1, i + 1 );
if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0,
method, class, status ) ){
} else if( i == j ) {
dval = 1.0;
} else {
dval = 0.0;
}
/* Store it as PCi_j */
SetValue( ret, FormatKey( "PC", j + 1, i + 1, ' ', status ),
(void *) &dval, AST__FLOAT, NULL, status );
gotpcij = 1;
}
}
/* If there is a CROTA value and no PCjjjii keywords, create a PCj_i
matrix from the CROTA values. We need to have latitude and longitude
axes for this. */
} else if( s == ' ' && axlat != -1 && axlon != -1 && crota != AST__BAD ){
/* Get the sin and cos of CROTA */
cosrota = cos( crota*AST__DD2R );
sinrota = sin( crota*AST__DD2R );
/* Get the CDELT values for the longitude and latitude axes. */
if( GetValue2( ret, this, FormatKey( "CDELT", axlat + 1, -1, ' ', status ),
AST__FLOAT, (void *) &cdeltj, 1, method,
class, status ) &&
GetValue2( ret, this, FormatKey( "CDELT", axlon + 1, -1, ' ', status ),
AST__FLOAT, (void *) &cdelti, 1, method,
class, status ) ){
/* Save the ratio, needed below. */
lambda = cdeltj/cdelti;
/* Save a corresponding set of PCi_j keywords in the FitsChan. First do
the diagonal terms. */
for( i = 0; i < naxis; i++ ){
if( i == axlat ) {
dval = cosrota;
} else if( i == axlon ) {
dval = cosrota;
} else {
dval = 1.0;
}
SetValue( ret, FormatKey( "PC", i + 1, i + 1, ' ', status ),
(void *) &dval, AST__FLOAT, NULL, status );
gotpcij = 1;
}
/* Now do the non-zero off-diagonal terms. */
dval = sinrota/lambda;
SetValue( ret, FormatKey( "PC", axlat + 1, axlon + 1, ' ', status ),
(void *) &dval, AST__FLOAT, NULL, status );
dval = -sinrota*lambda;
SetValue( ret, FormatKey( "PC", axlon + 1, axlat + 1, ' ', status ),
(void *) &dval, AST__FLOAT, NULL, status );
}
}
}
}
/* Conversion of old PROJP, etc, is done once on the "primary" pass. */
if( s == ' ' ) {
/* PROJP keywords
-------------- */
if( astKeyFields( this, "PROJP%d", 1, ubnd, lbnd ) && axlat != -1 ) {
/* Some people produce headers with both PROJP and PV. Even worse, the
PROJP and PV values are sometimes inconsistent. In this case we trust
the PV values rather than the PROJP values, but only if the PV values
are not obviously incorrect for some reason. In particularly, we check
that, *if* either PVi_1 or PVi_2 (where i=longitude axis) is present,
then PVi_0 is also present. Conversely we check that if PVi_0 is
present then at least one of PVi_1 or PVi_2 is present. */
use_projp = 1;
if( axlat != -1 &&
astKeyFields( this, "PV%d_%d", 2, tubnd, tlbnd ) ){
use_projp = 0;
/* Are there any PV values for the longitude axis? */
if( tlbnd[ 0 ] <= axlon + 1 && axlon + 1 <= tubnd[ 0 ] ) {
/* Are either PVi_1 or PVi_2 available? */
if( HasCard( this, FormatKey( "PV", axlon + 1, 1, ' ', status ),
method, class, status ) ||
HasCard( this, FormatKey( "PV", axlon + 1, 2, ' ', status ),
method, class, status ) ) {
/* If so use PROJP if PVi_0 is not also available. */
if( !HasCard( this, FormatKey( "PV", axlon + 1, 0, ' ', status ),
method, class, status ) ) use_projp = 1;
/* If neither PVi_1 or PVi_2 are available, use PROJP if PVi_0 is
available. */
} else if( HasCard( this, FormatKey( "PV", axlon + 1, 0, ' ', status ),
method, class, status ) ) {
use_projp = 1;
}
}
}
/* Translate PROJP to PV if required. */
if( use_projp ) {
for( i = lbnd[ 0 ]; i <= ubnd[ 0 ]; i++ ){
if( GetValue2( ret, this, FormatKey( "PROJP", i, -1, ' ', status ),
AST__FLOAT, (void *) &dval, 0, method, class, status ) &&
( encoding == FITSPC_ENCODING ||
encoding == FITSIRAF_ENCODING ) ){
SetValue( ret, FormatKey( "PV", axlat + 1, i, ' ', status ),
(void *) &dval, AST__FLOAT, CardComm( this, status ), status );
}
}
}
}
/* CmVALi keywords
--------------- */
if( astKeyFields( this, "C%1dVAL%d", 2, ubnd, lbnd ) ){
ss = 'A';
for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){
for( i = lbnd[ 1 ]; i <= ubnd[ 1 ]; i++ ){
sprintf( keyname, "C%dVAL%d", m, i );
if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0,
method, class, status ) &&
( encoding == FITSPC_ENCODING ||
encoding == FITSIRAF_ENCODING ) ){
sprintf( keyname, "CRVAL%d%c", i, ss );
SetValue( ret, keyname, (void *) &dval, AST__FLOAT,
CardComm( this, status ), status );
}
}
ss++;
}
}
/* CmPIXi keywords
--------------- */
if( astKeyFields( this, "C%1dPIX%d", 2, ubnd, lbnd ) ){
ss = 'A';
for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){
for( i = lbnd[ 1 ]; i <= ubnd[ 1 ]; i++ ){
sprintf( keyname, "C%dPIX%d", m, i );
if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0,
method, class, status ) &&
( encoding == FITSPC_ENCODING ||
encoding == FITSIRAF_ENCODING ) ){
sprintf( keyname, "CRPIX%d%c", i, ss );
SetValue( ret, keyname, (void *) &dval, AST__FLOAT,
CardComm( this, status ), status );
}
}
ss++;
}
}
/* CmYPEi keywords
--------------- */
if( astKeyFields( this, "C%1dYPE%d", 2, ubnd, lbnd ) ){
ss = 'A';
for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){
for( i = lbnd[ 1 ]; i <= ubnd[ 1 ]; i++ ){
sprintf( keyname, "C%dYPE%d", m, i );
if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0,
method, class, status ) &&
( encoding == FITSPC_ENCODING ||
encoding == FITSIRAF_ENCODING ) ){
sprintf( keyname, "CTYPE%d%c", i, ss );
SetValue( ret, keyname, (void *) &cval, AST__STRING,
CardComm( this, status ), status );
}
}
ss++;
}
}
/* CmNITi keywords
--------------- */
if( astKeyFields( this, "C%1dNIT%d", 2, ubnd, lbnd ) ){
ss = 'A';
for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){
for( i = lbnd[ 1 ]; i <= ubnd[ 1 ]; i++ ){
sprintf( keyname, "C%dNIT%d", m, i );
if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0,
method, class, status ) &&
( encoding == FITSPC_ENCODING ||
encoding == FITSIRAF_ENCODING ) ){
sprintf( keyname, "CUNIT%d%c", i, ss );
SetValue( ret, keyname, (void *) &cval, AST__STRING,
CardComm( this, status ), status );
}
}
ss++;
}
}
/* CmELTi keywords
--------------- */
if( astKeyFields( this, "C%1dELT%d", 2, ubnd, lbnd ) ){
ss = 'A';
for( m = lbnd[ 0 ]; m <= ubnd[ 0 ]; m++ ){
/* Create a PCj_is matrix by copying the PCjjjiii values and rename CmELTi as
CDELTis. */
/* Do each row in the matrix. */
for( j = 0; j < naxis; j++ ){
/* Get the CDELT value for this row. Report an error if not present. */
sprintf( keyname, "C%dELT%d", m, j + 1 );
GetValue2( ret, this, keyname, AST__FLOAT, (void *) &cdeltj, 1,
method, class, status );
/* If CDELT is zero, use one hundredth of the corresponding CRVAL value
instead, or 1.0 if CRVAL is zero. Otherwise, the zeros could cause the
matrix to be non-invertable. The Mapping could then not be simplified
or used by a Plot. CDELT values of zero are usually used to indicate
"redundant" axes. For instance, a 2D image may be stored as a 3D cube
with a single plane with the "redundant" 3rd axis used to specify the
wavelength of the filter. The actual value used for CDELT shouldn't
matter since the axis only spans a single pixel anyway. */
if( cdeltj == 0.0 ){
GetValue2( ret, this, FormatKey( "CRVAL", j + 1, -1, ss, status ), AST__FLOAT,
(void *) &dval, 1, method, class, status );
cdeltj = 0.01*dval;
if( cdeltj == 0.0 ) cdeltj = 1.0;
}
/* Save it as CDELTis */
sprintf( keyname, "CDELT%d%c", j + 1, ss );
SetValue( ret, keyname, (void *) &cdeltj, AST__FLOAT,
CardComm( this, status ), status );
/* Do each column in the matrix. */
for( i = 0; i < naxis; i++ ){
/* Get the PCiiijjj matrix element */
sprintf( keyname, "PC%.3d%.3d", j + 1, i + 1 );
if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0,
method, class, status ) ){
} else if( i == j ) {
dval = 1.0;
} else {
dval = 0.0;
}
/* Store it as PCi_js. */
SetValue( ret, FormatKey( "PC", j + 1, i + 1, ss, status ),
(void *) &dval, AST__FLOAT, NULL, status );
}
}
ss++;
}
}
/* EPOCH keywords
------------ */
/* Get any EPOCH card, marking it as read. */
if( GetValue2( ret, this, "EPOCH", AST__FLOAT, (void *) &dval, 0, method,
class, status ) ){
/* Convert values of zero to B1950. */
if( dval == 0.0 ) dval = 1950.0;
/* Save a new EQUINOX card in the FitsChan, so long as there is not
already one there. */
if( !GetValue2( ret, this, "EQUINOX", AST__STRING, (void *) &cval, 0,
method, class, status ) ){
SetValue( ret, "EQUINOX", (void *) &dval, AST__FLOAT,
"Reference equinox", status );
}
}
/* String EQUINOX values
---------------------
If found, EQUINOX will be used in favour of any EPOCH value found
above. */
if( GetValue2( ret, this, "EQUINOX", AST__STRING, (void *) &cval, 0, method,
class, status ) ){
/* Note the first character. */
bj = cval[ 0 ];
/* If it is "B" or "J", read a floating value from the rest */
if( bj == 'B' || bj == 'J' ) {
if( 1 == astSscanf( cval + 1, " %lf ", &dval ) ){
/* If it is a Besselian epoch, convert to Julian. */
if( bj == 'B' ) dval = palEpj( palEpb2d( dval ) );
/* Replace the original EQUINOX card. */
SetValue( ret, "EQUINOX", (void *) &dval, AST__FLOAT,
CardComm( this, status ), status );
}
}
}
/* EQUINOX = 0.0 keywords
---------------------- */
if( GetValue2( ret, this, "EQUINOX", AST__FLOAT, (void *) &dval, 0, method,
class, status ) ){
if( dval == 0.0 ){
dval = 1950.0;
SetValue( ret, "EQUINOX", (void *) &dval, AST__FLOAT,
CardComm( this, status ), status );
}
}
}
/* DATE-OBS keywords
---------------- */
/* Read any DATE-OBS card. This prevents it being written out when the
FitsChan is deleted. */
if( s == ' ' ) {
strcpy( keyname, "DATE-OBS" );
if( GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0, method,
class, status ) ){
/* Ignore DATE-OBS values if the header contains an MJD-OBS value */
strcpy( keyname, "MJD-OBS" );
if( !GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0,
method, class, status ) ){
/* Get the corresponding mjd-obs value, checking that DATE-OBS is valid. */
dval = DateObs( cval, status );
if( dval != AST__BAD ){
SetValue( ret, keyname, (void *) &dval, AST__FLOAT,
"Date of observation", status );
}
}
}
}
/* Things specific to the CLASS encoding
------------------------------------- */
if( encoding == FITSCLASS_ENCODING ) ClassTrans( this, ret, axlat,
axlon, method, class, status );
/* Convert SAO distorted TAN headers to TPN distorted TAN headers.
-------------------------------------------------------------- */
if( s == ' ' && !Ustrcmp( prj, "-TAN", status ) ){
/* Translate the COi_m keywords into PV i+m keywords. */
if( SAOTrans( this, ret, method, class, status ) ) {
/* Change the CTYPE projection form TAN to TPV. */
strcpy( prj, "-TPN" );
strcpy( lontype + 4, "-TPN" );
cval = lontype;
SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ),
(void *) &cval, AST__STRING, NULL, status );
strcpy( lattype + 4, "-TPN" );
cval = lattype;
SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ),
(void *) &cval, AST__STRING, NULL, status );
}
}
/* AIPS "NCP" projections
--------------------- */
/* Compare the projection type with "-NCP" */
if( !Ustrcmp( prj, "-NCP", status ) ) {
/* Get the latitude reference value, and take is cot. */
GetValue2( ret, this, FormatKey( "CRVAL", axlat + 1, -1, s, status ),
AST__FLOAT, (void *) &dval, 1, method, class, status );
sinval = sin( dval*AST__DD2R );
if( sinval != 0.0 ) {
dval = cos( dval*AST__DD2R )/sinval;
/* Replace NCP with SIN in the CTYPE values. */
strcpy( lontype + 4, "-SIN" );
cval = lontype;
SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ),
(void *) &cval, AST__STRING, NULL, status );
strcpy( lattype + 4, "-SIN" );
cval = lattype;
SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ),
(void *) &cval, AST__STRING, NULL, status );
/* Store the new projection parameters using names suitable to FITS_WCS
encoding. */
SetValue( ret, FormatKey( "PV", axlat + 1, 2, s, status ),
(void *) &dval, AST__FLOAT, NULL, status );
dval = 0.0;
SetValue( ret, FormatKey( "PV", axlat + 1, 1, s, status ),
(void *) &dval, AST__FLOAT, NULL, status );
}
}
/* CLASS "ATF" projections
---------------------- */
/* Replace ATF with AIT in the CTYPE values. */
if( !Ustrcmp( prj, "-ATF", status ) ) {
strcpy( lontype + 4, "-AIT" );
cval = lontype;
SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ),
(void *) &cval, AST__STRING, NULL, status );
strcpy( lattype + 4, "-AIT" );
cval = lattype;
SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ),
(void *) &cval, AST__STRING, NULL, status );
}
/* AIPS "GLS" projections
--------------------- */
/* Compare the projection type with "-GLS" */
if( !Ustrcmp( prj, "-GLS", status ) ) {
/* Convert to "-SFL" */
strcpy( lontype + 4, "-SFL" );
cval = lontype;
SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ),
(void *) &cval, AST__STRING, NULL, status );
strcpy( lattype + 4, "-SFL" );
cval = lattype;
SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ),
(void *) &cval, AST__STRING, NULL, status );
/* FITS-WCS paper 2 (sec. 6.1.4) describes how to handle AIPS GLS
projections, but requires that the axes are not rotated. Instead, we
modify the native latitude at the fiducial point, theta_0, as is done
in wcslib function celfix in file wcsfix.c (see also FITS-WCS paper
II sec. 2.5). We only need to change theta_0 if the CRVAL position is
not the celestial origin. */
shifted = 0;
sprintf( keyname, "CRVAL%d", axlon + 1 );
if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0,
method, class, status ) ){
if( dval != 0.0 ) shifted = 1;
}
sprintf( keyname, "CRVAL%d", axlat + 1 );
if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0,
method, class, status ) ){
if( dval != 0.0 ) shifted = 1;
}
if( 0 && shifted ) {
SetValue( ret, FormatKey( "PV", axlon + 1, 2, s, status ),
(void *) &dval, AST__FLOAT, NULL, status );
dval = 0.0;
SetValue( ret, FormatKey( "PV", axlon + 1, 1, s, status ),
(void *) &dval, AST__FLOAT, NULL, status );
dval = 1.0;
SetValue( ret, FormatKey( "PV", axlon + 1, 0, s, status ),
(void *) &dval, AST__FLOAT, NULL, status );
}
}
/* Rename any "QV" projection parameters to "PV" (such as used by
-TAB to indicate the interpolation method, or by the internal
-TPN projection to indicate distortion coefficients).
------------------------------------------------------------ */
/* Rewind the FitsChan. */
astClearCard( this );
/* Search the FitsChan for QV cards. */
if( s != ' ' ) {
sprintf( template, "QV%%d_%%d%c", s );
} else {
strcpy( template, "QV%d_%d" );
}
while( FindKeyCard( this, template, method, class, status ) && astOK ) {
/* If the projection name is "TAN", replace TAN with TPN in the CTYPE values. */
if( !Ustrcmp( prj, "-TAN", status ) ){
strcpy( prj, "-TPN" );
strcpy( lontype + 4, "-TPN" );
cval = lontype;
SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ),
(void *) &cval, AST__STRING, NULL, status );
strcpy( lattype + 4, "-TPN" );
cval = lattype;
SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ),
(void *) &cval, AST__STRING, NULL, status );
}
/* Indicate that the QV card has been consumed. */
MarkCard( this, status );
/* Get the keyword name and change it from QV to PV. */
strcpy( keyname, CardName( this, status ) );
keyname[ 0 ] ='P';
/* Store the new PV card so long as there it is not already present in the
FitsChan. */
if( !GetValue2( ret, this, keyname, AST__FLOAT, (void *) &cval, 0,
method, class, status ) ){
SetValue( ret, keyname, CardData( this, &size, status ), AST__FLOAT,
CardComm( this, status ), status );
}
/* Move on to the next card. */
MoveCard( this, 1, method, class, status );
}
/* Change any TAN projection to TPN projection if the PolyTan attribute
is non-zero. Also change any TPV projection to TPN projection.
--------------------------------------------------- */
if( ( !Ustrcmp( prj, "-TAN", status ) &&
GetUsedPolyTan( this, ret, axlat + 1, axlon + 1, s, method, class, status ) ) ||
!Ustrcmp( prj, "-TPV", status ) ){
strcpy( prj, "-TPN" );
strcpy( lontype + 4, "-TPN" );
cval = lontype;
SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, s, status ),
(void *) &cval, AST__STRING, NULL, status );
strcpy( lattype + 4, "-TPN" );
cval = lattype;
SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, s, status ),
(void *) &cval, AST__STRING, NULL, status );
}
/* IRAF "ZPX" projections
--------------------- */
if( s == ' ' && !Ustrcmp( prj, "-ZPX", status ) ) {
/* Replace "ZPX" with "ZPN-ZPX" (i.e. ZPN projection with ZPX distortion
code) in the CTYPE values. */
strcpy( lontype + 4, "-ZPN-ZPX" );
cval = lontype;
SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, ' ', status ),
(void *) &cval, AST__STRING, NULL, status );
strcpy( lattype + 4, "-ZPN-ZPX" );
cval = lattype;
SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, ' ', status ),
(void *) &cval, AST__STRING, NULL, status );
/* Check latitude then longitude axes */
for( i = 0; i < 2; i++ ){
iaxis = i ? axlat : axlon;
/* Concatenate all the IRAF "WAT" keywords together for this axis. These
keywords are marked as having been used, so that they are not written
out when the FitsChan is deleted. */
watmem = ConcatWAT( this, iaxis, method, class, status );
/* Search the total WAT string for any projp terms. */
if( watmem ){
for( iproj = 0; iproj < 10 && astOK; iproj++ ) {
sprintf( format, "projp%d=", iproj );
start = strstr( watmem, format );
if( start ) {
sprintf( format, "projp%d=%%lf", iproj );
if( astSscanf( start, format, &projp ) ){
SetValue( ret, FormatKey( "PV", axlat + 1, iproj, ' ', status ),
(void *) &projp, AST__FLOAT,
"ZPN projection parameter", status );
}
}
}
/* Release the memory used to hold the concatenated WAT keywords. */
watmem = (char *) astFree( (void *) watmem );
}
}
/* IRAF "TNX" projections
--------------------- */
} else if( s == ' ' && !Ustrcmp( prj, "-TNX", status ) ) {
/* Replace TNX with TPN in the CTYPE values. */
strcpy( lontype + 4, "-TPN" );
cval = lontype;
SetValue( ret, FormatKey( "CTYPE", axlon + 1, -1, ' ', status ),
(void *) &cval, AST__STRING, NULL, status );
strcpy( lattype + 4, "-TPN" );
cval = lattype;
SetValue( ret, FormatKey( "CTYPE", axlat + 1, -1, ' ', status ),
(void *) &cval, AST__STRING, NULL, status );
/* Check latitude then longitude axes */
for( i = 0; i < 2; i++ ){
iaxis = i ? axlat : axlon;
/* Concatenate all the IRAF "WAT" keywords together for this axis. These
keywords are marked as having been used, so that they are not written
out when the FitsChan is deleted. */
watmem = ConcatWAT( this, iaxis, method, class, status );
/* Extract the polynomial coefficients from the concatenated WAT string.
These are returned in the form of a list of PVi_m values for a TPN
projection. */
ncoeff = WATCoeffs( watmem, i, &cvals, &mvals, &ok, status );
/* If we can handle the TNX projection, store the PV values in the FitsChan. */
if( ok ) {
for( icoeff = 0; icoeff < ncoeff; icoeff++ ) {
SetValue( ret, FormatKey( "PV", iaxis + 1, mvals[ icoeff ],
' ', status ),
(void *) (cvals + icoeff), AST__FLOAT,
"TAN projection parameter", status );
}
/* If the TNX cannot be represented in FITS-WCS (within our restrictions), add
warning keywords to the FitsChan. */
} else {
Warn( this, "tnx", "This FITS header includes, or was "
"derived from, a TNX projection which requires "
"unsupported IRAF-specific corrections. The WCS "
"information may therefore be incorrect.", method, class, status );
}
/* Release the memory used to hold the concatenated WAT keywords. */
watmem = (char *) astFree( (void *) watmem );
}
}
/* MSX CAR projections.
------------------- */
if( !Ustrcmp( prj, "-CAR", status ) && !astGetCarLin( this ) ) {
/* The CAR projection has valid projection plane points only for native
longitudes in the range [-180,+180]. The reference pixel (CRPIX) is at
native longitude zero. We need to check that the reference point is not
so far outside the image that the entire image lies outside the range
[-180,+180]. If it is, we modify the CRPIX value by the number of
pixels corresponding to 360 degres of longitude in order to bring the
array into the valid domain ([-180,+180]) of the projection. */
if( GetValue2( ret, this, FormatKey( "CDELT", axlon + 1, -1, s, status ),
AST__FLOAT, (void *) &cdelti, 1, method, class, status ) &&
GetValue2( ret, this, FormatKey( "CRPIX", axlon + 1, -1, s, status ),
AST__FLOAT, (void *) &dval, 0, method, class, status ) ) {
/* We check if the mid point of the array is in the valiud longitude range. Use the
bottom left corner as a fallback if the image size is unknown. */
if( !GetValue( this, FormatKey( "NAXIS", axlon + 1, -1, ' ', status ),
AST__INT, &dim, 0, 0, method, class, status ) ) {
dim = 0;
}
if( cdelti != 0.0 ) {
double offset = 0.5*( dim + 1 );
dval = offset + AST__DR2D*palDrange( AST__DD2R*( dval - offset )*cdelti )/cdelti;
SetValue( ret, FormatKey( "CRPIX", axlon + 1, -1, s, status ),
(void *) &dval, AST__FLOAT, CardComm( this, status ), status );
}
}
}
/* Replace RESTFREQ by RESTFRQ.
---------------------------- */
/* Get any RESTFREQ card, marking it as read. */
if( s != ' ' ) {
sprintf( keyname, "RESTFREQ%c", s );
} else {
strcpy( keyname, "RESTFREQ" );
}
if( GetValue2( ret, this, keyname, AST__FLOAT, (void *) &dval, 0, method,
class, status ) ){
/* Look for "MHz" and "GHz" within the comment. If found scale the value
into Hz. */
comm = CardComm( this, status );
if( comm ) {
if( strstr( comm, "GHz" ) ) {
dval *= 1.0E9;
comm = "[Hz] Rest Frequency";
} else if( strstr( comm, "MHz" ) ) {
dval *= 1.0E6;
comm = "[Hz] Rest Frequency";
}
}
/* Save a new RESTFRQ card in the FitsChan, so long as there is not
already one there. */
if( s != ' ' ) {
sprintf( keyname, "RESTFRQ%c", s );
} else {
strcpy( keyname, "RESTFRQ" );
}
if( !GetValue2( ret, this, keyname, AST__STRING, (void *) &cval, 0,
method, class, status ) ){
SetValue( ret, keyname, (void *) &dval, AST__FLOAT, comm, status );
}
}
/* Translate AIPS spectral CTYPE values to FITS-WCS paper III equivalents.
These are of the form AAAA-BBB, where "AAAA" can be "FREQ", "VELO" (=VRAD!)
or "FELO" (=VOPT-F2W), and BBB can be "LSR", "LSD", "HEL" (=*Bary*centric!)
or "GEO". Also convert "LAMBDA" to "WAVE". */
for( j = 0; j < naxis; j++ ) {
if( GetValue2( ret, this, FormatKey( "CTYPE", j + 1, -1, s, status ),
AST__STRING, (void *) &cval, 0, method,
class, status ) ){
if( IsAIPSSpectral( cval, &astype, &assys, status ) ) {
SetValue( ret, FormatKey( "CTYPE", j + 1, -1, s, status ),
(void *) &astype, AST__STRING, NULL, status );
SetValue( ret, "SPECSYS", (void *) &assys, AST__STRING, NULL, status );
break;
} else if( !strcmp( cval, "LAMBDA " ) ) {
cval = "WAVE";
SetValue( ret, FormatKey( "CTYPE", j + 1, -1, s, status ),
(void *) &cval, AST__STRING, NULL, status );
break;
}
}
}
/* Common case insensitive CUNIT values: "Hz", "Angstrom", "km/s", "M/S" */
if( s != ' ' ) {
sprintf( template, "CUNIT%%d%c", s );
} else {
strcpy( template, "CUNIT%d" );
}
if( astKeyFields( this, template, 1, &jhi, &jlo ) ){
/* Convert keyword indices from 1-based to 0-base, and loop round them all. */
jhi--;
jlo--;
for( j = jlo; j <= jhi; j++ ){
char *keynam;
keynam = FormatKey( "CUNIT", j + 1, -1, s, status );
if( GetValue2( ret, this, keynam, AST__STRING, (void *) &cval, 0,
method, class, status ) ){
size_t nc = astChrLen( cval );
if( nc == 0 ) {
cval = NULL;
} else if( !Ustrcmp( cval, "Hz", status ) ) {
cval = "Hz";
} else if( !Ustrcmp( cval, "Angstrom", status ) ) {
cval = "Angstrom";
} else if( !Ustrcmp( cval, "km/s", status ) ) {
cval = "km/s";
} else if( !Ustrcmp( cval, "m/s", status ) ) {
cval = "m/s";
} else {
cval = NULL;
}
if( cval ) SetValue( ret, keynam, (void *) &cval, AST__STRING, NULL, status );
}
}
}
/* After doing the primary axis descriptions, prepare to do the "A"
description. */
if( s == ' ' ) s = 'A' - 1;
}
/* IRAF mini-WCS keywords
---------------------- */
/* Rewind the FitsChan to search from the first card. */
astClearCard( this );
/* Search forward through until all cards have been checked. */
while( !astFitsEof( this ) && astOK ){
/* Check to see if the keyword name from the current card matches
any of the known mini-WCS keywords. If so, mark the card as read. */
if( Match( CardName( this, status ), "WAT%d_%d", 0, NULL, &m, method, class, status ) ||
Match( CardName( this, status ), "LTM%d_%d", 0, NULL, &m, method, class, status ) ||
Match( CardName( this, status ), "LTV%d", 0, NULL, &m, method, class, status ) ||
Match( CardName( this, status ), "WSV%d_LEN", 0, NULL, &m, method, class, status ) ||
Match( CardName( this, status ), "WSV%d_%d", 0, NULL, &m, method, class, status ) ){
MarkCard( this, status );
}
/* Now move the current card on to the next card. */
MoveCard( this, 1, method, class, status );
}
/* Delete the returned FitsChan if it is empty. */
if( ret && !astGetNcard( ret ) ) ret = (AstFitsChan *) astDelete( ret );
/* Return. */
return ret;
}
int Split( AstFitsChan *this, const char *card, char **name, char **value,
char **comment, const char *method, const char *class, int *status ){
/*
* Name:
* Split
* Purpose:
* Extract the keyword name, value and comment from a FITS header card.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int Split( AstFitsChan *this, const char *card, char **name, char **value,
* char **comment, const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* The name, value and comment (if present) are extracted from the
* supplied card text and returned.
* Parameters:
* this
* Pointer to the FitsCHan.
* card
* Pointer to a string holding the FITS header card.
* name
* Pointer to a location at which to return the pointer to a string
* holding the keyword name.
* value
* Pointer to a location at which to return the pointer to a string
* holding the keyword value.
* comment
* Pointer to a location at which to return the pointer to a string
* holding the keyword comment.
* 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:
* - An integer identifying the data type of the keyword value. This
* will be one of the values AST__UNDEF, AST__COMMENT, AST__INT,
* AST__STRING, AST__CONTINUE, AST__FLOAT, AST__COMPLEXI or AST__COMPLEXF
* defined in fitschan.h.
* Notes:
* - If the keyword value is a string, then the returned value does not
* include the delimiting quotes, and pairs of adjacent quotes within the
* string are replaced by single quotes.
* - A maximum of 80 characters are read from the supplied card, so the
* string does not need to be null terminated unless less than 80
* characters are to be read.
* - The memory holding the three strings "name", "value" and "comment"
* should be released when no longer needed using astFree.
* - NULL pointers and a data type of AST__COMMENT are returned if an
* error has already occurred, or if this function fails for any reason.
*/
/* Local Variables: */
char *c; /* Pointer to returned comment string */
char *dd; /* Pointer to intermediate character */
char *slash; /* Pointer to comment character */
char *v; /* Pointer to returned value string */
char buf[255]; /* Buffer for warning text */
const char *d; /* Pointer to first comment character */
const char *v0; /* Pointer to first non-blank value character */
double fi, fr; /* Values read from value string */
int badval; /* Is the keyword value illegal? */
int blank_name; /* Is keyword name blank? */
int cont; /* Is this a continuation card? */
int i; /* Character index */
int ii, ir; /* Values read from value string */
int iopt; /* Index of option within list */
int len; /* Used length of value string */
int lq; /* Was previous character an escaping quote? */
int nch; /* No. of characters used */
int ndig; /* No. of digits in the formatted integer */
int type; /* Keyword data type */
size_t nc; /* Number of character in the supplied card */
size_t ncc; /* No. of characters in the comment string */
size_t ncv; /* No. of characters in the value string */
/* Initialise the returned pointers. */
*name = NULL;
*value = NULL;
*comment = NULL;
type = AST__COMMENT;
/* Check the global status. */
if( !astOK ) return type;
/* Assume initially that the keyword value is legal. */
badval = 0;
/* Store the number of characters to be read from the supplied card. This
is not allowed to be more than the length of a FITS header card. */
nc = 0;
while( nc < AST__FITSCHAN_FITSCARDLEN && card[ nc ] ) nc++;
/* Reduce the number of characters to read so that any non-printing
characters such as new-lines at the end of the string are ignored. */
while( nc > 0 && !isprint( card[ nc - 1 ] ) ) nc--;
/* Allocate memory for a copy of the keyword name plus a terminating
null character. */
*name = (char *) astMalloc( ( 1 + FITSNAMLEN )*sizeof(char) );
/* Check the pointer can be used. */
if( astOK ){
/* Initialise the name string by filling it with spaces, and terminating it. */
for( i = 0; i < FITSNAMLEN; i++ ) (*name)[ i ] = ' ';
(*name)[ FITSNAMLEN ] = 0;
/* Copy the the keyword name, ensuring that no more than FITSNAMLEN (8)
characters are copied. */
strncpy( *name, card, ( nc > FITSNAMLEN ) ? FITSNAMLEN : nc );
/* If there is no keyword name, flag that we have a blank name which will
be treated as a comment card. */
if( strspn( *name, " " ) == strlen( *name ) ){
blank_name = 1;
/* If the card contains a keyword name, replace any white space with
nulls. */
} else {
blank_name = 0;
dd = *name + strlen( *name ) - 1;
while( isspace( *dd ) ) *(dd--) = 0;
}
/* Check the keyword name is legal. */
CheckFitsName( this, *name, method, class, status );
/* Allocate memory to hold the keyword value and comment strings. */
*value = (char *) astMalloc( sizeof(char)*( 2 + nc ) );
*comment = (char *) astMalloc( sizeof(char)*( 1 + nc ) );
/* Check the pointers can be used. */
if( astOK ){
/* Check for CONTINUE cards. These have keyword CONTINUE but have a space
instead of an equals sign in column 9. They must also have a single quote
in column 11. */
cont = ( !Ustrcmp( *name, "CONTINUE", status ) &&
nc > FITSNAMLEN + 3 &&
card[ FITSNAMLEN ] == ' ' &&
card[ FITSNAMLEN + 2 ] == '\'' );
/* If column 9 does not contain an equals sign (but is not a CONTINUE card), or if
the keyword is "HISTORY", "COMMENT" or blank, then columns 9 to the end are
comment characters, and the value string is null. */
if( ( nc <= FITSNAMLEN || card[ FITSNAMLEN ] != '='
|| !Ustrcmp( *name, "HISTORY", status )
|| !Ustrcmp( *name, "COMMENT", status )
|| blank_name ) && !cont ){
(*value)[ 0 ] = 0;
if( nc > FITSNAMLEN ){
(void) strncpy( *comment, card + FITSNAMLEN,
nc - FITSNAMLEN );
(*comment)[ nc - FITSNAMLEN ] = 0;
} else {
(*comment)[ 0 ] = 0;
}
/* Otherwise there is a value field. */
} else {
/* Find the first non-blank character in the value string. */
v0 = card + FITSNAMLEN + 1;
while( (size_t)(v0 - card) < nc &&
isspace( (int) *v0 ) ) v0++;
/* Store pointers to the start of the returned value and comment strings. */
v = *value;
c = *comment;
/* If the first character in the value string is a single quote, the value is
a string. In this case the value ends at the first non-escaped single
quote. */
if( *v0 == '\''){
type = cont ? AST__CONTINUE : AST__STRING;
/* We want to copy the string value, without the delimiting quotes, to the
returned value string. Single quotes within the string are represented
by two adjacent quotes, so we also need to check for these and replace
them by one quote in the returned string. First initialise a pointer
to the first character after the opening quote, and set a flag
indicating that (for the purposes of identifying pairs of adjacent
quotes within the string) the previous character was not a quote. */
d = v0 + 1;
lq = 0;
/* Loop round each remaining character in the supplied card. */
while( (size_t)(d - card) < nc ){
/* If the current character is a single quote... */
if( *d == '\'' ){
/* If the previous character was also a single quote then the quote does
not mark the end of the string, but is a quote to be included literally
in the value. Copy the quote to the returned string and clear the flag
to indicate that the pair of adjacent quotes is now complete. */
if( lq ){
*(v++) = '\'';
lq = 0;
/* If the last character was not a quote, then set the flag for the next
pass through the loop, but do not copy the quote to the returned string
since it will either be a quote escaping a following adjacent quote, or
a quote to mark the end of the string. */
} else {
lq = 1;
}
/* If the current character is not a quote... */
} else {
/* If the previous character was a quote, then we have found a single
isolated quote which therefore marks the end of the string value.
The pointer "d" is left pointing to the first character
after the terminating quote. */
if( lq ){
break;
/* If the last character was not a quote, copy it to the returned string. */
} else {
*(v++) = *d;
}
}
d++;
}
/* Terminate the returned value string. */
*v = 0;
/* Now deal with logical and numerical values. */
} else {
/* The end of the value field is marked by the first "/". Find the number
of characters in the value field. Pointer "d" is left pointing to the
first character in the comment (if any). Only use "/" characters which
occur within the first nc characters, and do not occur wiuthin the
keyword name (not strictly legal, but a warning will have been issued
by CheckFitsName in such cases). */
d = strchr( card + FITSNAMLEN, '/' );
if( !d || ( d - card ) >= nc ){
ncv = nc - FITSNAMLEN - 1;
d = NULL;
} else {
ncv = (size_t)( d - card ) - FITSNAMLEN - 1;
}
/* Copy the value string to the returned string. */
if( ncv == 0 ){
*v = 0;
} else {
strncpy( v, card + FITSNAMLEN + 1, ncv );
v[ ncv ] = ' ';
v[ ncv + 1 ] = 0;
}
/* Find the first non-blank character in the value string. */
v0 = v;
while( *v0 && isspace( (int) *v0 ) ) v0++;
/* See if the value string is one of the following strings (optionally
abbreviated and case insensitive): YES, NO, TRUE, FALSE. */
iopt = FullForm( "YES NO TRUE FALSE", v0, 1, status );
/* Return the single character "T" or "F" at the start of the value string
if the value matches one of the above strings. */
if( iopt == 0 || iopt == 2 ) {
type = AST__LOGICAL;
strcpy ( v, "T" );
} else if( iopt == 1 || iopt == 3 ) {
type = AST__LOGICAL;
strcpy ( v, "F" );
/* If it does not match, see if the value is numerical. */
} else {
/* Save the length of the value string excluding trailing blanks. */
len = ChrLen( v, status );
/* If the entire string is blank, the value type is UNDEF. */
if( len == 0 ) {
type = AST__UNDEF;
/* If there are no dots (decimal points) or exponents (D or E) in the value... */
} else if( !strpbrk( v, ".EeDd" ) ){
/* First attempt to read two integers from the string (separated by white
space). */
if( nch = 0,
( 2 == astSscanf( v, " %d %d%n", &ir, &ii, &nch ) ) &&
( nch >= len ) ) {
type = AST__COMPLEXI;
/* If that failed, attempt to read a single integer from the string. */
} else if( nch = 0,
( 1 == astSscanf( v, " %d%n", &ir, &nch ) ) &&
( nch >= len ) ) {
type = AST__INT;
}
/* If there are dots (decimal points) in the value... */
} else {
/* First attempt to read two doubles from the string (separated by white
space). */
if( nch = 0,
( 2 == astSscanf( v, " %lf %lf%n", &fr, &fi, &nch ) ) &&
( nch >= len ) ) {
type = AST__COMPLEXF;
/* If that failed, attempt to read a single double from the string. */
} else if( nch = 0,
( 1 == astSscanf( v, " %lf%n", &fr, &nch ) ) &&
( nch >= len ) ) {
type = AST__FLOAT;
}
/* If both the above failed, it could be because the string contains a
"D" exponent (which is probably valid FITS) instead of an "E" exponent.
Replace any "D" in the string with "e" and try again. */
if( type == AST__COMMENT && astOK ) {
/* Replace "d" and "D" by "e" (if this doesn't produce a readable floating
point value then the value string will not be used, so it is safe to
do the replacement in situ). */
for( i = 0; i < len; i++ ) {
if( v[ i ] == 'd' || v[ i ] == 'D' ) v[ i ] = 'e';
}
/* Attempt to read two doubles from the edited string (separated by white
space). */
if( nch = 0,
( 2 == astSscanf( v, " %lf %lf%n", &fr, &fi, &nch ) ) &&
( nch >= len ) ) {
type = AST__COMPLEXF;
/* If that failed, attempt to read a single double from the edited string. */
} else if( nch = 0,
( 1 == astSscanf( v, " %lf%n", &fr, &nch ) ) &&
( nch >= len ) ) {
type = AST__FLOAT;
}
}
}
}
/* If the value type could not be determined, indicate that a warning
should be issued. */
if( type == AST__COMMENT && astOK ) {
badval = 1;
(*value)[ 0 ] = 0;
(*comment)[ 0 ] = 0;
d = NULL;
}
}
/* Find the number of characters in the comment. Pointer "d" should point to
the first character following the value string. */
if( d ){
ncc = nc - (size_t)( d - card );
} else {
ncc = 0;
}
/* Copy the remainder of the card to the returned comment string. */
if( astOK && ncc > 0 ){
strncpy( c, d, ncc );
c[ ncc ] = 0;
/* Find the start of the comment (indicated by the first "/" after the
value string). */
slash = strchr( c, '/' );
/* Temporarily terminate the string at the slash. */
if( slash ) *slash = 0;
/* Shuffle the characters following the slash down to the
start of the returned string. */
if( slash ){
ncc -= (size_t)( slash - c ) + 1;
d = slash + 1;
for( i = 0; i < 1 + (int) ncc; i++ ) *(c++) = *(d++);
}
/* If there is no comment string, return a null string. */
} else {
*c = 0;
}
}
}
}
/* Truncate the returned string to avoid wasting space. */
if( *name ) *name = (char *) astRealloc( (void *) *name, strlen( *name ) + 1 );
if( *comment ) *comment = (char *) astRealloc( (void *) *comment, strlen( *comment ) + 1 );
if( *value ) *value = (char *) astRealloc( (void *) *value, strlen( *value ) + 1 );
/* If the value is deemed to be integer, check that the number of digits
in the formatted value does not exceed the capacity of an int. This may
be the case if there are too many digits in the integer for an "int" to
hold. In this case, change the data type to float. */
if( *value && type == AST__INT ) {
ndig = 0;
c = *value;
while( *c ) {
if( isdigit( *(c++) ) ) ndig++;
}
if( ndig >= int_dig ) type = AST__FLOAT;
}
/* If an error occurred, free the returned strings and issue a context message. */
if( !astOK ){
*name = (char *) astFree( (void *) *name );
*value = (char *) astFree( (void *) *value );
*comment = (char *) astFree( (void *) *comment );
type = AST__COMMENT;
if( !astOK ) {
astError( astStatus, "%s(%s): Unable to store the following FITS "
"header card:\n%s\n", status, method, class, card );
} else {
sprintf( buf, "The keyword value in the FITS header card '%s' "
" is illegal.", card );
Warn( this, "badkeyvalue", buf, method, class, status );
}
/* If a bad keyword value was encountered, issue a warning. */
} else if( badval ){
sprintf( buf, "The keyword value in the FITS header card '%s' "
" is illegal.", card );
Warn( this, "badkeyvalue", buf, method, class, status );
}
/* Return the data type. */
return type;
}
static int SplitMap( AstMapping *map, int invert, int ilon, int ilat,
AstMapping **map1, AstWcsMap **map2, AstMapping **map3, int *status ){
/*
* Name:
* SplitMap
* Purpose:
* Locate a WCS projection within a Mapping.
* Type:
* Private function.
* Synopsis:
* int SplitMap( AstMapping *map, int invert, int ilon, int ilat,
* AstMapping **map1, AstWcsMap **map2, AstMapping **map3, int *status )
* Class Membership:
* FitsChan
* Description:
* If possible, the supplied Mapping is decomposed into three component
* mappings to be compounded in series. To be acceptable, the second of
* these three Mappings must be an inverted WcsMap with a non-zero
* FITSProj attribute value, and there must not be such a WcsMap in
* either of the other two Mappings. If it is not possible to produce
* such a group of three Mappings, then a zero function value is returned,
* together with three NULL Mapping pointers. All the mappings before the
* WcsMap are compounded together and returned as "map1". The inverse of
* the WcsMap itself is returned as "map2", and any remaining Mappings
* are compounded together and returned as "map3".
*
* The search algorithm allows for an arbitrary combination of series and
* parallel CmpMaps.
* Parameters:
* map
* A pointer to the Mapping from pixel to physical coordinates.
* invert
* The value of the Invert attribute to use with "map" (the value
* returned by astGetInvert is not used).
* ilon
* Index of mapping output which is connected to the longitude axis.
* ilat
* Index of mapping output which is connected to the latitude axis.
* map1
* A location at which to return a pointer to the Mapping from pixel
* to intermediate world coordinates.
* map2
* A location at which to return a pointer to the Mapping from
* intermediate world coordinates to native spherical coordinates. This
* will be an inverted WcsMap with non-zero FITSProj attribute value.
* map3
* A location at which to return a pointer to the Mapping from
* native spherical coordinates to physical coordinates.
* dep
* The address of an integer holding the current depth of recursion
* into this function.
* status
* Pointer to the inherited status variable.
* Returned Value:
* One if a suitable WcsMap was found, zero otherwise.
* Notes:
* - The returned Mappings contain independant copies of the relevant
* components of the supplied Mapping and can be modified without
* changing the supplied Mapping.
* - NULL pointers will be returned for all Mappings if no WcsMap
* can be found in the supplied Mapping.
* - A pointer to a UnitMap will be returned for map1 if no mappings
* exist before the WcsMap.
* - A pointer to a UnitMap will be returned for map3 if no mappings
* exist after the WcsMap.
* - NULL pointers will be returned for all Mappings and a function
* value of zero will be returned if an error has occurred, or if this
* function should fail for any reason.
*/
/* Local Variables */
AstFitsChan *fc; /* Pointer to temporary FitsChan */
AstFrameSet *tfs; /* Temporary FrameSet */
AstMapping *mapa; /* Pre-wcs Mapping */
AstMapping *mapc; /* Post-wcs Mapping */
AstMapping *tmap1; /* Temporary Mapping */
AstMapping *tmap2; /* Temporary Mapping */
AstPointSet *pset1; /* Pixel positions */
AstPointSet *pset2; /* WCS positions */
AstWcsMap *mapb; /* WcsMap */
char card[ AST__FITSCHAN_FITSCARDLEN + 1 ]; /* Buffer for header card */
double **ptr1; /* Pointer to pixel axis values */
double **ptr2; /* Pointer to WCS axis values */
double *iwc_origin; /* Array holding IWC at pixel origin */
double *pix_origin; /* Array holding pixel coords at pixel origin */
double *w1; /* Pointer to work space */
int i; /* Loop index */
int npix; /* Number of pixel axes */
int nwcs; /* Number of WCS axes */
int ret; /* Was a non-linear Mapping found? */
/* Initialise */
*map1 = NULL;
*map2 = NULL;
*map3 = NULL;
ret = 0;
/* Check the global status. */
if( !astOK ) return ret;
/* Call SplitMap2 to do the work. SplitMap2 does not check that the
WcsMap is an *inverted* WcsMap, neither does it check that there
are no WcsMaps in either map1 or map3. */
if( SplitMap2( map, invert, map1, map2, map3, status ) ) {
/* Check that the WcsMap is inverted. */
if( astGetInvert( *map2 ) ) {
/* Check that map 1 does not contain a WcsMap with non-zero FITSProj
attribute. */
if( !SplitMap2( *map1, astGetInvert( *map1 ), &mapa, &mapb, &mapc,
status ) ) {
/* Check that map 3 does not contain a WcsMap with non-zero FITSProj
attribute. */
if( !SplitMap2( *map3, astGetInvert( *map3 ), &mapa, &mapb, &mapc,
status ) ) {
/* If so, the three Mappings are OK. */
ret = 1;
} else {
mapa = astAnnul( mapa );
mapb = astAnnul( mapb );
mapc = astAnnul( mapc );
}
} else {
mapa = astAnnul( mapa );
mapb = astAnnul( mapb );
mapc = astAnnul( mapc );
}
}
}
/* If the above failed to find a suitable WcsMap, we now consider cases
where the pixel->WCS mapping is linear. We can invent a CAR projection
WcsMap for such cases. We use a ShiftMap to move the origin of the
longitude IWC axis to a sensible value (it is left at zero otherwise).
We cannot do this with the latitude axis since pre-FITS-WCS fits
readers could not handle the resulting rotation from native to celestial
coords. */
if( !ret && astGetIsLinear( map ) ) {
nwcs = astGetNout( map );
npix = astGetNin( map );
iwc_origin = astMalloc( sizeof( double )*nwcs );
pix_origin = astMalloc( sizeof( double )*npix );
if( astOK ) {
for( i = 0; i < npix; i++ ) pix_origin[ i ] = 0.0;
astTranN( map, 1, npix, 1, pix_origin, 1, nwcs, 1, iwc_origin );
for( i = 0; i < nwcs; i++ ) {
if( i != ilon ) {
iwc_origin[ i ] = 0.0;
} else {
iwc_origin[ i ] *= -1;
}
}
mapa = (AstMapping *) astShiftMap( nwcs, iwc_origin, "", status );
*map1 = (AstMapping *) astCmpMap( map, mapa, 1, "", status );
*map2 = astWcsMap( nwcs, AST__CAR, ilon + 1, ilat + 1, "Invert=1", status );
astInvert( mapa );
*map3 = astClone( mapa );
mapa = astAnnul( mapa );
ret = 1;
}
iwc_origin = astFree( iwc_origin );
pix_origin = astFree( pix_origin );
}
/* If the above failed to find a suitable WcsMap, we now consider cases
where the output (long,lat) values are constants supplied by a
final PermMap. We can invent a WcsMap for such cases. */
if( !ret ) {
/* Transform two arbitrary pixel positions into the WCS Frame. */
npix = astGetNin( map );
nwcs = astGetNout( map );
pset1 = astPointSet( 2, npix, "", status );
pset2 = astPointSet( 2, nwcs, "", status );
ptr1 = astGetPoints( pset1 );
ptr2 = astGetPoints( pset2 );
w1 = astMalloc( sizeof( double )*(size_t) nwcs );
if( astOK ) {
for( i = 0; i < npix; i++ ) {
ptr1[ i ][ 0 ] = 1.0;
ptr1[ i ][ 1 ] = 1000.0;
}
(void) astTransform( map, pset1, 1, pset2 );
/* If the two wcs positions have equal longitude and latitude values,
assume that the output longitude and latitude axes are assigned
constant values by the Mapping. */
if( ptr2[ ilon ][ 0 ] == ptr2[ ilon ][ 1 ] &&
ptr2[ ilon ][ 0 ] != AST__BAD &&
ptr2[ ilat ][ 0 ] == ptr2[ ilat ][ 1 ] &&
ptr2[ ilat ][ 0 ] != AST__BAD ) {
/* Create a set of Mappings to return, including a WcsMap, which result in
these constant latitude and longitude values. We do this by creating a
FITS-WCS header and reading the FrameSet from it. Keywords which are not
important to the final mappings are given arbitrary values. */
fc = astFitsChan( NULL, NULL, "", status );
for( i = 0; i < nwcs; i++ ) {
sprintf( card, "CRPIX%d = 0", i + 1 );
astPutFits( fc, card, 0 );
sprintf( card, "CDELT%d = 0.0003", i + 1 );
astPutFits( fc, card, 0 );
if( i == ilon ) {
sprintf( card, "CTYPE%d = 'RA---TAN'", i + 1 );
} else if( i == ilat ) {
sprintf( card, "CTYPE%d = 'DEC--TAN'", i + 1 );
} else {
sprintf( card, "CTYPE%d = 'DUMMY'", i + 1 );
}
astPutFits( fc, card, 0 );
if( i == ilon ) {
sprintf( card, "CRVAL%d = %.*g", i + 1, DBL_DIG, AST__DR2D*ptr2[ ilon ][ 0 ] );
} else if( i == ilat ) {
sprintf( card, "CRVAL%d = %.*g", i + 1, DBL_DIG, AST__DR2D*ptr2[ ilat ][ 0 ] );
} else {
sprintf( card, "CRVAL%d = 0.0", i + 1 );
}
astPutFits( fc, card, 0 );
}
astClearCard( fc );
tfs = astRead( fc );
if( tfs ) {
/* Use SplitMap to get the required Mapings from the FrameSet. */
tmap2 = astGetMapping( tfs, AST__BASE, AST__CURRENT );
SplitMap( tmap2, astGetInvert( tmap2 ), 0, 1, &tmap1, map2,
map3, status );
tmap1 = astAnnul( tmap1 );
tmap2 = astAnnul( tmap2 );
/* Create a ShiftMap which subtract the constant longitude and latitude
values off the inputs. */
for( i = 0; i < nwcs; i++ ) w1[ i ] = 0.0;
w1[ ilon ] = -ptr2[ ilon ][ 0 ];
w1[ ilat ] = -ptr2[ ilat ][ 0 ];
tmap1 = (AstMapping *) astShiftMap( nwcs, w1, "", status );
/* Compose this with the supplied Mapping. This results in the celestial
outputs being zero. This gives the required "map1". */
*map1 = (AstMapping *) astCmpMap( map, tmap1, 1, "", status );
/* Indicate success.*/
ret = 1;
/* Free resources. */
tmap1 = astAnnul( tmap1 );
tfs = astAnnul( tfs );
}
fc = astAnnul( fc );
}
}
/* Free resources */
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
w1 = astFree( w1 );
}
if( !ret ) {
if( *map1 ) *map1 = astAnnul( *map1 );
if( *map2 ) *map2 = astAnnul( *map2 );
if( *map3 ) *map3 = astAnnul( *map3 );
}
return ret;
}
static int SplitMap2( AstMapping *map, int invert, AstMapping **map1,
AstWcsMap **map2, AstMapping **map3, int *status ){
/*
* Name:
* SplitMap2
* Purpose:
* Locate a WCS projection within a Mapping.
* Type:
* Private function.
* Synopsis:
* int SplitMap2( AstMapping *map, int invert, AstMapping **map1,
* AstWcsMap **map2, AstMapping **map3, int *status )
* Class Membership:
* FitsChan
* Description:
* If possible, the supplied Mapping is decomposed into three component
* mappings to be compounded in series. To be acceptable, the second of
* these three Mappings must be a WcsMap with a non-zero FITSProj value.
* If it is not possible to produce such a group of three Mappings, then a
* zero function value is returned, together with three NULL Mapping
* pointers. All the mappings before the WcsMap are compounded together
* and returned as "map1". The WcsMap itself is returned as "map2", and
* any remaining Mappings are compounded together and returned as "map3".
*
* The search algorithm allows for an arbitrary combination of series and
* parallel CmpMaps.
* Parameters:
* map
* A pointer to the Mapping from pixel to physical coordinates.
* invert
* The value of the Invert attribute to use with "map" (the value
* returned by astGetInvert is not used).
* map1
* A location at which to return a pointer to the Mapping from pixel
* to intermediate world coordinates.
* map2
* A location at which to return a pointer to the Mapping from relative
* physical coordinates to native spherical coordinates. This will
* be a WcsMap, and it will have a non-zero FITSProj value.
* map3
* A location at which to return a pointer to the Mapping from
* native spherical coordinates to physical coordinates.
* dep
* The address of an integer holding the current depth of recursion
* into this function.
* status
* Pointer to the inherited status variable.
* Returned Value:
* One if a suitable WcsMap was found, zero otherwise.
* Notes:
* - The returned Mappings contain independant copies of the relevant
* components of the supplied Mapping and can be modified without
* changing the supplied Mapping.
* - NULL pointers will be returned for all Mappings if no WcsMap
* with anon-zero FITSProj value can be found in the supplied Mapping.
* - A pointer to a UnitMap will be returned for map1 if no mappings
* exist before the WcsMap.
* - A pointer to a UnitMap will be returned for map3 if no mappings
* exist after the WcsMap.
* - NULL pointers will be returned for all Mappings and a function
* value of zero will be returned if an error has occurred, or if this
* function should fail for any reason.
* - "*map1" and "*map3" may contain WcsMaps, but they will have zero
* values for their FITSProj values.
*/
/* Local Variables */
AstMapping **map_list; /* Mapping array pointer */
AstMapping *mapa; /* Pre-wcs Mapping */
AstWcsMap *mapb; /* WcsMap */
AstMapping *mapc; /* Post-wcs Mapping */
AstMapping *temp; /* Intermediate Mapping */
const char *class; /* Pointer to class of supplied Mapping */
double pv; /* Projection parameter value */
int *invert_list; /* Invert array pointer */
int axis; /* No. of axes in whole Mapping */
int axlat; /* Index of latitude axis */
int axlon; /* Index of longitude axis */
int haswcs; /* Was a usable inverted WcsMap found? */
int imap; /* Index of current Mapping in list */
int i; /* axis index */
int m; /* Parameter index */
int nax; /* No. of axes in Mapping */
int nmap; /* Number of Mappings in the list */
int ret; /* Was a non-linear Mapping found? */
int wcsaxis; /* Index of first WcsMap axis */
/* Initialise */
*map1 = NULL;
*map2 = NULL;
*map3 = NULL;
ret = 0;
/* Check the global status. */
if( !astOK ) return ret;
/* Get the class of the Mapping. */
class = astGetClass( map );
/* If the supplied Mapping is a CmpMap... */
wcsaxis = -1;
if( !strcmp( class, "CmpMap" ) ){
/* Decompose the Mapping into a sequence of Mappings to be applied in
series and an associated list of Invert flags. */
map_list = NULL;
invert_list = NULL;
nmap = 0;
astMapList( map, 1, invert, &nmap, &map_list, &invert_list );
/* If there is more than one Mapping, this must be a series CmpMap. */
if( nmap > 1 && astOK ){
/* Initialise the returned pre-wcs Mapping to be a UnitMap. */
if( invert == astGetInvert( map ) ){
*map1 = (AstMapping *) astUnitMap( astGetNin( map ), "", status );
} else {
*map1 = (AstMapping *) astUnitMap( astGetNout( map ), "", status );
}
/* Indicate we have not yet found a WcsMap. */
ret = 0;
/* Process each series Mapping. */
for( imap = 0; imap < nmap; imap++ ){
/* If we have not yet found a WcsMap... */
if( !ret ){
/* Search this Mapping for a WcsMap. */
ret = SplitMap2( map_list[ imap ], invert_list[ imap ], &mapa,
map2, map3, status );
/* If no WcsMap was found, use the whole mapping as part of the
pre-wcs Mapping. */
if( !ret ){
mapa = astCopy( map_list[ imap ] );
astSetInvert( mapa, invert_list[ imap ] );
}
/* Add the pre-wcs mapping to the cumulative pre-wcs CmpMap. */
temp = (AstMapping *) astCmpMap( *map1, mapa, 1, "", status );
*map1 = astAnnul( *map1 );
mapa = astAnnul( mapa );
*map1 = temp;
/* If we have previously found a WcsMap, use the whole mapping
as part of the post-wcs mapping. */
} else {
mapc = astCopy( map_list[ imap ] );
astSetInvert( mapc, invert_list[ imap ] );
temp = (AstMapping *) astCmpMap( *map3, mapc, 1, "", status );
*map3 = astAnnul( *map3 );
mapc = astAnnul( mapc );
*map3 = temp;
}
}
/* If there is only one Mapping, this must be a parallel CmpMap. */
} else {
/* Annul the Mapping pointer in the series list created above, and free the
dynamic arrays. */
map_list[ 0 ] = astAnnul( map_list[ 0 ] );
map_list = astFree( map_list );
invert_list = astFree( invert_list );
nmap = 0;
/* Decompose the Mapping into a sequence of Mappings to be applied in
parallel and an associated list of Invert flags. */
astMapList( map, 0, invert, &nmap, &map_list, &invert_list );
/* Process each parallel Mapping. */
axis = 0;
for( imap = 0; imap < nmap && astOK; imap++ ){
/* See if this Mapping contains a usable WcsMap. Only do the search
if no such WcsMap has already been found, since only the first is usable. */
if( !ret ) {
/* Search this Mapping for a WcsMap. */
haswcs = SplitMap2( map_list[ imap ], invert_list[ imap ], &mapa,
&mapb, &mapc, status );
/* Note if we have found a usable WcsMap, and its first axis index. */
if( haswcs ){
ret = 1;
wcsaxis = axis;
}
/* If a WcsMap has already been found, the mapping cannot contain a
usable WcsMap. */
} else {
haswcs = 0;
}
/* If the Mapping did not contain a usable WcsMap, use the whole mapping as
part of the pre-wcs Mapping, and create a UnitMap as part of the post-wcs
mapping. */
if( !haswcs ){
mapa = astCopy( map_list[ imap ] );
astSetInvert( mapa, invert_list[ imap ] );
nax = astGetNout( mapa );
mapc = (AstMapping *) astUnitMap( nax, "", status );
}
/* Increment the index of the first axis in the next Mapping. */
axis += astGetNout( mapa );
/* Add the pre-wcs mapping in parallel with the cumulative pre-wcs CmpMap. */
if( *map1 ){
temp = (AstMapping *) astCmpMap( *map1, mapa, 0, "", status );
*map1 = astAnnul( *map1 );
mapa = astAnnul( mapa );
*map1 = temp;
} else {
*map1 = mapa;
}
/* Add the post-wcs mapping in parallel with the cumulative post-wcs CmpMap. */
if( *map3 ){
temp = (AstMapping *) astCmpMap( *map3, mapc, 0, "", status );
*map3 = astAnnul( *map3 );
mapc = astAnnul( mapc );
*map3 = temp;
} else {
*map3 = mapc;
}
}
/* If a usable WcsMap was found, create a new one which has all the same
properties, but with enough axes to join the pre and post wcs Mappings
together. Ensure the correct axes are used for longitude and latitude,
and copy the projection parameters. */
if( ret ){
axlat = astGetWcsAxis( mapb, 1 );
axlon = astGetWcsAxis( mapb, 0 );
*map2 = astWcsMap( axis, astGetWcsType( mapb ),
axlon + wcsaxis + 1,
axlat + wcsaxis + 1, "", status );
for( i = 0; i < astGetNin( mapb ); i++ ){
for( m = 0; m < WCSLIB_MXPAR; m++ ){
if( astTestPV( mapb, i, m ) ) {
pv = astGetPV( mapb, i, m );
if( pv != AST__BAD ) astSetPV( *map2, i + wcsaxis, m, pv );
}
}
}
astInvert( *map2 );
mapb = astAnnul( mapb );
}
}
/* Loop to annul all the Mapping pointers in the list. */
for ( imap = 0; imap < nmap; imap++ ) map_list[ imap ] = astAnnul( map_list[ imap ] );
/* Free the dynamic arrays. */
map_list = astFree( map_list );
invert_list = astFree( invert_list );
/* If the supplied Mapping is not a CmpMap, see if it is a WcsMap with a
non-zero FITSProj value. If so, take a copy and set its invert attribute
correctly. Also create UnitMaps for the pre and post wcs mappings. */
} else if( astOK && !strcmp( class, "WcsMap" ) && astGetFITSProj( map ) ){
ret = 1;
nax = astGetNin( map );
*map1 = (AstMapping *) astUnitMap( nax, "", status );
*map2 = astCopy( map );
astSetInvert( *map2, invert );
*map3 = (AstMapping *) astUnitMap( nax, "", status );
}
/* If an error has occurred, or if no suitable WcsMap was found, annul any
Mappings. */
if( !astOK || !ret ){
ret = 0;
if( *map1 ) *map1 = astAnnul( *map1 );
if( *map2 ) *map2 = astAnnul( *map2 );
if( *map3 ) *map3 = astAnnul( *map3 );
}
/* Return the answer. */
return ret;
}
static int SplitMat( int naxis, double *matrix, double *cdelt, int *status ){
/*
* Name:
* SplitMat
* Purpose:
* Factorises a single "CD"-style matrix into a diagonal CDELT matrix
* and a "PC"-style matrix.
* Type:
* Private function.
* Synopsis:
* int SplitMat( int naxis, double *matrix, double *cdelt, int *status )
* Class Membership:
* FitsChan
* Description:
* This function splits up the supplied CD matrix into separate PC and
* CDELT matrices. The product of the returned matrices (CDELT.PC)
* equals the supplied CD matrix. The CDELT values are chosen so that
* the corresponding row of the PC matrix represents a unit vector.
* The signs of the CDELT values are chosen so that the diagonal terms
* of the PC matrix are all positive.
*
* Parameters:
* naxis
* The number of axes.
* matrix
* A pointer to an array of naxis*naxis elements. On entry this holds
* the "CD" matrix. On exit, it is modified to represent the "PC"
* matrix.
* cdelt
* A pointer to an array of naxis elements. On exit this holds the CDELT
* values for each axis (i.e. the diagonal terms of the CDELT matrix).
* status
* Pointer to the inherited status variable.
* Returned Value:
* Zero is returned if any bad values are found in the supplied
* matrix, or if an error has already occurred. One is returned otherwise.
*/
/* Local Variables: */
int i;
int j;
int ok;
double *a;
int dineg;
double s2;
double cdlt;
/* Check the inherited status. */
if( !astOK ) return 0;
/* Assume success. */
ok = 1;
/* Loop round every row in the matrix. Get a pointer to the first element
in the row. */
for( i = 0; i < naxis; i++ ){
a = matrix + i*naxis;
/* Note the sign of the diagonal term (i.e. the i'th element) of this row. */
dineg = ( a[ i ] < 0.0 );
/* Get the magnitude of the vector represented by this row. This is the
CDELT value for the row. BAD values cause the whole function to return. */
s2 = 0.0;
for( j = 0; j < naxis; j++ ){
if( *a == AST__BAD ) {
ok = 0;
break;
}
s2 += (*a)*(*a);
a++;
}
if( !ok ) break;
cdlt = sqrt( MAX( 0.0, s2 ) );
/* If the diagonal term for this row of the matrix is negative, make
the CDELT value negative instead. This means that the diagonal term in
the final PC matrix will be positive. */
if( dineg ) cdlt = -cdlt;
/* Store the CDELT value. */
cdelt[ i ] = cdlt;
/* The row of the PC matrix is obtained by dividing the original row by
the CDELT value. Set to zero any PC values which are less than 1.0E-7
(such values may be produced by rounding errors). */
a = matrix + i*naxis;
for( j = 0; j < naxis; j++ ) {
if( cdlt != 0.0 ){
*a /= cdlt;
if( fabs( *a ) < 1.E-7 ) *a = 0.0;
} else {
*a = 0.0;
}
a++;
}
}
return ok;
}
static void TableSource( AstFitsChan *this,
void (* tabsource)( AstFitsChan *, const char *,
int, int, int * ),
int *status ){
/*
*++
* Name:
c astTableSource
f AST_TABLESOURCE
* Purpose:
c Register a source function for accessing tables in FITS files.
f Register a source routine for accessing tables in FITS files.
* Type:
* Public function.
* Synopsis:
c #include "fitschan.h"
c void astTableSource( AstFitsChan *this,
c void (* tabsource)( AstFitsChan *, const char *,
c int, int, int * ) )
f CALL AST_TABLESOURCE( THIS, TABSOURCE, STATUS )
* Class Membership:
* FitsChan member function.
* Description:
c This function can be used to register a call-back function
f This routine can be used to register a call-back routine
* with a FitsChan. The registered
c function
f routine
* is called when-ever the FitsChan needs to read information from a
* binary table contained within a FITS file. This occurs if the
c astRead
f AST_READ
* function is invoked to read a FrameSet from a set of FITS headers
* that use the "-TAB" algorithm to describe one or more axes. Such
* axes use a FITS binary table to store a look-up table of axis values.
* The FitsChan will fail to read such axes unless the "TabOK" attribute
* is set to a non-zero positive integer value. The table containing the
* axis values must be made available to the FitsChan either by storing
* the table contents in the FitsChan (using
c astPutTables or astPutTable) prior to invoking astRead
f AST_PUTTABLES or AST_PUTTABLE) prior to invoking AST_READ
* or by registering a call-back
c function using astTableSource.
f routine using AST_TABLESOURCE.
* The first method is possibly simpler, but requires that the name of
* the extension containing the table be known in advance. Since the
* table name is embedded in the FITS headers, the name is often not
* known in advance. If a call-back is registered, the FitsChan will
* determine the name of the required table and invoke the call-back
c function
f routine
* to supply the table at the point where it is needed (i.e. within
c the astRead method).
f the AST_READ method).
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
c tabsource
f TABSOURCE = SUBROUTINE (Given)
c Pointer to the table source function to use.
f The table source routine to use.
* It takes five arguments - the first is a pointer to the
* FitsChan, the second is a string holding the name of the
* FITS extension containing the required binary table ("EXTNAME"),
* the third is the integer FITS "EXTVER" header value for the
* required extension, the fourth is the integer FITS "EXTLEVEL"
* header value for the required extension, and the fifth is
c a pointer to
* the inherited integer status value.
*
* The call-back should read the entire contents (header and data)
* of the binary table in the named extension of the external FITS
* file, storing the contents in a newly created FitsTable object. It
* should then store this FitsTable in the FitsChan using the
c astPutTables or astPutTable
f AST_PUTTABLES or AST_PUTTABLE
* method, and finally annull its local copy of the FitsTable pointer.
* If the table cannot be read for any reason, or if any other
* error occurs, it should return a non-zero integer for the final
* (third) argument.
*
c If "tabsource" is NULL,
f If TABSOURCE is AST_NULL,
* any registered call-back function will be removed.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Notes:
c - Application code can pass arbitrary data (such as file
c descriptors, etc) to the table source function using the
c astPutChannelData function. The source function should use
c the astChannelData macro to retrieve this data.
f - The name of the routine supplied for the TABSOURCE
f argument should appear in an EXTERNAL statement in the Fortran
f routine which invokes AST_TABLESOURCE. 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).
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.
*--
*/
/* Check the global error status. */
if ( !astOK ) return;
/* Register the supplied source function, using the wrapper function
appropriate for calling C table source functions. */
astSetTableSource( this, (void (*)( void )) tabsource, TabSourceWrap );
}
static AstMapping *TabMapping( AstFitsChan *this, FitsStore *store, char s,
int **tabaxis, const char *method,
const char *class, int *status ) {
/*
* Name:
* TabMapping
* Purpose:
* Create a Mapping that performs any -TAB look-ups for all WCS axes.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstMapping *TabMapping( AstFitsChan *this, FitsStore *store, char s,
* int **tabaxis, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function returns a Mapping that has "nwcs" inputs and outputs,
* where "nwcs" is the number of FITS WCS axes defined in the supplied
* FitsStore. The inputs and outputs are in the same order as the
* CTYPEi keywords in the FitsStore. The forward transformation of the
* Mapping converts positions from the axes defined by the CRVALi keywords
* to the WCS axes. This transformation will be a UnitMap except for
* any axes that are described using the "-TAB" algorithm. For "-TAB"
* axes, the transformation will implement the relevant coordinate
* look-up function.
* Parameters:
* this
* Pointer to the FitsChan.
* store
* Pointer to the FitsStore structure holding the values to use for
* the WCS keywords.
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* tabaxis
* Address of a location at which to store a pointer to an array of
* flags, one for each output of the returned Mapping. A flag will
* be non-zero if the corresponding output of the returned Mapping
* corresponds to a -TAB axis. A NULL pointer is returned if the
* returned Mapping is NULL.
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to a Mapping. A NULL pointer is returned if the FitsChan does
* not support the -TAB algorithm (i.e. if the value of the TabOK
* attribute is zero or negative), or if no axes use the "-TAB" algorithm.
*/
/* Local Variables: */
AstFitsTable *table;
AstKeyMap *used_tables;
AstMapping *tmap1;
AstMapping *tmap2;
AstMapping *indexmap;
AstMapping *tmap0;
AstMapping *ret;
AstPermMap *pm;
char name[21];
const char *indexcol;
const char *extname;
const char *cval;
const char *ctype;
const char *coordscol;
double dval;
int *marray;
int *permin;
int *permout;
int extlevel;
int extver;
int iaxis;
int iiaxis;
int ikey;
int interp;
int ival;
int maxis;
int mdim;
int nkey;
int nperm;
int unit;
int wcsaxes;
/* Initialise */
ret = NULL;
*tabaxis = NULL;
extname = NULL;
tmap0 = NULL;
tmap2 = NULL;
/* Check the global status. */
if( !astOK ) return ret;
/* Obtain the number of physical axes in the header. If the WCSAXES header
was specified, use it. Otherwise assume it is the same as the number
of pixel axes. */
dval = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status );
if( dval != AST__BAD ) {
wcsaxes = (int) dval + 0.5;
} else {
wcsaxes = store->naxis;
}
/* If the FitsChan does not support the -TAB algorithm, return a NULL
pointer. */
if( astGetTabOK( this ) > 0 ) {
/* Create a KeyMap to hold a list of the used extension names. */
used_tables = astKeyMap( " ", status );
/* Allocate memory to indicate if each WCS axis is described by a -TAB
algorithm or not. Initialiss it to zero. */
*tabaxis = astCalloc( wcsaxes, sizeof( int ) );
/* Allocate memory to hold the FITS-WCS axis index corresponding to each
input of the "tmap0" Mapping. Indicate that as yet, not values are
stored in this array. Also allocate memory for the inverse of this
permutation array. */
permout = astMalloc( wcsaxes*sizeof( int ) );
permin = astMalloc( wcsaxes*sizeof( int ) );
nperm = 0;
if( astOK ) {
/* Initialise the permutation arrays. */
for( iaxis = 0; iaxis < wcsaxes; iaxis++ ) {
permout[ iaxis ] = permin[ iaxis ] = -1;
}
/* Otherwise, loop round all FITS WCS axis indices present in the FitsStore. */
for( iaxis = 0; iaxis < wcsaxes; iaxis++ ) {
/* If the current FITS WCS axis is already included in the returned
Mapping, skip it. This will be the case if the axis uses the same
coordinate array as an earlier axis since all FITS WCS axes associated
with a coordinate array are processed together. */
if( permin[ iaxis ] == -1 ) {
/* See if this WCS axis uses the -TAB algorithm. */
ctype = GetItemC( &(store->ctype), iaxis, 0, s, NULL, method,
class, status );
if( ctype && strlen(ctype) > 4 && !strncmp( ctype + 4, "-TAB", 4 ) ) {
/* Get the name of the FITS binary table extension holding the coordinate
info. No default, so report an error if not present. */
sprintf( name, "PS%d_0%c", iaxis + 1, s );
extname = GetItemC( &(store->ps), iaxis, 0, s, name, method,
class, status );
/* Get the extension version and level. */
dval = GetItem( &(store->pv), iaxis, 1, s, NULL, method,
class, status );
extver = ( dval != AST__BAD ) ? (int) dval : 1;
dval = GetItem( &(store->pv), iaxis, 2, s, NULL, method,
class, status );
extlevel = ( dval != AST__BAD ) ? (int) dval : 1;
/* Get the FITS binary table. This will invoke any supplied table source
function, and put a copy of the table into the FitsChan structure.
Report an error if the table can not be obtained. */
table = GetNamedTable( this, extname, extver, extlevel, 1,
method, status );
/* Add this extension name to a list of used extensions. */
astMapPut0I( used_tables, extname, 1, NULL );
/* Get the name of the table column containing the main coords array. No
default so report error if not present. Report an error if the column
is not present in the table. */
sprintf( name, "PS%d_1%c", iaxis + 1, s );
coordscol = GetItemC( &(store->ps), iaxis, 1, s, name, method,
class, status );
if( !astHasColumn( table, coordscol ) && astOK ) {
astError( AST__BADTAB, "%s(%s): Unable to find the "
"coordinate array for FITS-WCS axis %d (type %s): "
"column '%s' cannot be found in table '%s'.", status,
method, class, iaxis + 1, ctype, coordscol, extname );
}
/* Get the number of dimensions spanned by the coordinate array. Report
an error if the coordinate array has only one axis (FITS-WCS paper III
requires it to have at leats two axes). */
mdim = astGetColumnNdim( table, coordscol );
if( mdim == 1 && astOK ) {
astError( AST__BADTAB, "%s(%s): Unable to use the "
"coordinate array for FITS-WCS axis %d (type %s): "
"column '%s' in table '%s' has one axis but at "
"least two are required.", status, method, class,
iaxis + 1, ctype, coordscol, extname );
}
/* Allocate memory to hold the FITS-WCS axis corresponding to each dimension
of the coordinate array. Initialise it to hold -1 (i.e. "no matching
FITS-WCS axis yet found") at every element. */
marray = astMalloc( mdim*sizeof( int ) );
if( astOK ) {
for( maxis = 0; maxis < mdim; maxis++ ) {
marray[ maxis ] = -1;
}
/* Loop round each dimension of the coordinate array, storing the index
of the corresponding FITS-WCS axis in "marray". We omit the first axis
(axis 0) since FITS-WCS Paper III defines it is a "conventional" axis
used to enumerate the planes of coordinate values. */
for( maxis = 1; maxis < mdim && astOK ; maxis++ ) {
/* Each axis of the coordinate array (except axis 0) must have one, and only
one, corresponding FITS-WCS axis. Check each FITS-WCS axis to find one
that uses the same table and column as the "iaxis" axis, and which
corresponds to axis "maxis" of the coordinate array. */
for( iiaxis = 0; iiaxis < wcsaxes; iiaxis++ ) {
cval = GetItemC( &(store->ps), iiaxis, 0, s, NULL,
method, class, status );
if( cval && !strcmp( cval, extname ) ) {
cval= GetItemC( &(store->ps), iiaxis, 1, s, NULL,
method, class, status );
if( cval && !strcmp( cval, coordscol ) ) {
dval = GetItem( &(store->pv), iiaxis, 3, s,
NULL, method, class, status );
if( dval != AST__BAD ) {
ival = (int)( dval + 0.5 );
} else {
ival = 1;
}
if( ival == maxis ) {
/* Arrive here if the "iiaxis" FITS-WCS axis uses the same table and column
as "iaxis", and corresponds to the "maxis" axis in the coordinate
array. If this is the first matching FITS-WCS axis, store its index. */
if( marray[ maxis ] == -1 ) {
marray[ maxis ] = iiaxis;
/* If a matching FITS-WCS axis has already been found, report an error. */
} else if( astOK ) {
astError( AST__BADTAB, "%s(%s): Unable to use "
"the coordinate array for FITS-WCS "
"axis %d (type %s): more than one "
"intermediate WCS axis is mapped onto "
" dimension %d of the coordinate "
"array in column '%s' of table '%s'.",
status, method, class, iaxis + 1,
ctype, maxis, coordscol, extname );
}
}
}
}
}
}
/* Check that every dimension of the coordinate array (except the first) has
a corresponding FITS-WCS axis. */
for( maxis = 1; maxis < mdim && astOK ; maxis++ ) {
if( marray[ maxis ] == -1 ) {
astError( AST__BADTAB, "%s(%s): Unable to use the "
"coordinate array for FITS-WCS axis %d (type "
"%s): no intermediate WCS axis is mapped onto "
" dimension %d of the coordinate array in column "
" '%s' of table '%s'.", status, method, class,
iaxis + 1, ctype, maxis, coordscol, extname );
}
}
/* Now we know which FITS-WCS axis corresponds to each dimension of the
coordinate array. We now need to form a parallel CmpMap (compound Mapping)
by gathering together the indexing vectors for each dimension of the
coordinates array. Each indexing vector is represented by an inverted
1D LutMap - dimensions that do not have an indexing vector are
represented using a UnitMap. */
indexmap = NULL;
unit = 1;
for( maxis = 1; maxis < mdim && astOK ; maxis++ ) {
/* Get the name of the column containing the index array. Defaults is to
use a unit index, so do not report an error if not present. */
indexcol = GetItemC( &(store->ps), marray[ maxis ], 2,
s, NULL, method, class, status );
/* If the table contains an index vector, create a LutMap from it, then
invert it. */
if( indexcol ) {
tmap1 = MakeColumnMap( table, indexcol, 1, 0,
method, class, status );
astInvert( tmap1 );
unit = 0;
/* If the table does not contain an index vector, use a UnitMap. */
} else {
tmap1 = (AstMapping *) astUnitMap( 1, " ", status );
}
/* Combine the index Mapping for this dimension in parallel with the
Mapping for all earlier dimensions. */
if( indexmap ) {
tmap2 = (AstMapping *) astCmpMap( indexmap, tmap1,
0, " ", status );
indexmap = astAnnul( indexmap );
tmap1 = astAnnul( tmap1 );
indexmap = tmap2;
} else {
indexmap = tmap1;
}
}
/* Get the interpolation method to use for the main coordinate array.
This is an extension to the published -TAB algorithm in which the
QVi_4a keyword is assumed to hold zero for linear interpolation (the
default) and non-zero for nearest neighbour interpolation. The QVi_4a
keyword will be translated to PVi_4a by the SpecTrans function. */
dval = GetItem( &(store->pv), iaxis, 4, s,
NULL, method, class, status );
if( dval != AST__BAD ) {
interp = (int)( dval + 0.5 );
} else {
interp = 0;
}
/* Make a Mapping from the main coordinate array, and then if required
append it in series to the end of the index Mapping created above. */
tmap1 = MakeColumnMap( table, coordscol, 0, interp,
method, class, status );
if( ! unit ) {
tmap2 = (AstMapping *) astCmpMap( indexmap, tmap1, 1,
" ", status );
} else {
tmap2 = astClone( tmap1 );
}
indexmap = astAnnul( indexmap );
tmap1 = astAnnul( tmap1 );
/* Extend the array that holds the zero-based FITS-WCS axis index
corresponding to each input of the extended "tmap0" mapping. Also create
the inverse permutation (i.e. zero-based "tmap0" input indexed by
zero-based FITS-WCS axis index). */
for( maxis = 1; maxis < mdim; maxis++ ) {
permout[ nperm ] = marray[ maxis ];
permin[ marray[ maxis ] ] = nperm++;
}
/* Free resources. */
marray = astFree( marray );
}
/* Annul the table pointer. */
table = astAnnul( table );
/* Clear the CTYPE algorithm code to indicate that the axis should be
considered to be linear from now on. This means that the following
functions will create a Mapping from pixel to psi (the system in which
the CRVAL values are defined when using -TAB). The psi axes will then
be mapping into the CS axes using the Mappign returned by this function. */
strncpy( name, ctype, 4 );
strcpy( name + 4, ctype + 8 );
SetItemC( &(store->ctype), iaxis, 0, s, name, status );
/* Set the returned flag for this axis. */
(*tabaxis)[ iaxis ] = 1;
/* If the FITS WCS axis "iaxis" does not use a -TAB algorithm, describe
it in the returned Mapping using a 1D UnitMap. */
} else {
tmap2 = (AstMapping *) astUnitMap( 1, " ", status );
/* Extend the array that holds the zero-based FITS-WCS axis index
corresponding to each input of the extended "tmap0" mapping. Also create
the inverse permutation (i.e. zero-based "tmap0" input indexed by
zero-based FITS-WCS axis index). */
permout[ nperm ] = iaxis;
permin[ iaxis ] = nperm++;
}
/* Append the Mapping describing the FITS WCS axis "iaxis" in parallel to any
Mappings created for earlier "iaxis" axes. */
if( tmap0 ) {
tmap1 = (AstMapping *) astCmpMap( tmap0, tmap2, 0, " ", status );
tmap0 = astAnnul( tmap0 );
tmap2 = astAnnul( tmap2 );
tmap0 = tmap1;
} else {
tmap0 = tmap2;
}
}
}
/* If no -TAB axes were found, just return a NULL pointer. */
if( extname && astOK ) {
/* Do a sanity check on the permutation arrays. */
for( iaxis = 0; iaxis < wcsaxes; iaxis++ ) {
if( permin[ iaxis ] < 0 || permin[ iaxis ] >= wcsaxes ||
permout[ permin[ iaxis ] ] != iaxis ) {
astError( AST__INTER, "%s(%s): Invalid permutation "
"arrays in function TabMapping (internal AST "
"progranmming error).", status, method, class );
break;
}
}
/* Sandwich the "tmap0" Mapping in series between two PermMaps to create a
Mapping in which the inputs and outputs correspond to FITS WCS axis
numbering. */
pm = astPermMap( wcsaxes, permin, wcsaxes, permout, NULL, " ",
status );
tmap1 = (AstMapping *) astCmpMap( pm, tmap0, 1, " ", status );
astInvert( pm );
tmap2 = (AstMapping *) astCmpMap( tmap1, pm, 1, " ", status );
pm = astAnnul( pm );
tmap1 = astAnnul( tmap1 );
/* Simplify the returned Mapping. */
ret = astSimplify( tmap2 );
tmap2 = astAnnul( tmap2 );
}
/* Free remaining resources */
tmap0 = astAnnul( tmap0 );
}
permout = astFree( permout );
permin = astFree( permin );
/* Remove all used tables from the FitsChan now that they have been used. */
nkey = astMapSize( used_tables );
for( ikey = 0; ikey < nkey; ikey++ ) {
astRemoveTables( this, astMapKey( used_tables, ikey ) );
}
/* Delete the KeyMap holding the used table names. */
used_tables = astAnnul( used_tables );
/* If we are not returning a Mapping, ensure we do not return any axis
flags either. */
if( !ret ) *tabaxis = astFree( *tabaxis );
}
/* Return the result */
return ret;
}
static void TabSourceWrap( void (*tabsource)( void ),
AstFitsChan *this, const char *extname,
int extver, int extlevel, int *status ){
/*
* Name:
* TabSourceWrap
* Purpose:
* Wrapper function to invoke the C table source function.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void TabSourceWrap( void (*tabsource)( void ),
* AstFitsChan *this, const char *extname,
* int extver, int extlevel, int *status )
* Class Membership:
* Channel member function.
* Description:
* This function invokes the table source function whose pointer is
* supplied in order to read a named FITS binary table from an external
* FITS file.
* Parameters:
* tabsource
* Pointer to the C tab source function.
* this
* Pointer to the FitsChan. The reference count for the FitsChan is
* decremented by this function (this behaviour is imposed by
* restrictions in the equivalent Fortran wrapper function).
* extname
* Pointer to the string holding the name of the FITS extension
* from which a table is to be read.
* extver
* The integer "EXTVER" value for the required extension.
* extlevel
* The integer "EXTLEVEL" value for the required extension.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
AstFitsChan *this_id;
int lstat;
/* Check the global error status. */
if ( !astOK ) return;
/* Get an external identifier for the FitsChan. Could use astClone here
to avoid this function anulling the supplied pointer, but the F77 wrapper
cannot use the protected version of astClone, so for consistency we do
not use it here either. */
this_id = astMakeId( this );
/* Invoke the table source function (casting it to the C API first) to
read the table, and store it in the FitsChan. */
( *( void (*)( struct AstFitsChan *, const char *, int, int, int * ) )tabsource )( this_id, extname, extver, extlevel, &lstat );
/* Free the FitsChan identifier (this annuls the supplied "this" pointer). */
this_id = astAnnulId( this_id );
/* Report an error if the source function failed. */
if( !lstat ) {
astError( AST__NOTAB, "astRead(%s): The table source function failed to read "
"a binary table from extension %s in an external FITS file.",
status, astGetClass( this ), extname );
}
}
static double TDBConv( double mjd, int timescale, int fromTDB,
const char *method, const char *class, int *status ){
/*
* Name:
* TDBConv
* Purpose:
* Convert an MJD between the TDB time scale and another timescale.
* Type:
* Private function.
* Synopsis:
* double TDBConv( double mjd, int timescale, int fromTDB,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* This function converts the supplied mjd value to or from the TDB
* timescale.
* Parameters:
* mjd
* The input MJD value.
* timescale
* The other timescale.
* fromTDB
* Indicates the direction of the required conversion. If non-zero,
* the supplied "mjd" value should be in the TDB timescale, and the
* returned value will be in the timescale specified by "timescale".
* If zero, the supplied "mjd" value should be in the timescale
* specified by "timescale", and the returned value will be in the
* TDB timescale.
* method
* The calling method. Used only in error messages.
* class
* The object class. Used only in error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The converted MJD value, or AST__BAD if an error occurs.
*/
/* Local Variables: */
AstFrameSet *fs; /* Mapping from supplied timescale to TDB */
double ret; /* The returned value */
/* Initialise */
ret = AST__BAD;
/* Check inherited status and supplied TDB value. */
if( !astOK || mjd == AST__BAD ) return ret;
/* Return the supplied value if no conversion is needed. */
if( timescale == AST__TDB ) {
ret = mjd;
/* Otherwise, do the conversion. */
} else {
/* Lock the timeframes for use by the current thread, waiting if they are
currently locked by another thread. */
astManageLock( timeframe, AST__LOCK, 1, NULL );
astManageLock( tdbframe, AST__LOCK, 1, NULL );
/* Set the required timescale. */
astSetTimeScale( timeframe, timescale );
/* Get the Mapping between the two timescales, and use it to convert the
suipplied value. */
fs = astConvert( tdbframe, timeframe, "" );
astTran1( fs, 1, &mjd, fromTDB, &ret );
fs = astAnnul( fs );
/* Unlock the timeframes. */
astManageLock( timeframe, AST__UNLOCK, 1, NULL );
astManageLock( tdbframe, AST__UNLOCK, 1, NULL );
}
/* Return the result */
return ret;
}
static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
* Name:
* TestAttrib
* Purpose:
* Test if a specified attribute value is set for a FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int TestAttrib( AstObject *this, const char *attrib, int *status )
* Class Membership:
* FitsChan 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 FitsChan's attributes.
* Parameters:
* this
* Pointer to the FitsChan.
* 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: */
AstFitsChan *this; /* Pointer to the FitsChan structure */
int result; /* Result value to return */
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_object;
/* Card. */
/* ----- */
if ( !strcmp( attrib, "card" ) ) {
result = astTestCard( this );
/* Encoding. */
/* --------- */
} else if ( !strcmp( attrib, "encoding" ) ) {
result = astTestEncoding( this );
/* FitsAxisOrder. */
/* -------------- */
} else if ( !strcmp( attrib, "fitsaxisorder" ) ) {
result = astTestFitsAxisOrder( this );
/* FitsDigits. */
/* ----------- */
} else if ( !strcmp( attrib, "fitsdigits" ) ) {
result = astTestFitsDigits( this );
/* DefB1950. */
/* --------- */
} else if ( !strcmp( attrib, "defb1950" ) ) {
result = astTestDefB1950( this );
/* TabOK. */
/* ------ */
} else if ( !strcmp( attrib, "tabok" ) ) {
result = astTestTabOK( this );
/* CDMatrix. */
/* --------- */
} else if ( !strcmp( attrib, "cdmatrix" ) ) {
result = astTestCDMatrix( this );
/* CarLin. */
/* --------- */
} else if ( !strcmp( attrib, "carlin" ) ) {
result = astTestCarLin( this );
/* PolyTan */
/* ------- */
} else if ( !strcmp( attrib, "polytan" ) ) {
result = astTestPolyTan( this );
/* Iwc. */
/* ---- */
} else if ( !strcmp( attrib, "iwc" ) ) {
result = astTestIwc( this );
/* Clean. */
/* ------ */
} else if ( !strcmp( attrib, "clean" ) ) {
result = astTestClean( this );
/* Warnings. */
/* -------- */
} else if ( !strcmp( attrib, "warnings" ) ) {
result = astTestWarnings( this );
/* If the name is not recognised, test if it matches any of the
read-only attributes of this class. If it does, then return
zero. */
} else if ( !strcmp( attrib, "ncard" ) ||
!strcmp( attrib, "nkey" ) ||
!strcmp( attrib, "cardtype" ) ||
!strcmp( attrib, "cardcomm" ) ||
!strcmp( attrib, "cardname" ) ||
!strcmp( attrib, "allwarnings" ) ){
result = 0;
/* If the attribute is still not recognised, pass it on to the parent
method for further interpretation. */
} else {
result = (*parent_testattrib)( this_object, attrib, status );
}
/* Return the result, */
return result;
}
static int TestCard( AstFitsChan *this, int *status ){
/*
*+
* Name:
* astTestCard
* Purpose:
* Test the Card attribute.
* Type:
* Protected virtual function.
* Synopsis:
* #include "fitschan.h"
* int astTestCard( AstFitsChan *this )
* Class Membership:
* FitsChan method.
* Description:
* This function tests the Card attribute for the supplied FitsChan.
* Parameters:
* this
* Pointer to the FitsChan.
* Returned Value:
* If the Card attribute has its "cleared" value (i.e. if the first card
* in the FitsChan will be the next one to be read), then zero is returned,
* otherwise 1 is returned.
*-
*/
/* Local Variables: */
int card; /* The original value of Card */
int ret; /* The returned flag */
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Get the current value of Card. */
card = astGetCard( this );
/* Temporarily clear Card. */
astClearCard( this );
/* See if the original Card is equal to the cleared card, and set the
returned flag appropriately. Re-instate the original value of card is
required.*/
if( astGetCard( this ) == card ) {
ret = 0;
} else {
astSetCard( this, card );
ret = 1;
}
/* Return the flag. */
return ret;
}
static int TestFits( AstFitsChan *this, const char *name, int *there,
int *status ){
/*
*++
* Name:
c astTestFits
f AST_TESTFITS
* Purpose:
* See if a named keyword has a defined value in a FitsChan.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c int astTestFits( AstFitsChan *this, const char *name, int *there )
f RESULT = AST_TESTFITS( THIS, NAME, THERE, STATUS )
* Class Membership:
* FitsChan method.
* Description:
* This function serches for a named keyword in a FitsChan. If found,
* and if the keyword has a value associated with it, a
c non-zero
f .TRUE.
* value is returned. If the keyword is not found, or if it does not
* have an associated value, a
c zero
f .FALSE.
* value is returned.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
c name
f NAME = CHARACTER * ( * ) (Given)
c Pointer to a null-terminated character string
f A character string
* containing the FITS keyword name. This may be a complete FITS
* header card, in which case the keyword to use is extracted from
* it. No more than 80 characters are read from this string.
c there
f THERE = LOGICAL (Returned)
c Pointer to an integer which will be returned holding a non-zero
c value if the keyword was found in the header, and zero otherwise.
f A value of .TRUE. will be returned if the keyword was found in the
f header, and .FALSE. otherwise.
* This parameter allows a distinction to be made between the case
* where a keyword is not present, and the case where a keyword is
* present but has no associated value.
c A NULL pointer may be supplied if this information is not
c required.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Returned Value:
c astTestFits()
f AST_TESTFITS = LOGICAL
* A value of zero
f .FALSE.
* is returned if the keyword was not found in the FitsChan or has
* no associated value. Otherwise, a value of
c one
f .TRUE.
* is returned.
* Notes:
* - The current card is left unchanged by this function.
* - The card following the current card is checked first. If this is
* not the required card, then the rest of the FitsChan is searched,
* starting with the first card added to the FitsChan. Therefore cards
* should be accessed in the order they are stored in the FitsChan (if
* possible) as this will minimise the time spent searching for cards.
* - An error will be reported if the keyword name does not conform
* to FITS requirements.
c - Zero
f - .FALSE.
* is returned as the function value if an error has already occurred,
* or if this function should fail for any reason.
*--
*/
/* Local Variables: */
const char *class; /* Object class */
const char *method; /* Calling method */
char *lcom; /* Supplied keyword comment */
char *lname; /* Supplied keyword name */
char *lvalue; /* Supplied keyword value */
int icard; /* Current card index on entry */
int ret; /* The returned value */
/* Initialise */
if( there ) *there = 0;
/* Check the global error status. */
if ( !astOK ) return 0;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Store the calling method and object class. */
method = "astTestFits";
class = astGetClass( this );
/* Initialise the returned value. */
ret = 0;
/* Extract the keyword name from the supplied string. */
(void) Split( this, name, &lname, &lvalue, &lcom, method, class, status );
/* Store the current card index. */
icard = astGetCard( this );
/* Attempt to find a card in the FitsChan refering to this keyword,
and make it the current card. Only proceed if a card was found. */
if( SearchCard( this, lname, method, class, status ) ){
/* Indicate the card has been found. */
if( there ) *there = 1;
/* If the cards data type is no undefined, return 1. */
if( CardType( this, status ) != AST__UNDEF ) ret = 1;
}
/* Re-instate the original current card index. */
astSetCard( this, icard );
/* Release the memory used to hold keyword name, value and comment strings. */
lname = (char *) astFree( (void *) lname );
lvalue = (char *) astFree( (void *) lvalue );
lcom = (char *) astFree( (void *) lcom );
/* Return the answer. */
return ret;
}
static void TidyOffsets( AstFrameSet *fset, int *status ) {
/*
* Name:
* TidyOffsets
* Purpose:
* Remove un-needed offset coordinate Frames.
* Type:
* Private function.
* Synopsis:
* void TidyOffsets( AstFrameSet *fset, int *status )
* Class Membership:
* FitsChan
* Description:
* A FITS header stores offset sky coordinates as two alternaive axis
* descriptions - one giving the offset axes and one giving the absolute
* axes. But AST can hold both forms in a single SkyFrame. This function
* removes the FITS Frames describing offset axes from the FrameSet.
* The remaining absolute Frame is then used to describe both absolute
* and offset.
* Parameters:
* fset
* A FrameSet holding the Frames read from a FITS-WCS Header.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
AstFrame *frm;
AstFrame *pfrm;
const char *dom;
const char *skyrefis;
int hasabs;
int hasoff;
int iax;
int icurr;
int icurr_is_offset;
int ifrm;
int nax;
int nfrm;
int pax;
int remove;
/* Check the inherited status. */
if( !astOK ) return;
/* Note the original current Frame index. */
icurr = astGetCurrent( fset );
/* Assume the current Frame is not an offset frame until proven
otherwise. */
icurr_is_offset = 0;
/* Does the FrameSet contain any Frames holding sky offsets? Such Frames
should have been given a Domain of SKY_OFFSETS within function
WcsSkyFrame. Loop round all Frames, checking each one. Also note if
the FrameSet contains any (absolute) SKY frames. Also set the SkyRefIs
attribute for any absolute SkyFrames that were marked with domains
SKY_POLE or SKY_OFFSET in WcsSkyFrame. */
hasabs = 0;
hasoff = 0;
nfrm = astGetNframe( fset );
for( ifrm = 1; ifrm <= nfrm; ifrm++ ){
skyrefis = NULL;
frm = astGetFrame( fset, ifrm );
nax = astGetNaxes( frm );
for( iax = 0; iax < nax; iax++ ) {
astPrimaryFrame( frm, iax, &pfrm, &pax );
if( IsASkyFrame( pfrm ) ) {
dom = astGetDomain( pfrm );
if( dom ) {
if( !strcmp( dom, "SKY_OFFSETS" ) ){
hasoff = 1;
if( ifrm == icurr ) icurr_is_offset = 1;
iax = nax;
} else if( !strcmp( dom, "SKY" ) ){
hasabs = 1;
iax = nax;
} else if( !strcmp( dom, "SKY_POLE" ) ){
hasabs = 1;
skyrefis = "POLE";
iax = nax;
} else if( !strcmp( dom, "SKY_ORIGIN" ) ){
hasabs = 1;
skyrefis = "ORIGIN";
iax = nax;
}
}
}
pfrm = astAnnul( pfrm );
}
frm = astAnnul( frm );
if( skyrefis ) {
astSetI( fset, "Current", ifrm);
astSetC( fset, "SkyRefIs", skyrefis );
astSetI( fset, "Current", icurr );
}
}
/* If one or more absolute sky frames were found, then remove any offset
sky frames. Clear the Ident attribute (that holds the FITS-WCS alternate
axis description character) for any absoute Frames. */
if( hasabs && hasoff ) {
for( ifrm = nfrm; ifrm > 0; ifrm-- ) {
remove = 0;
frm = astGetFrame( fset, ifrm );
nax = astGetNaxes( frm );
for( iax = 0; iax < nax; iax++ ) {
astPrimaryFrame( frm, iax, &pfrm, &pax );
if( IsASkyFrame( pfrm ) ) {
dom = astGetDomain( pfrm );
if( dom ) {
if( !strcmp( dom, "SKY_OFFSETS" ) ){
remove = 1;
iax = nax;
} else if( !strcmp( dom, "SKY_POLE" ) ||
!strcmp( dom, "SKY_ORIGIN" ) ){
astClearIdent( frm );
astClearDomain( pfrm );
/* If we will be deleting the original current Frame (because it is an
offset Frame), then mark the first absolute Frame as the new current
Frame. */
if( icurr_is_offset ) {
astSetCurrent( fset, ifrm );
icurr_is_offset = 0;
}
iax = nax;
}
}
}
pfrm = astAnnul( pfrm );
}
frm = astAnnul( frm );
if( remove ) astRemoveFrame( fset, ifrm );
}
}
}
static AstTimeScaleType TimeSysToAst( AstFitsChan *this, const char *timesys,
const char *method, const char *class, int *status ){
/*
* Name:
* TimeSysToAst
* Purpose:
* Convert a FITS TIMESYS value to an AST TimeFrame timescale value.
* Type:
* Private function.
* Synopsis:
* AstTimeScaleType TimeSysToAst( AstFitsChan *this, const char *timesys,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* This function returns the value used by the AST TimeFrame class to
* represent the timescale specified by the "timesys" parameter, which
* should hold the value of a FITS TIMESYS keyword. The TIMESYS
* convention was introduced as part of the Y2K DATE-OBS changes, and
* is not currently part of the published FITS-WCS conventions.
*
* If the requested timescale is not supported by AST, then a warning is
* added to the FitsChan and a value of AST__UTC is returned (but no
* error is reported).
* Parameters:
* this
* Pointer to the FitsChan.
* timesys
* Pointer to the string holding the TIMESYS value. A NULL pointer
* returns the default timescale of UTC.
* 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 equivalent AstTimeScaleType value.
*/
/* Local Variables: */
AstTimeScaleType result; /* The returned timescale */
char buf[ 200 ]; /* Buffer for warning message */
/* Initialise */
result = AST__UTC;
/* Check the inherited status. */
if( !astOK ) return result;
if( !timesys ) {
result = AST__UTC;
} else if( !strcmp( timesys, "UTC" ) ) {
result = AST__UTC;
} else if( !strcmp( timesys, "UT" ) ) {
result = AST__UTC;
Warn( this, "badval", "The original FITS header contained a value of UT "
"for keyword TIMESYS which is being interpreted as UTC.", method,
class, status );
} else if( !strcmp( timesys, "TAI" ) ) {
result = AST__TAI;
} else if( !strcmp( timesys, "IAT" ) ) {
result = AST__TAI;
} else if( !strcmp( timesys, "ET" ) ) {
result = AST__TT;
Warn( this, "badval", "The original FITS header contained a value of ET "
"for keyword TIMESYS. TT will be used instead.", method, class, status );
} else if( !strcmp( timesys, "TT" ) ) {
result = AST__TT;
} else if( !strcmp( timesys, "TDT" ) ) {
result = AST__TT;
} else if( !strcmp( timesys, "TDB" ) ) {
result = AST__TDB;
} else if( !strcmp( timesys, "TCG" ) ) {
result = AST__TCG;
} else if( !strcmp( timesys, "TCB" ) ) {
result = AST__TCB;
} else {
result = AST__UTC;
sprintf( buf, "The original FITS header contained a value of %s for "
"keyword TIMESYS. AST does not support this timescale so "
"UTC will be used instead.", timesys );
Warn( this, "badval", buf, method, class, status );
}
/* Return the result */
return result;
}
static char *UnPreQuote( const char *string, int *status ) {
/*
* Name:
* UnPreQuote
* Purpose:
* Reverse the pre-quoting of FITS character data.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* char *UnPreQuote( const char *string, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* This function reverses the effect of the PreQuote function on a
* string (apart from any loss of data due to truncation). It
* should be used to recover the original character data from the
* pre-quoted version of a string retrieved from a FITS character
* value associated with a keyword.
* Parameters:
* string
* Pointer to a constant null-terminated string containing the
* pre-quoted character data.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to a dynamically allocated null-terminated string
* containing the un-quoted character data. The memory holding this
* string should be freed by the caller (using astFree) when no
* longer required.
* Notes:
* - A NULL pointer value will be returned if this function is
* invoked wth the global error status set, or if it should fail
* for any reason.
*/
/* Local Variables: */
char *result; /* Pointer value to return */
int i1; /* Offset of first useful character */
int i2; /* Offest of last useful character */
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Initialise to use the first and last characters in the input
string. */
i1 = 0;
i2 = strlen( string ) - 1;
/* If the string contains at least 2 characters, check if the first
and last characters are double quotes ("). If so, adjust the
offsets to exclude them. */
if ( ( i2 > i1 ) &&
( string[ i1 ] == '"' ) && ( string[ i2 ] == '"' ) ) {
i1++;
i2--;
}
/* Make a dynamically allocated copy of the useful part of the
string. */
result = astString( string + i1, i2 - i1 + 1 );
/* Return the answer. */
return result;
}
static int Use( AstFitsChan *this, int set, int helpful, int *status ) {
/*
* Name:
* Use
* Purpose:
* Decide whether to write a value to a FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int Use( AstFitsChan *this, int set, int helpful, int *status )
* Class Membership:
* FitsChan 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 a FitsChan.
*
* This decision is based on the settings of the "set" and
* "helpful" flags supplied to the astWrite... method, plus the
* attribute settings of the FitsChan.
* Parameters:
* this
* A pointer to the FitsChan.
* 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 FitsChan'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 "fitschan.h"
* int Ustrcmp( const char *a, const char *b, int *status )
* Class Membership:
* FitsChan 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 "fitschan.h"
* int Ustrncmp( const char *a, const char *b, size_t n, int *status )
* Class Membership:
* FitsChan 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 void Warn( AstFitsChan *this, const char *condition, const char *text,
const char*method, const char *class, int *status ){
/*
* Name:
* Warn
* Purpose:
* Store warning cards in a FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int Warn( AstFitsChan *this, const char *condition, const char *text,
* const char*method, const char *class, int *status );
* Class Membership:
* FitsChan member function.
* Description:
* If the Warnings attribute indicates that occurences of the specified
* condition should be reported, the supplied text is split into lines
* and stored in the FitsChan as a series of ASTWARN cards, in front
* of the current card. If the specified condition is not being reported,
* this function returns without action.
* Parameters:
* this
* The FitsChan. If NULL, this function returns without action.
* condition
* Pointer to a string holding a lower case condition name.
* text
* Pointer to a string holding the text of the warning.
* 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.
*/
/* Local Variables: */
char buff[ AST__FITSCHAN_FITSCARDLEN + 1 ]; /* Buffer for new card text */
const char *a; /* Pointer to 1st character in next card */
const char *b; /* Pointer to terminating null character */
const char *c; /* Pointer to last character in next card */
int exists; /* Has the supplied warning already been issued? */
int icard; /* Index of original card */
int nc; /* No. of characters in next card */
/* Check the inherited status, warning text, FitsChan and Clean attribute. */
if( !astOK || !text || !text[0] || !this || astGetClean( this ) ) return;
/* Ignore the warning if the supplied condition is not contained within
the list of conditions to be reported in this way (given by the
Warnings attribute). */
if( FullForm( astGetWarnings( this ), condition, 0, status ) >= 0 ){
/* If found, store the warning in the parent Channel structure. */
astAddWarning( this, 1, "%s", method, status, text );
/* For historical reasons, warnings are also stored in the FitsChan as a
set of FITS cards... First save the current card index, and rewind the
FitsChan. */
icard = astGetCard( this );
astClearCard( this );
/* Break the supplied text into lines and check the FitsChan to see if
a block of adjacent ASTWARN cards with these lines already exist
within the FitsChan. Assume they do until proven otherwise. */
exists = 1;
a = text;
b = a + strlen( text );
while( a < b ){
/* Each card contains about 60 characters of the text. Get a pointer to
the nominal last character in the next card. */
c = a + 60;
/* If this puts the last character beyond the end of the text, use the
last character before the null as the last character in the card. */
if( c >= b ) {
c = b - 1;
/* Otherwise, if the last character is not a space, move the last
character backwards to the first space. This avoids breaking words
across cards. */
} else {
while( !isspace( *c ) && c > a ) c--;
}
/* Copy the text into a null terminated buffer. */
nc = c - a + 1;
strncpy( buff, a, nc );
buff[ nc ] = 0;
/* If this is the first line, search the entire FitsChan for an ASTWARN card
with this text. If not, indiate that the supplied text needs to be
stored in the FitsChan, and break out of the loop. */
if( a == text ) {
exists = 0;
while( !exists &&
FindKeyCard( this, "ASTWARN", method, class, status ) ) {
if( !strcmp( (const char *) CardData( this, NULL, status ), buff ) ) {
exists = 1;
}
MoveCard( this, 1, method, class, status );
}
if( !exists ) break;
/* If this is not the first line, see if the next card in the FitsChan is
an ASTWARN card with this text. If not, indiate that the supplied text
needs to be stored in the FitsChan, and break out of the loop. */
} else {
if( !strcmp( CardName( this, status ), "ASTWARN" ) &&
!strcmp( (const char *) CardData( this, NULL, status ), buff ) ) {
MoveCard( this, 1, method, class, status );
} else {
exists = 0;
break;
}
}
/* Set the start of the next bit of the text. */
a = c + 1;
}
/* Reinstate the original current card index. */
astSetCard( this, icard );
/* We only add new cards to the FitsChan if they do not already exist. */
if( !exists ) {
/* Break the text into lines using the same algorithm as above, and store
each line as a new ASTWARN card. Start with a blank ASTWARN card. */
astSetFitsS( this, "ASTWARN", " ", NULL, 0 );
/* Loop until the entire text has been written out. */
a = text;
b = a + strlen( text );
while( a < b ){
/* Each card contains about 60 characters of the text. Get a pointer to
the nominal last character in the next card. */
c = a + 60;
/* If this puts the last character beyond the end of the text, use the
last character before the null as the last character in the card. */
if( c >= b ) {
c = b - 1;
/* Otherwise, if the last character is not a space, move the last
character backwards to the first space. This avoids breaking words
across cards. */
} else {
while( !isspace( *c ) && c > a ) c--;
}
/* Copy the text into a null terminated buffer. */
nc = c - a + 1;
strncpy( buff, a, nc );
buff[ nc ] = 0;
/* Store the buffer as the next card. */
astSetFitsS( this, "ASTWARN", buff, NULL, 0 );
/* Set the start of the next bit of the text. */
a = c + 1;
}
/* Include a final blank card. */
astSetFitsS( this, "ASTWARN", " ", NULL, 0 );
}
}
}
static int WATCoeffs( const char *watstr, int iaxis, double **cvals,
int **mvals, int *ok, int *status ){
/*
* Name:
* WATCoeffs
* Purpose:
* Get the polynomial coefficients from the lngcor or latcor component
* of an IRAF WAT string.
* Type:
* Private function.
* Synopsis:
* int WATCoeffs( const char *watstr, int iaxis, double **cvals,
* int **mvals, int *ok, int *status )
* Class Membership:
* FitsChan
* Description:
* This function extracts the polynomial coefficients from a supplied
* string containing the concatenated values of a set of IRAF "WAT"
* keywords, such as used for the IRAF-specific TNX and ZPX projections.
* The coefficients are returned in the form of a set of PVi_m values
* for a TPN projection.
* Parameters:
* watstr
* The concatentated WAT keyword values.
* iaxis
* Zero based index of the axis to which the WAT keywords refer (0
* or 1).
* cvals
* Location at which to return a pointer to a dynamically allocated
* list of coefficient values, or NULL if no lngcor/latcor values
* were found in the WAT string. Free using astFree.
* mvals
* Location at which to return a pointer to a dynamically allocated
* list of coefficient indices, or NULL if no lngcor/latcor values
* were found in the WAT string. Free using astFree.
* ok
* Pointer to an in which is returned set to zero if the polynomial
* in the supplied WAT string cannot be represented using TPN form.
* Non-zero otherwise.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The size of the returned cvals and mvals arrays.
*/
/* Local Variables: */
char **w1;
char **w2;
double *coeff;
double *pc;
int result;
double dval;
double etamax;
double etamin;
double ximax;
double ximin;
int cheb;
int etaorder;
int iword;
int m;
int mn;
int nword;
int order;
int porder;
int xiorder;
int ires;
/* The number of lngcor/latcor values needed for each order. */
static const int nab[] = {1,3,6,10,15,21,28,36};
/* Initialise the pointer to the returned Mapping. */
result = 0;
*mvals = NULL;
*cvals = NULL;
*ok = 1;
/* Other initialisation to avoid compiler warnings. */
etamin = 0.0;
etamax = 0.0;
ximax = 0.0;
ximin = 0.0;
order = 0;
/* Check the global status. */
if ( !astOK || !watstr ) return result;
/* Look for cor = "..." and extract the "..." string. */
w1 = astChrSplitRE( watstr, "cor *= *\"(.*)\"", &nword, NULL );
if( w1 ) {
/* Split the "..." string into words. */
w2 = astChrSplit( w1[ 0 ], &nword );
if( w2 ) {
/* Initialise flags. */
cheb = 0;
xiorder = 0;
etaorder = 0;
coeff = NULL;
porder = -1;
/* Loop round each word. Break early if we find that the projection
cannot be represented as a TPN projection. */
for( iword = 0; iword < nword && *ok; iword++ ) {
/* Convert the word to double. */
dval = astChr2Double( w2[ iword ] );
if( dval == AST__BAD ) {
astError( AST__BDFTS, "astRead(FitsChan): Failed to read a "
"numerical value from sub-string \"%s\" found in "
"an IRAF \"WAT...\" keyword.", status, w2[ iword ] );
break;
}
/* The first value gives the correction surface type. We can only handle type
1 (chebyshev) or 3 (simple polynomial). */
if( iword == 0 ){
if( dval == 1.0 ) {
cheb = 1;
} else if( dval == 2.0 ) {
*ok = 0;
}
/* The second and third numbers gives the orders of the polynomial in X
and Y. We can only handle cases in which the orders are the same on
both axes, and greater than 0 and less than 8. Store a pointer to the
first TAN projection parameter index to use. */
} else if( iword == 1 ){
order = dval;
porder = order - 1;
} else if( iword == 2 ){
if( dval - 1 != porder || dval < 0 || dval > 7 ) *ok = 0;
/* The fourth number defines the type of cross-terms. We can only handle
type 2 (half-cross terms). */
} else if( iword == 3 ){
if( dval != 2.0 ) *ok = 0;
/* We now know the maximum number of co-efficients that may be needed.
Allocate memory to hold them, and fill it with zeros. They are
stored in this array as if full cross-terms have been supplied (the
unspecified coefficients retain their initialised value of zero). */
coeff = astCalloc( order*order, sizeof( double ) );
if( !astOK ) break;
/* The next 4 numbers describe the region of validity of the fits in IRAF's
xi and eta space, e.g. ximin, ximax, etamin, etamax. We only uses
these if we have a chebyshev polynomial. */
} else if( iword == 4 ) {
ximin = dval;
} else if( iword == 5 ) {
ximax = dval;
} else if( iword == 6 ) {
etamin = dval;
} else if( iword == 7 ) {
etamax = dval;
/* The remaining terms are the coefficients of the polynomial terms. */
} else if( iword > 7 ){
/* Store the coefficient in the array. They are stored so that power of
xi increases fastest. */
coeff[ xiorder + order*etaorder ] = dval;
/* Increment the powers of the next coefficient. We know we only have half
cross-terms, so the maximum power of xi decreases from order to zero
as we move through the list of coefficients. */
if( ++xiorder == order - etaorder ) {
xiorder = 0;
etaorder++;
}
}
}
/* Check that all the required co-efficients were found */
if( porder == -1 || nword != 8 + nab[ porder ] ) *ok = 0;
/* If we can handle the projection, proceed. */
if( *ok && astOK ) {
/* If the coefficients were supplied in chebyshev form, convert to simple
form. */
if( cheb ) {
double *tcoeff = coeff;
coeff = Cheb2Poly( tcoeff, order, order, ximin,
ximax, etamin, etamax, status );
tcoeff = astFree( tcoeff );
}
/* The polynomials provide a "correction* to be added to the supplied X and
Y values. Therefore increase the linear co-efficients by 1 on the axis
that is being calculated. */
coeff[ iaxis ? order : 1 ] += 1.0;
/* Loop round all coefficients, keeping track of the power of xi and eta
for the current coefficient. */
pc = coeff;
for( etaorder = 0; etaorder < order; etaorder++ ) {
for( xiorder = 0; xiorder < order; xiorder++,pc++ ) {
/* Skip coefficients that have their default values (zero, except for the
linear coefficients which default to 1.0). */
mn = xiorder + etaorder;
if( *pc != ( mn == 1 ? 1.0 : 0.0 ) ) {
/* Find the "m" index of the PVi_m FITS keyword for the current
coefficient. */
m = mn*( 1 + mn )/2 + mn/2;
m += iaxis ? xiorder : etaorder;
/* Append the PV and m values to the ends of the returned arrays. */
ires = result++;
*cvals = astGrow( *cvals, sizeof( double ), result );
*mvals = astGrow( *mvals, sizeof( int ), result );
if( astOK ) {
(*cvals)[ ires ] = *pc;
(*mvals)[ ires ] = m;
}
}
}
}
/* Free coefficients arrays */
coeff = astFree( coeff );
}
/* Free resources */
w2 = astFree( w2 );
}
w1 = astFree( w1 );
}
/* Return the result. */
return result;
}
static AstMatrixMap *WcsCDeltMatrix( FitsStore *store, char s, int naxes,
const char *method, const char *class, int *status ){
/*
* Name:
* WcsCDeltMatrix
* Purpose:
* Create a MatrixMap representing the CDELT scaling.
* Type:
* Private function.
* Synopsis:
* AstMatrixMap *WcsCDeltMatrix( FitsStore *store, char s, int naxes,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* A diagonal MatrixMap representing the FITS "CDELT" keywords is
* returned.
* Parameters:
* store
* A structure containing values for FITS keywords relating to
* the World Coordinate System.
* s
* A character s identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* naxes
* The number of intermediate world coordinate axes (WCSAXES).
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the created MatrixMap or a NULL pointer if an
* error occurred.
*/
/* Local Variables: */
AstMatrixMap *new; /* The created MatrixMap */
double *el; /* Pointer to next matrix element */
double *mat; /* Pointer to matrix array */
int i; /* Pixel axis index */
/* Initialise/ */
new = NULL;
/* Check the global status. */
if ( !astOK ) return new;
/* Allocate memory for the diagonal matrix elements. */
mat = (double *) astMalloc( sizeof(double)*naxes );
if( astOK ){
/* Fill the matrix diagonal with values from the FitsStore. */
el = mat;
for( i = 0; i < naxes; i++ ){
/* Get the CDELTi value for this axis. Missing terms can be defaulted so
do not report an error if the required value is not present in the
FitsStore. */
*el = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
/* Missing terms default to to 1.0. */
if( *el == AST__BAD ) *el = 1.0;
/* Move on to the next matrix element. */
el++;
}
/* Create the diagional matrix. */
new = astMatrixMap( naxes, naxes, 1, mat, "", status );
/* Report an error if the inverse transformation is undefined. */
if( !astGetTranInverse( new ) && astOK ) {
astError( AST__BDFTS, "%s(%s): Unusable CDELT values found "
"in the FITS-WCS header - one or more values are zero.", status, method, class );
}
/* Release the memory used to hold the matrix. */
mat = (double *) astFree( (void *) mat );
}
/* If an error has occurred, attempt to annul the returned MatrixMap. */
if( !astOK ) new = astAnnul( new );
/* Return the MatrixMap. */
return new;
}
static AstMapping *WcsCelestial( AstFitsChan *this, FitsStore *store, char s,
AstFrame **frm, AstFrame *iwcfrm, double *reflon, double *reflat,
AstSkyFrame **reffrm, AstMapping **tabmap,
int *tabaxis, const char *method,
const char *class, int *status ){
/*
* Name:
* WcsCelestial
* Purpose:
* Create a Mapping from intermediate world coords to celestial coords
* as described in a FITS header.
* Type:
* Private function.
* Synopsis:
* AstMapping *WcsCelestial( AstFitsChan *this, FitsStore *store, char s,
* AstFrame **frm, AstFrame *iwcfrm, double *reflon, double *reflat,
* AstSkyFrame **reffrm, , AstMapping **tabmap,
* int *tabaxis, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* This function interprets the contents of the supplied FitsStore
* structure, looking for world coordinate axes which describe positions
* on the sky. If a pair of such longitude/latitude axes is found, a
* Mapping is returned which transforms the corresponding intermediate
* world coordinates to celestial world coordinates (this mapping leaves
* any other axes unchanged). It also, modifies the supplied Frame to
* describe the axes (again, other axes are left unchanged). If no
* pair of celestial axes is found, a UnitMap is returned, and the
* supplied Frame is left unchanged.
* Parameters:
* this
* The FitsChan.
* store
* A structure containing information about the requested axis
* descriptions derived from a FITS header.
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* frm
* The address of a location at which to store a pointer to the
* Frame describing the world coordinate axes.
* iwcfrm
* A pointer to the Frame describing the intermediate world coordinate
* axes. The properties of this Frame may be changed on exit.
* reflon
* Address of a location at which to return the celestial longitude
* at the reference point. It is returned as AST__BAD if no
* celestial coordinate frame is found.
* reflat
* Address of a location at which to return the celestial latitude
* at the reference point. It is returned as AST__BAD if no
* celestial coordinate frame is found.
* reffrm
* Address of a location at which to return a pointer to a SkyFrame
* which define the reference values returned in reflon and reflat.
* It is returned as NULL if no celestial coordinate frame is found.
* tabmap
* Address of a pointer to a Mapping describing any -TAB
* transformations to be applied to the results of the Mapping returned
* by this function. If any celestial axes are found, the supplied
* Mapping is modified so that the celestial axes produce values in
* radians rather than degrees. NULL if no axes are described by -TAB.
* tabaxis
* Pointer to an array of flags, one for each WCS axis, indicating
* if the corresponding WCS axis is described by the -TAB algorithm.
* NULL if no axes are described by -TAB.
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the Mapping.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
AstFrame *ofrm; /* Pointer to a Frame */
AstMapping *map1; /* Pointer to a Mapping */
AstMapping *map2; /* Pointer to a Mapping */
AstMapping *map3; /* Pointer to a Mapping */
AstMapping *map4; /* Pointer to a Mapping */
AstMapping *ret; /* Pointer to the returned Mapping */
AstMapping *newmap; /* Modified PIXEL->IWC Mapping */
AstMapping *shiftmap; /* ShiftMap from IWC to PPC */
AstSkyFrame *sfrm; /* Pointer to a SkyFrame */
char *ctype; /* Pointer to CTYPE string */
char *keyname; /* Pointer to keyword name string */
char buf[300]; /* Text buffer */
char latctype[MXCTYPELEN];/* Latitude CTYPE keyword value */
char latkey[10]; /* Latitude CTYPE keyword name */
char lattype[4]; /* Buffer for celestial system */
char lonctype[MXCTYPELEN];/* Longitude CTYPE keyword value */
char lonkey[10]; /* Longitude CTYPE keyword name */
char lontype[4]; /* Buffer for celestial system */
double *shifts; /* Array holding axis shifts */
double *ina; /* Pointer to memory holding input position A */
double *inb; /* Pointer to memory holding input position B */
double *mat; /* Pointer to data for deg->rad scaling matrix */
double *outa; /* Pointer to memory holding output position A */
double *outb; /* Pointer to memory holding output position B */
double latval; /* CRVAL for latitude axis */
double lonval; /* CRVAL for longitude axis */
double pv; /* Projection parameter value */
double x0; /* IWC X at the projection fiducial point */
double y0; /* IWC Y at the projection fiducial point */
int *axes; /* Point to a list of axis indices */
int axlat; /* Index of latitude physical axis */
int axlon; /* Index of longitude physical axis */
int carlin; /* Assume native and WCS axes are the same? */
int ctlen; /* Length of CTYPE string */
int gotax; /* Celestial axis found? */
int i; /* Loop count */
int j; /* Axis index */
int latprj; /* Latitude projection type identifier */
int lonprj; /* Longitude projection type identifier */
int m; /* Parameter index */
int mxpar_lat; /* Max. projection parameter index on lat axis */
int mxpar_lon; /* Max. projection parameter index on lon axis */
int naxes; /* Number of axes */
int nc; /* String length */
int np; /* Max parameter index */
int prj; /* Projection type identifier */
/* Initialise the returned values. */
ret = NULL;
*reflon = AST__BAD;
*reflat = AST__BAD;
*reffrm = NULL;
/* Other initialisation to avoid compiler warnings. */
map1 = NULL;
/* Check the global status. */
if ( !astOK ) return ret;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this);
/* Get the number of physical axes. */
naxes = astGetNaxes( *frm );
/* See if CAR projections should be interpreted in the old fashioned way
(i.e native coords are always the same as WCS coords, so no need for
any rotation). */
carlin = astGetCarLin( this );
/* The first major section sees if the physical axes include a pair of
longitude/latitude celestial axes.
================================================================= */
/* We have not yet found any celestial axes. */
axlon = -1;
axlat = -1;
latprj = AST__WCSBAD;
lonprj = AST__WCSBAD;
prj = AST__WCSBAD;
/* First, we examine the CTYPE values in the FitsStore to determine
which axes are the longitude and latitude axes, and what the celestial
co-ordinate system and projection are. Loop round the physical axes,
getting each CTYPE value. */
for( i = 0; i < naxes && astOK; i++ ){
keyname = FormatKey( "CTYPE", i + 1, -1, s, status );
ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
/* Issue a warning if no CTYPE value was found. */
if( !ctype ) {
sprintf( buf, "Axis type keywords (CTYPE, etc) were not found "
"for one or more axes in the original FITS header. These "
"axes will be assumed to be linear." );
Warn( this, "noctype", buf, method, class, status );
} else {
/* See if this is a longitude axis (e.g. if the first 4 characters of CTYPE
are "RA--" or "xLON" or "yzLN" ). If so, store the value of "x" or "yz"
(or "EQU" for equatorial coordinates) in variable "type" to indicate which
coordinate system is being used. */
nc = strlen( ctype );
gotax = 0;
if( !strcmp( ctype, "RA" ) || !strncmp( ctype, "RA--", 4 ) ){
strcpy( wcscelestial_type, "EQU" );
gotax = 1;
} else if( !strcmp( ctype, "AZ" ) || !strncmp( ctype, "AZ--", 4 ) ){
strcpy( wcscelestial_type, "AZL" );
gotax = 1;
} else if( nc > 1 && ( !strcmp( ctype + 1, "LON" ) ||
!strncmp( ctype + 1, "LON-", 4 ) ) ){
wcscelestial_type[ 0 ] = ctype[ 0 ];
wcscelestial_type[ 1 ] = 0;
gotax = 1;
} else if( nc > 2 && ( !strcmp( ctype + 2, "LN" ) ||
!strncmp( ctype + 2, "LN-", 3 ) ) ){
wcscelestial_type[ 0 ] = ctype[ 0 ];
wcscelestial_type[ 1 ] = ctype[ 1 ];
wcscelestial_type[ 2 ] = 0;
gotax = 1;
}
/* If this is a longitude axis... */
if( gotax ){
/* Check that this is the first longitude axis to be found. */
if( axlon == -1 ){
/* Find the projection type as specified by the last 4 characters
in the CTYPE keyword value. AST__WCSBAD is stored in "prj" if the
last 4 characters do not specify a known WCS projection, but no error
is reported. Assume simple linear axes if no projection code is
supplied. Note, AST__WCSBAD is used to indicate a TAB header. */
ctlen = strlen( ctype );
if( ctlen > 4 ) {
prj = astWcsPrjType( ctype + ctlen - 4 );
} else if( tabmap && *tabmap ) {
prj = AST__WCSBAD;
} else {
prj = AST__CAR;
carlin = 1;
}
/* Report an error if the projection is unknown. */
if( prj == AST__WCSBAD && ctlen > 4 ){
astError( AST__BDFTS, "%s(%s): FITS keyword '%s' refers to "
"an unknown projection type '%s'.", status, method, class,
keyname, ctype + ctlen - 4 );
break;
}
/* Store the index of the longitude axis, type of longitude, etc. */
axlon = i;
strcpy( lontype, wcscelestial_type );
strcpy( lonkey, keyname );
strcpy( lonctype, ctype );
lonprj = prj;
/* If another longitude axis has already been found, report an error. */
} else {
astError( AST__BDFTS, "%s(%s): FITS keywords '%s' (='%s') "
"and '%s' (='%s') both describe celestial longitude axes.", status,
method, class, keyname, ctype, lonkey, lonctype );
break;
}
}
/* Do the same for the latitude axis, checking for "DEC-" and "xLAT" and
"yzLT". */
gotax = 0;
if( !strcmp( ctype, "DEC" ) || !strncmp( ctype, "DEC-", 4 ) ){
strcpy( wcscelestial_type, "EQU" );
gotax = 1;
} else if( !strcmp( ctype, "EL" ) || !strncmp( ctype, "EL--", 4 ) ){
strcpy( wcscelestial_type, "AZL" );
gotax = 1;
} else if( !strcmp( ctype + 1, "LAT" ) || !strncmp( ctype + 1, "LAT-", 4 ) ){
wcscelestial_type[ 0 ] = ctype[ 0 ];
wcscelestial_type[ 1 ] = 0;
gotax = 1;
} else if( !strcmp( ctype + 2, "LT" ) || !strncmp( ctype + 2, "LT-", 3 ) ){
wcscelestial_type[ 0 ] = ctype[ 0 ];
wcscelestial_type[ 1 ] = ctype[ 1 ];
wcscelestial_type[ 2 ] = 0;
gotax = 1;
}
if( gotax ){
if( axlat == -1 ){
ctlen = strlen( ctype );
if( ctlen > 4 ) {
prj = astWcsPrjType( ctype + ctlen - 4 );
} else if( tabmap && *tabmap ) {
prj = AST__WCSBAD;
} else {
prj = AST__CAR;
carlin = 1;
}
if( prj == AST__WCSBAD && ctlen > 4 ){
astError( AST__BDFTS, "%s(%s): FITS keyword '%s' refers to "
"an unknown projection type '%s'.", status, method, class,
keyname, ctype + ctlen - 4 );
break;
}
axlat = i;
strcpy( lattype, wcscelestial_type );
strcpy( latkey, keyname );
strcpy( latctype, ctype );
latprj = prj;
} else {
astError( AST__BDFTS, "%s(%s): FITS keywords '%s' (='%s') "
"and '%s' (='%s') both describe celestial latitude axes.", status,
method, class, keyname, ctype, latkey, latctype );
break;
}
}
}
}
/* Check the above went OK */
if( astOK ){
/* If both longitude and latitude axes were found... */
if( axlat != -1 && axlon != -1 ){
/* Report an error if they refer to different celestial coordinate systems. */
if( strcmp( lattype, lontype ) ){
astError( AST__BDFTS, "%s(%s): FITS keywords '%s' and '%s' "
"indicate different celestial coordinate systems "
"('%s' and '%s').", status, method, class, latkey, lonkey,
latctype, lonctype );
/* Otherwise report an error if longitude and latitude axes use different
projections. */
} else if( lonprj != latprj ){
astError( AST__BDFTS, "%s(%s): FITS keywords '%s' and '%s' "
"indicate different projections ('%s' and '%s').", status,
method, class, latkey, lonkey, latctype, lonctype );
}
/* If only one axis has been provided without the other (e.g. longitude but no
latitude), report an error. */
} else if( axlat != -1 && prj != AST__WCSBAD ){
astError( AST__BDFTS, "%s(%s): A latitude axis ('%s') was found "
"without a corresponding longitude axis.", status, method, class,
latctype );
} else if( axlon != -1 && prj != AST__WCSBAD ){
astError( AST__BDFTS, "%s(%s): A longitude axis ('%s') was found "
"without a corresponding latitude axis.", status, method, class,
lonctype );
}
}
/* If a pair of matching celestial axes was not found, return a UnitMap
and leave the Frame unchanged.
===================================================================== */
if( axlat == -1 || axlon == -1 ) {
ret = (AstMapping *) astUnitMap( naxes, "", status );
/* The rest of this function deals with creating a Mapping from
intermediate world coords to celestial coords, and modifying the
Frame appropriately.
===================================================================== */
} else if( astOK ) {
/* Create a MatrixMap which scales the intermediate world coordinate axes
corresponding to the longitude and latitude axes from degrees to radians.
Only do this if a projection was supplied. */
if( latprj != AST__WCSBAD ) {
mat = (double *) astMalloc( sizeof(double)*naxes );
if( mat ){
for( i = 0; i < naxes; i++ ){
if( i == axlat || i == axlon ){
mat[ i ] = AST__DD2R;
} else {
mat[ i ] = 1.0;
}
}
map1 = (AstMapping *) astMatrixMap( naxes, naxes, 1, mat, "", status );
mat = (double *) astFree( (void *) mat );
}
} else {
map1 = (AstMapping *) astUnitMap( naxes, " ", status );
}
/* If the projection is a CAR projection, but the CarLin attribute is
set, then we consider the CAR projection to be a simple linear mapping
of pixel coords to celestial coords. Do this by using a WcsMap with no
projection. All axes will then be treated as linear and non-celestial.
If no projection was specified (i.e. if prj == AST__WCSBAD, as is the
case when using -TAB for instance) then do the same but use a UnitMap
instead of a WcsMap. */
map3 = NULL;
if( ( latprj == AST__CAR && carlin ) || latprj == AST__WCSBAD ) {
if( latprj == AST__CAR ) {
map2 = (AstMapping *) astWcsMap( naxes, AST__WCSBAD, axlon + 1,
axlat + 1, "", status );
} else {
map2 = (AstMapping *) astUnitMap( naxes, "", status );
}
/* Now create a WinMap which adds on the CRVAL values to each axis. */
ina = astMalloc( sizeof(double)*naxes );
inb = astMalloc( sizeof(double)*naxes );
outa = astMalloc( sizeof(double)*naxes );
outb = astMalloc( sizeof(double)*naxes );
if( astOK ) {
for( i = 0; i < naxes; i++ ) {
ina[ i ] = 0.0;
inb[ i ] = 1.0;
outa[ i ] = 0.0;
outb[ i ] = 1.0;
}
lonval = GetItem( &(store->crval), axlon, 0, s, NULL, method, class, status );
if( lonval != AST__BAD ) {
/* For recognised projections the CRVAL value is required to be degrees,
so convert to radians. For other algorithms (e.g. -TAB) the CRVAL
values are in unknown units so retain their original scaling. */
*reflon = ( latprj == AST__CAR ) ? lonval*AST__DD2R : lonval;
outa[ axlon ] += *reflon;
outb[ axlon ] += *reflon;
} else {
outa[ axlon ] = AST__BAD;
outb[ axlon ] = AST__BAD;
}
latval = GetItem( &(store->crval), axlat, 0, s, NULL, method, class, status );
if( latval != AST__BAD ) {
*reflat = ( latprj == AST__CAR ) ? latval*AST__DD2R : latval;
outa[ axlat ] += *reflat;
outb[ axlat ] += *reflat;
} else {
outa[ axlat ] = AST__BAD;
outb[ axlat ] = AST__BAD;
}
map3 = (AstMapping *) astWinMap( naxes, ina, inb, outa, outb, "", status );
}
ina = astFree( ina );
inb = astFree( inb );
outa = astFree( outa );
outb = astFree( outb );
/* Otherwise, create a WcsMap with the specified projection. The WcsMap
is equivalent to a unit mapping for all axes other than "axlat" and
"axlon". */
} else {
/* Get the highest index ("m" value) of any supplied PVi_m projection
parameters (on any axes). */
np = GetMaxJM( &(store->pv), s, status );
/* Create the WcsMap */
map2 = (AstMapping *) astWcsMap( naxes, latprj, axlon + 1,
axlat + 1, "", status );
/* If the FITS header contains any projection parameters, store them in
the WcsMap. */
mxpar_lat = astGetPVMax( map2, axlat );
mxpar_lon = astGetPVMax( map2, axlon );
for( m = 0; m <= np; m++ ){
pv = GetItem( &(store->pv), axlat, m, s, NULL, method, class, status );
if( pv != AST__BAD ) {
if( m <= mxpar_lat ) {
astSetPV( map2, axlat, m, pv );
} else {
sprintf( buf, "Projection parameter PV%d_%d found, "
"but is not used by %s projections.", axlat + 1,
m, astWcsPrjName( astGetWcsType( map2 ) ) );
Warn( this, "badpv", buf, method, class, status );
}
}
pv = GetItem( &(store->pv), axlon, m, s, NULL, method, class, status );
if( pv != AST__BAD ) {
if( m <= mxpar_lon ) {
astSetPV( map2, axlon, m, pv );
} else {
sprintf( buf, "Projection parameter PV%d_%d found, "
"but is not used by %s projections.", axlon + 1,
m, astWcsPrjName( astGetWcsType( map2 ) ) );
Warn( this, "badpv", buf, method, class, status );
}
}
}
/* Invert the WcsMap to get a DEprojection. */
astInvert( map2 );
/* Now produce a Mapping which converts the axes holding "Native Spherical
Coords" into "Celestial Coords", leaving all other axes unchanged. */
map3 = WcsNative( this, store, s, (AstWcsMap *) map2, -1, -1,
method, class, status );
/* Retrieve and store the reference longitude and latitude. */
*reflon = GetItem( &(store->crval), axlon, 0, s, NULL, method, class, status );
if( *reflon != AST__BAD ) *reflon *= AST__DD2R;
*reflat = GetItem( &(store->crval), axlat, 0, s, NULL, method, class, status );
if( *reflat != AST__BAD ) *reflat *= AST__DD2R;
}
/* If projection parameter PVi_0a for the longitude axis "i" is non-zero,
then there is a shift of origin between Intermediate World Coords, IWC,
(the CRPIXi values correspond to the origin of IWC), and Projection Plane
Coords, PPC (these are the cartesian coordinates used by the WcsMap).
This shift of origin results in the fiducial point specified by the
CRVALi values mapping onto the pixel reference point specified by the
CRPIXj values. In this case we need to add a Mapping which implements
the shift of origin. Note, the AST-specific "TPN" projection cannot use
this convention since it uses PVi_0 to hold a polynomial correction term. */
if( latprj != AST__WCSBAD && astGetWcsType( map2 ) != AST__TPN &&
astGetPV( map2, axlon, 0 ) != 0.0 ) {
/* Find the projection plane coords corresponding to the fiducial point
of the projection. This is done by using the inverse WcsMap to convert
the native spherical coords at the fiducial point into PPC (x,y), which
are returned in units of radians (not degrees). */
GetFiducialPPC( (AstWcsMap *) map2, &x0, &y0, status );
if( x0 != AST__BAD && y0 != AST__BAD ) {
/* Allocate resources. */
shifts = astMalloc( sizeof( double )*(size_t) naxes );
/* Check pointers can be used safely. */
if( astOK ) {
/* Create a Mapping (a ShiftMap) from IWC to PPC. */
for( i = 0; i < naxes; i++ ) shifts[ i ] = 0.0;
shifts[ axlon ] = x0;
shifts[ axlat ] = y0;
shiftmap = (AstMapping *) astShiftMap( naxes, shifts, "", status );
/* Produce a CmpMap which combines "map1" (which converts degrees to
radians on the celestial axes) with the above ShiftMap. */
newmap = (AstMapping *) astCmpMap( map1, shiftmap, 1, "", status );
/* Annul the component Mappings and use the new one in place of map1. */
shiftmap = astAnnul( shiftmap );
map1 = astAnnul( map1 );
map1 = newmap;
}
/* Free resources. */
shifts = astFree( shifts );
}
}
/* Now concatenate the Mappings to produce the returned Mapping. */
map4 = (AstMapping *) astCmpMap( map1, map2, 1, "", status );
ret = (AstMapping *) astCmpMap( map4, map3, 1, "", status );
/* Annul the component Mappings. */
map1 = astAnnul( map1 );
map2 = astAnnul( map2 );
map3 = astAnnul( map3 );
map4 = astAnnul( map4 );
/* We now make changes to the supplied Frame so that the longitude and
latitude axes are described by a SkyFrame. First create an appropriate
SkyFrame. */
sfrm = WcsSkyFrame( this, store, s, prj, wcscelestial_type, axlon,
axlat, method, class, status );
/* The values currently stored in *reflat and *reflon are the CRVAL
values. In some circumstances, these may not be the original values in
the supplied header but may have been translated within the SpecTrans
function as part of the process of translating an old unsupported
projection into a new supported projection. Since the returned RefLat
and RefLon values may be used to set the reference position for a
SpecFrame, we should return the original values rather than the
translated values. The original values will have been stored (within
SpecTrans) in the FitsChan as keywords RFVALi. If such keywords can
be found, use their values in preference to the currently stored CRVAL
values.*/
if( GetValue( this, FormatKey( "RFVAL", axlon + 1, -1, s, status ),
AST__FLOAT, (void *) &lonval, 0, 0, method, class, status ) &&
GetValue( this, FormatKey( "RFVAL", axlat + 1, -1, s, status ),
AST__FLOAT, (void *) &latval, 0, 0, method, class, status ) ) {
*reflon = lonval*AST__DD2R;
*reflat = latval*AST__DD2R;
}
/* Store the reflon and reflat values as the SkyRef position in the
SkyFrame, and set SkyRefIs to "ignore" so that the SkyFrame continues
to represent absolute celestial coords. Do not change the SkyFrame if
it already had a set reference posiiton. */
if( ! astTestSkyRef( sfrm, 0 ) ) {
if( *reflon != AST__BAD && *reflat != AST__BAD ) {
astSetSkyRef( sfrm, 0, *reflon );
astSetSkyRef( sfrm, 1, *reflat );
astSet( sfrm, "SkyRefIs=Ignored", status );
}
}
/* Return a clone of this SkyFrame as the reference Frame. */
*reffrm = astClone( sfrm );
/* Create a Frame by picking all the other (non-celestial) axes from the
supplied Frame. */
axes = astMalloc( naxes*sizeof( int ) );
if( axes ) {
j = 0;
for( i = 0; i < naxes; i++ ) {
if( i != axlat && i != axlon ) axes[ j++ ] = i;
}
/* If there were no other axes, replace the supplied Frame with the skyframe. */
if( j == 0 ) {
(void) astAnnul( *frm );
*frm = (AstFrame *) astClone( sfrm );
/* Otherwise pick the other axes from the supplied Frame */
} else {
ofrm = astPickAxes( *frm, j, axes, NULL );
/* Replace the supplied Frame with a CmpFrame made up of this Frame and
the SkyFrame. */
(void) astAnnul( *frm );
*frm = (AstFrame *) astCmpFrame( ofrm, sfrm, "", status );
ofrm = astAnnul( ofrm );
}
/* Permute the axis order to put the longitude and latitude axes back in
their original position. The SkyFrame will have the default axis
ordering (lon=axis 0, lat = axis 1). */
j = 0;
for( i = 0; i < naxes; i++ ) {
if( i == axlat ) {
axes[ i ] = naxes - 1;
} else if( i == axlon ) {
axes[ i ] = naxes - 2;
} else {
axes[ i ] = j++;
}
}
astPermAxes( *frm, axes );
/* Free the axes array. */
axes= astFree( axes );
}
/* Set the units in the supplied IWC Frame for the longitude and latitude
axes. Unless using -TAB, these are degrees (the conversion from degs to
rads is part of the Mapping from IWC to WCS). If using -TAB the units
are unknown. */
if( !tabaxis || !tabaxis[ axlon ] ) astSetUnit( iwcfrm, axlon, "deg" );
if( !tabaxis || !tabaxis[ axlat ] ) astSetUnit( iwcfrm, axlat, "deg" );
/* Modify any supplied tabmap so that the celestial outputs create
radians rather than degrees (but only if the celestial axes are
generated by the -TAB algorithm). */
if( tabaxis && tabaxis[ axlon ] && tabaxis[ axlat ] ) {
mat = (double *) astMalloc( sizeof(double)*naxes );
if( mat ){
for( i = 0; i < naxes; i++ ){
if( i == axlat || i == axlon ){
mat[ i ] = AST__DD2R;
} else {
mat[ i ] = 1.0;
}
}
map1 = (AstMapping *) astMatrixMap( naxes, naxes, 1, mat, "", status );
mat = (double *) astFree( (void *) mat );
map2 = (AstMapping *) astCmpMap( *tabmap, map1, 1, " ", status );
map1 = astAnnul( map1 );
(void) astAnnul( *tabmap );
*tabmap = map2;
}
/* Also modify the returned reflon and reflat values to transform them
using the tabmap. Also transform the reference position in the SkyFrame. */
if( *reflon != AST__BAD && *reflat != AST__BAD ) {
ina = astMalloc( sizeof(double)*naxes );
outa = astMalloc( sizeof(double)*naxes );
if( astOK ) {
for( i = 0; i < naxes; i++ ) ina[ i ] = 0.0;
ina[ axlat ] = *reflat;
ina[ axlon ] = *reflon;
astTranN( *tabmap, 1, naxes, 1, ina, 1, naxes, 1, outa );
*reflon = outa[ axlon ];
*reflat = outa[ axlat ];
}
ina = astFree( ina );
outa = astFree( outa );
/* Store this transformed reference position in the SkyFrame. */
astSetSkyRef( sfrm, 0, *reflon );
astSetSkyRef( sfrm, 1, *reflat );
astSet( sfrm, "SkyRefIs=Ignored", status );
}
}
/* If the header contains AXREF values for both lon and lat axes, use
them as the sky reference position in preferences to the values
derived form the CRVAL values. AXREF keywords are created by the
astWrite method for axes described by -TAB algorithm that have no inverse
transformation. */
if( GetValue( this, FormatKey( "AXREF", axlon + 1, -1, s, status ),
AST__FLOAT, (void *) &lonval, 0, 0, method, class, status ) &&
GetValue( this, FormatKey( "AXREF", axlat + 1, -1, s, status ),
AST__FLOAT, (void *) &latval, 0, 0, method, class, status ) ) {
*reflon = lonval*AST__DD2R;
*reflat = latval*AST__DD2R;
astSetSkyRef( sfrm, 0, *reflon );
astSetSkyRef( sfrm, 1, *reflat );
astSet( sfrm, "SkyRefIs=Ignored", status );
}
/* Free resources. */
sfrm = astAnnul( sfrm );
}
/* Return the result. */
return ret;
}
static void WcsFcRead( AstFitsChan *fc, AstFitsChan *fc2, FitsStore *store,
const char *method, const char *class, int *status ){
/*
* Name:
* WcsFcRead
* Purpose:
* Extract WCS information from a supplied FitsChan using a FITSWCS
* encoding, and store it in the supplied FitsStore.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void WcsFcRead( AstFitsChan *fc, AstFitsChan *fc2, FitsStore *store,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* A FitsStore is a structure containing a generalised represention of
* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
* from a set of FITS header cards (using a specified encoding), or
* an AST FrameSet. In other words, a FitsStore is an encoding-
* independant intermediary staging post between a FITS header and
* an AST FrameSet.
*
* This function extracts FITSWCS keywords from the supplied FitsChan,
* and stores the corresponding WCS information in the supplied FitsStore.
* Parameters:
* fc
* Pointer to the FitsChan containing the cards read from the
* original FITS header. This should not include any un-used
* non-standard keywords.
* fc2
* Pointer to a second FitsChan. If a card read from "fc" fails to
* be converted to its correct data type, a warning is only issued
* if there is no card for this keyword in "fc2". "fc2" may be NULL
* in which case a warning is always issued.
* store
* Pointer to the FitsStore structure.
* 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.
*/
/* Local Variables: */
char buf[200]; /* Buffer for warning message */
char *cval; /* String keyword value */
char *keynam; /* Pointer to current keyword name */
char s; /* Co-ordinate version character */
double dval; /* Floating point keyword value */
int fld[2]; /* Integer field values from keyword name */
int jm; /* Pixel axis or projection parameter index */
int i; /* Intermediate axis index */
int mark; /* Non-zero if card should be removed once used */
int nfld; /* Number of integer fields in test string */
int ok; /* Was value converted succesfully? */
int type; /* Keyword data type */
int undef; /* Is an undefined keyword value acceptable? */
void *item; /* Pointer to item to get/put */
/* Check the global error status. */
if ( !astOK ) return;
/* Ensure the FitsChan is re-wound. */
astClearCard( fc );
/* Loop round all the cards in the FitsChan obtaining the keyword name for
each card. Note, the single "=" is correct in the following "while"
statement. */
s = 0;
jm = -1;
i = -1;
type = AST__NOTYPE;
while( (keynam = CardName( fc, status )) ){
item = NULL;
/* Assume the card is to be consumed by the reading process. This means
the card will be marked as used and effectively excluded from the header.
Keywords which supply observation details that do not depend on the
mapping from pixel to WCS axes, or on the nature of the WCS axes,
are not removed as they may be needed for other, non-WCS related,
purposes. */
mark = 1;
/* For most keywords, if the keyword is present in the header it must
have a definded value. However, some keywords are read from the header
but not actually used for anything. This is done to ensure that the
keyword is stripped from the header. It is acceptable for such
keywords to have an undefined value. Initialise a flag indicating that
the next keyword read is not allowed to have an undefined value. */
undef = 0;
/* Is this a primary CRVAL keyword? */
if( Match( keynam, "CRVAL%d", 1, fld, &nfld, method, class, status ) ){
item = &(store->crval);
type = AST__FLOAT;
i = fld[ 0 ] - 1;
jm = 0;
s = ' ';
/* Is this a secondary CRVAL keyword? */
} else if( Match( keynam, "CRVAL%d%1c", 1, fld, &nfld, method, class, status ) ){
item = &(store->crval);
type = AST__FLOAT;
i = fld[ 0 ] - 1;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary CRPIX keyword? */
} else if( Match( keynam, "CRPIX%d", 1, fld, &nfld, method, class, status ) ){
item = &(store->crpix);
type = AST__FLOAT;
i = 0;
jm = fld[ 0 ] - 1;
s = ' ';
/* Is this a secondary CRPIX keyword? */
} else if( Match( keynam, "CRPIX%d%1c", 1, fld, &nfld, method, class, status ) ){
item = &(store->crpix);
type = AST__FLOAT;
i = 0;
jm = fld[ 0 ] - 1;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary CDELT keyword? */
} else if( Match( keynam, "CDELT%d", 1, fld, &nfld, method, class, status ) ){
item = &(store->cdelt);
type = AST__FLOAT;
i = fld[ 0 ] - 1;
jm = 0;
s = ' ';
/* Is this a secondary CDELT keyword? */
} else if( Match( keynam, "CDELT%d%1c", 1, fld, &nfld, method, class, status ) ){
item = &(store->cdelt);
type = AST__FLOAT;
i = fld[ 0 ] - 1;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary CTYPE keyword? If so, store the associated comment. */
} else if( Match( keynam, "CTYPE%d", 1, fld, &nfld, method, class, status ) ){
item = &(store->ctype);
type = AST__STRING;
i = fld[ 0 ] - 1;
jm = 0;
s = ' ';
SetItemC( &(store->ctype_com), i, 0, ' ', CardComm( fc, status ), status );
/* Is this a secondary CTYPE keyword? If so, store the associated comment. */
} else if( Match( keynam, "CTYPE%d%1c", 1, fld, &nfld, method, class, status ) ){
item = &(store->ctype);
type = AST__STRING;
i = fld[ 0 ] - 1;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
SetItemC( &(store->ctype_com), i, 0, s, CardComm( fc, status ), status );
/* Is this a primary CNAME keyword? */
} else if( Match( keynam, "CNAME%d", 1, fld, &nfld, method, class, status ) ){
item = &(store->cname);
type = AST__STRING;
i = fld[ 0 ] - 1;
jm = 0;
s = ' ';
/* Is this a secondary CNAME keyword? */
} else if( Match( keynam, "CNAME%d%1c", 1, fld, &nfld, method, class, status ) ){
item = &(store->cname);
type = AST__STRING;
i = fld[ 0 ] - 1;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary CUNIT keyword? */
} else if( Match( keynam, "CUNIT%d", 1, fld, &nfld, method, class, status ) ){
item = &(store->cunit);
type = AST__STRING;
i = fld[ 0 ] - 1;
jm = 0;
s = ' ';
/* Is this a secondary CUNIT keyword? */
} else if( Match( keynam, "CUNIT%d%1c", 1, fld, &nfld, method, class, status ) ){
item = &(store->cunit);
type = AST__STRING;
i = fld[ 0 ] - 1;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary PC keyword? */
} else if( Match( keynam, "PC%d_%d", 2, fld, &nfld, method, class, status ) ){
item = &(store->pc);
type = AST__FLOAT;
i = fld[ 0 ] - 1;
jm = fld[ 1 ] - 1;
s = ' ';
/* Is this a secondary PC keyword? */
} else if( Match( keynam, "PC%d_%d%1c", 2, fld, &nfld, method, class, status ) ){
item = &(store->pc);
type = AST__FLOAT;
i = fld[ 0 ] - 1;
jm = fld[ 1 ] - 1;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary PV keyword? */
} else if( Match( keynam, "PV%d_%d", 2, fld, &nfld, method, class, status ) ){
item = &(store->pv);
type = AST__FLOAT;
i = fld[ 0 ] - 1;
jm = fld[ 1 ];
s = ' ';
/* Is this a secondary PV keyword? */
} else if( Match( keynam, "PV%d_%d%1c", 2, fld, &nfld, method, class, status ) ){
item = &(store->pv);
type = AST__FLOAT;
i = fld[ 0 ] - 1;
jm = fld[ 1 ];
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary PS keyword? */
} else if( Match( keynam, "PS%d_%d", 2, fld, &nfld, method, class, status ) ){
item = &(store->ps);
type = AST__STRING;
i = fld[ 0 ] - 1;
jm = fld[ 1 ];
s = ' ';
/* Is this a secondary PS keyword? */
} else if( Match( keynam, "PS%d_%d%1c", 2, fld, &nfld, method, class, status ) ){
item = &(store->ps);
type = AST__STRING;
i = fld[ 0 ] - 1;
jm = fld[ 1 ];
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary RADESYS keyword? */
} else if( Match( keynam, "RADESYS", 0, fld, &nfld, method, class, status ) ){
item = &(store->radesys);
type = AST__STRING;
i = 0;
jm = 0;
s = ' ';
/* Is this a secondary RADESYS keyword? */
} else if( Match( keynam, "RADESYS%1c", 0, fld, &nfld, method, class, status ) ){
item = &(store->radesys);
type = AST__STRING;
i = 0;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary EQUINOX keyword? */
} else if( Match( keynam, "EQUINOX", 0, fld, &nfld, method, class, status ) ){
item = &(store->equinox);
type = AST__FLOAT;
i = 0;
jm = 0;
s = ' ';
/* Is this a secondary EQUINOX keyword? */
} else if( Match( keynam, "EQUINOX%1c", 0, fld, &nfld, method, class, status ) ){
item = &(store->equinox);
type = AST__FLOAT;
i = 0;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary LATPOLE keyword? */
} else if( Match( keynam, "LATPOLE", 0, fld, &nfld, method, class, status ) ){
item = &(store->latpole);
type = AST__FLOAT;
i = 0;
jm = 0;
s = ' ';
/* Is this a secondary LATPOLE keyword? */
} else if( Match( keynam, "LATPOLE%1c", 0, fld, &nfld, method, class, status ) ){
item = &(store->latpole);
type = AST__FLOAT;
i = 0;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary LONPOLE keyword? */
} else if( Match( keynam, "LONPOLE", 0, fld, &nfld, method, class, status ) ){
item = &(store->lonpole);
type = AST__FLOAT;
i = 0;
jm = 0;
s = ' ';
/* Is this a secondary LONPOLE keyword? */
} else if( Match( keynam, "LONPOLE%1c", 0, fld, &nfld, method, class, status ) ){
item = &(store->lonpole);
type = AST__FLOAT;
i = 0;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary WXSAXES keyword? */
} else if( Match( keynam, "WCSAXES", 0, fld, &nfld, method, class, status ) ){
item = &(store->wcsaxes);
type = AST__FLOAT;
i = 0;
jm = 0;
s = ' ';
/* Is this a secondary WCSAXES keyword? */
} else if( Match( keynam, "WCSAXES%1c", 0, fld, &nfld, method, class, status ) ){
item = &(store->wcsaxes);
type = AST__FLOAT;
i = 0;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary DUT1 keyword? */
} else if( Match( keynam, "DUT1", 0, fld, &nfld, method, class, status ) ){
mark = 0;
item = &(store->dut1);
type = AST__FLOAT;
i = 0;
jm = 0;
s = ' ';
/* Is this a primary MJD-OBS keyword? */
} else if( Match( keynam, "MJD-OBS", 0, fld, &nfld, method, class, status ) ){
mark = 0;
item = &(store->mjdobs);
type = AST__FLOAT;
i = 0;
jm = 0;
s = ' ';
/* Is this a primary WCSNAME keyword? */
} else if( Match( keynam, "WCSNAME", 0, fld, &nfld, method, class, status ) ){
item = &(store->wcsname);
type = AST__STRING;
i = 0;
jm = 0;
s = ' ';
/* Is this a secondary WCSNAME keyword? */
} else if( Match( keynam, "WCSNAME%1c", 0, fld, &nfld, method, class, status ) ){
item = &(store->wcsname);
type = AST__STRING;
i = 0;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary SPECSYS keyword? */
} else if( Match( keynam, "SPECSYS", 0, fld, &nfld, method, class, status ) ){
item = &(store->specsys);
type = AST__STRING;
i = 0;
jm = 0;
s = ' ';
/* Is this a secondary SPECSYS keyword? */
} else if( Match( keynam, "SPECSYS%1c", 0, fld, &nfld, method, class, status ) ){
item = &(store->specsys);
type = AST__STRING;
i = 0;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary SSYSSRC keyword? */
} else if( Match( keynam, "SSYSSRC", 0, fld, &nfld, method, class, status ) ){
item = &(store->ssyssrc);
type = AST__STRING;
i = 0;
jm = 0;
s = ' ';
/* Is this a secondary SSYSSRC keyword? */
} else if( Match( keynam, "SSYSSRC%1c", 0, fld, &nfld, method, class, status ) ){
item = &(store->ssyssrc);
type = AST__STRING;
i = 0;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary ZSOURCE keyword? */
} else if( Match( keynam, "ZSOURCE", 0, fld, &nfld, method, class, status ) ){
item = &(store->zsource);
type = AST__FLOAT;
i = 0;
jm = 0;
s = ' ';
/* Is this a secondary ZSOURCE keyword? */
} else if( Match( keynam, "ZSOURCE%1c", 0, fld, &nfld, method, class, status ) ){
item = &(store->zsource);
type = AST__FLOAT;
i = 0;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary VELOSYS keyword? */
} else if( Match( keynam, "VELOSYS", 0, fld, &nfld, method, class, status ) ){
item = &(store->velosys);
type = AST__FLOAT;
undef = 1;
i = 0;
jm = 0;
s = ' ';
/* Is this a secondary VELOSYS keyword? */
} else if( Match( keynam, "VELOSYS%1c", 0, fld, &nfld, method, class, status ) ){
item = &(store->velosys);
type = AST__FLOAT;
undef = 1;
i = 0;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary RESTFRQ keyword? */
} else if( Match( keynam, "RESTFRQ", 0, fld, &nfld, method, class, status ) ){
item = &(store->restfrq);
type = AST__FLOAT;
i = 0;
jm = 0;
s = ' ';
/* Is this a secondary RESTFRQ keyword? */
} else if( Match( keynam, "RESTFRQ%1c", 0, fld, &nfld, method, class, status ) ){
item = &(store->restfrq);
type = AST__FLOAT;
i = 0;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary RESTWAV keyword? */
} else if( Match( keynam, "RESTWAV", 0, fld, &nfld, method, class, status ) ){
item = &(store->restwav);
type = AST__FLOAT;
i = 0;
jm = 0;
s = ' ';
/* Is this a secondary RESTWAV keyword? */
} else if( Match( keynam, "RESTWAV%1c", 0, fld, &nfld, method, class, status ) ){
item = &(store->restwav);
type = AST__FLOAT;
i = 0;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary IMAGFREQ keyword? */
} else if( Match( keynam, "IMAGFREQ", 0, fld, &nfld, method, class, status ) ){
item = &(store->imagfreq);
type = AST__FLOAT;
i = 0;
jm = 0;
s = ' ';
/* Is this a primary SKYREF keyword? */
} else if( Match( keynam, "SREF%d", 1, fld, &nfld, method, class, status ) ){
item = &(store->skyref);
type = AST__FLOAT;
i = fld[ 0 ] - 1;
jm = 0;
s = ' ';
/* Is this a secondary SKYREF keyword? */
} else if( Match( keynam, "SREF%d%1c", 1, fld, &nfld, method, class, status ) ){
item = &(store->skyref);
type = AST__FLOAT;
i = fld[ 0 ] - 1;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary SKYREFP keyword? */
} else if( Match( keynam, "SREFP%d", 1, fld, &nfld, method, class, status ) ){
item = &(store->skyrefp);
type = AST__FLOAT;
i = fld[ 0 ] - 1;
jm = 0;
s = ' ';
/* Is this a secondary SKYREFP keyword? */
} else if( Match( keynam, "SREFP%d%1c", 1, fld, &nfld, method, class, status ) ){
item = &(store->skyrefp);
type = AST__FLOAT;
i = fld[ 0 ] - 1;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary SKYREFIS keyword? */
} else if( Match( keynam, "SREFIS", 0, fld, &nfld, method, class, status ) ){
item = &(store->skyrefis);
type = AST__STRING;
i = 0;
jm = 0;
s = ' ';
/* Is this a secondary SKYREFIS keyword? */
} else if( Match( keynam, "SREFIS%1c", 0, fld, &nfld, method, class, status ) ){
item = &(store->skyrefis);
type = AST__STRING;
i = 0;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary AXREF keyword? */
} else if( Match( keynam, "AXREF%d", 1, fld, &nfld, method, class, status ) ){
item = &(store->axref);
type = AST__FLOAT;
i = fld[ 0 ] - 1;
jm = 0;
s = ' ';
/* Is this a secondary AXREF keyword? */
} else if( Match( keynam, "AXREF%d%1c", 1, fld, &nfld, method, class, status ) ){
item = &(store->axref);
type = AST__FLOAT;
i = fld[ 0 ] - 1;
jm = 0;
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a MJD-AVG keyword? */
} else if( Match( keynam, "MJD-AVG", 0, fld, &nfld, method, class, status ) ){
mark = 0;
item = &(store->mjdavg);
type = AST__FLOAT;
i = 0;
jm = 0;
s = ' ';
/* Is this a OBSGEO-X keyword? */
} else if( Match( keynam, "OBSGEO-X", 0, fld, &nfld, method, class, status ) ){
mark = 0;
item = &(store->obsgeox);
type = AST__FLOAT;
i = 0;
jm = 0;
s = ' ';
/* Is this a OBSGEO-Y keyword? */
} else if( Match( keynam, "OBSGEO-Y", 0, fld, &nfld, method, class, status ) ){
mark = 0;
item = &(store->obsgeoy);
type = AST__FLOAT;
i = 0;
jm = 0;
s = ' ';
/* Is this a OBSGEO-Z keyword? */
} else if( Match( keynam, "OBSGEO-Z", 0, fld, &nfld, method, class, status ) ){
mark = 0;
item = &(store->obsgeoz);
type = AST__FLOAT;
i = 0;
jm = 0;
s = ' ';
/* Is this a TIMESYS keyword? */
} else if( Match( keynam, "TIMESYS", 0, fld, &nfld, method, class, status ) ){
item = &(store->timesys);
type = AST__STRING;
i = 0;
jm = 0;
s = ' ';
/* Following keywords are used to describe "-SIP" distortion as used by
the Spitzer project... */
/* Is this a primary A keyword? */
} else if( Match( keynam, "A_%d_%d", 2, fld, &nfld, method, class, status ) ){
item = &(store->asip);
type = AST__FLOAT;
i = fld[ 0 ];
jm = fld[ 1 ];
s = ' ';
/* Is this a secondary A keyword? */
} else if( Match( keynam, "A_%d_%d%1c", 2, fld, &nfld, method, class, status ) ){
item = &(store->asip);
type = AST__FLOAT;
i = fld[ 0 ];
jm = fld[ 1 ];
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary B keyword? */
} else if( Match( keynam, "B_%d_%d", 2, fld, &nfld, method, class, status ) ){
item = &(store->bsip);
type = AST__FLOAT;
i = fld[ 0 ];
jm = fld[ 1 ];
s = ' ';
/* Is this a secondary B keyword? */
} else if( Match( keynam, "B_%d_%d%1c", 2, fld, &nfld, method, class, status ) ){
item = &(store->bsip);
type = AST__FLOAT;
i = fld[ 0 ];
jm = fld[ 1 ];
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary AP keyword? */
} else if( Match( keynam, "AP_%d_%d", 2, fld, &nfld, method, class, status ) ){
item = &(store->apsip);
type = AST__FLOAT;
i = fld[ 0 ];
jm = fld[ 1 ];
s = ' ';
/* Is this a secondary AP keyword? */
} else if( Match( keynam, "AP_%d_%d%1c", 2, fld, &nfld, method, class, status ) ){
item = &(store->apsip);
type = AST__FLOAT;
i = fld[ 0 ];
jm = fld[ 1 ];
s = keynam[ strlen( keynam ) - 1 ];
/* Is this a primary BP keyword? */
} else if( Match( keynam, "BP_%d_%d", 2, fld, &nfld, method, class, status ) ){
item = &(store->bpsip);
type = AST__FLOAT;
i = fld[ 0 ];
jm = fld[ 1 ];
s = ' ';
/* Is this a secondary BP keyword? */
} else if( Match( keynam, "BP_%d_%d%1c", 2, fld, &nfld, method, class, status ) ){
item = &(store->bpsip);
type = AST__FLOAT;
i = fld[ 0 ];
jm = fld[ 1 ];
s = keynam[ strlen( keynam ) - 1 ];
}
/* If this keyword was recognized, store it in the FitsStore, and mark it
as having been read. */
if( item ){
ok = 1;
if( type == AST__FLOAT ){
if( CnvValue( fc, AST__FLOAT, undef, &dval, method, status ) ) {
SetItem( (double ****) item, i, jm, s, dval, status );
if( mark ) MarkCard( fc, status );
} else {
ok = 0;
}
} else {
if( CnvValue( fc, AST__STRING, undef, &cval, method, status ) ) {
cval[ astChrLen( cval ) ] = 0; /* Exclude trailing spaces */
SetItemC( (char *****) item, i, jm, s, cval, status );
if( mark ) MarkCard( fc, status );
} else {
ok = 0;
}
}
/* Issue a warning if the value could not be converted to the expected
type. */
if( !ok ) {
/* First check that the keyword is not included in "fc2". */
if( !HasCard( fc2, keynam, method, class, status ) ) {
sprintf( buf, "The original FITS header contained a value for "
"keyword %s which could not be converted to a %s.",
keynam, ( type==AST__FLOAT ? "floating point number":
"character string" ) );
Warn( fc, "badval", buf, "astRead", "FitsChan", status );
}
}
}
/* Move on to the next card. */
MoveCard( fc, 1, method, class, status );
}
}
static int WcsFromStore( AstFitsChan *this, FitsStore *store,
const char *method, const char *class, int *status ){
/*
* Name:
* WcsFromStore
* Purpose:
* Store WCS keywords in a FitsChan using FITS-WCS encoding.
* Type:
* Private function.
* Synopsis:
* int WcsFromStore( AstFitsChan *this, FitsStore *store,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* A FitsStore is a structure containing a generalised represention of
* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
* from a set of FITS header cards (using a specified encoding), or
* an AST FrameSet. In other words, a FitsStore is an encoding-
* independant intermediary staging post between a FITS header and
* an AST FrameSet.
*
* This function copies the WCS information stored in the supplied
* FitsStore into the supplied FitsChan, using FITS-WCS encoding.
* Parameters:
* this
* Pointer to the FitsChan.
* store
* Pointer to the FitsStore.
* 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:
* A value of 1 is returned if succesfull, and zero is returned
* otherwise.
*/
/* Local Variables: */
char *comm; /* Pointer to comment string */
char *cval; /* Pointer to string keyword value */
char parprefix[3]; /* Prefix for projection parameter keywords */
char combuf[80]; /* Buffer for FITS card comment */
char s; /* Co-ordinate version character */
char sign[2]; /* Fraction's sign character */
char sup; /* Upper limit on s */
char type[MXCTYPELEN];/* Buffer for CTYPE value */
double cdl; /* CDELT value */
double fd; /* Fraction of a day */
double mjd99; /* MJD at start of 1999 */
double val; /* General purpose value */
int *tabaxis; /* Flags WCS axes that use -TAB algorithm */
int i; /* Axis index */
int ihmsf[ 4 ]; /* Hour, minute, second, fractional second */
int iymdf[ 4 ]; /* Year, month, date, fractional day */
int j; /* Axis index */
int jj; /* SlaLib status */
int m; /* Parameter index */
int maxm; /* Upper limit on m */
int naxis; /* Value of NAXIS keyword */
int nc; /* Length of STYPE string */
int nwcs; /* No. of WCS axes */
int ok; /* Frame created succesfully? */
int prj; /* Projection type */
int ret; /* Returned value */
/* Initialise */
ret = 0;
/* Other initialisation to avoid compiler warnings. */
tabaxis = NULL;
/* Check the inherited status. */
if( !astOK ) return ret;
/* If the FitsChan contains a value for the NAXIS keyword, note it.
Otherwise store -1. */
if( !astGetFitsI( this, "NAXIS", &naxis ) ) naxis = -1;
/* Find the last WCS related card. */
FindWcs( this, 1, 1, 0, method, class, status );
/* Loop round all co-ordinate versions */
sup = GetMaxS( &(store->crval), status );
for( s = ' '; s <= sup && astOK; s++ ){
/* For alternate axes, skip this axis description if there is no CRPIX1 or
CRVAL1 value. This avoids partial axis descriptions being written out. */
if( s != ' ' ) {
if( GetItem( &(store->crpix), 0, 0, s, NULL, method, class, status ) ==
AST__BAD ||
GetItem( &(store->crval), 0, 0, s, NULL, method, class, status ) ==
AST__BAD ) {
ok = 0;
goto next;
}
}
/* Assume the Frame can be created succesfully. */
ok = 1;
/* Save the number of wcs axes. If a value for WCSAXES has been set, or
if the number of axes is not the same as specified in the NAXIS keyword,
store a WCSAXES keyword. */
val = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) {
nwcs = (int) ( val + 0.5 );
} else {
nwcs = GetMaxJM( &(store->crpix), s, status ) + 1;
if( nwcs != 0 && nwcs != naxis ) val = (double) nwcs;
}
if( val != AST__BAD ) {
SetValue( this, FormatKey( "WCSAXES", -1, -1, s, status ),
&nwcs, AST__INT, "Number of WCS axes", status );
}
/* Get and save WCSNAME. This is NOT required, so do not return if it is
not available. If the WCS is 1-d, only store WCSNAME if its value is
different to the CTYPE1 value. */
cval = GetItemC( &(store->wcsname), 0, 0, s, NULL, method, class, status );
if( cval && nwcs == 1 ) {
comm = GetItemC( &(store->ctype), 0, 0, s, NULL, method, class, status );
if( comm && Similar( comm, cval, status ) ) cval = NULL;
}
if( cval ) SetValue( this, FormatKey( "WCSNAME", -1, -1, s, status ), &cval,
AST__STRING, "Reference name for the coord. frame", status );
/* The prefix for numerical projection parameters is usually "PV". */
strcpy( parprefix, "PV" );
/* Keywords common to all axis types... */
/* Get and save CRPIX for all pixel axes. These are required, so pass on
if they are not available. */
for( i = 0; i < nwcs; i++ ) {
val = GetItem( &(store->crpix), 0, i, s, NULL, method, class, status );
if( val == AST__BAD ) {
ok = 0;
goto next;
}
sprintf( combuf, "Reference pixel on axis %d", i + 1 );
SetValue( this, FormatKey( "CRPIX", i + 1, -1, s, status ), &val, AST__FLOAT,
combuf, status );
}
/* Get and save CRVAL for all WCS axes. These are required, so
pass on if they are not available. */
for( i = 0; i < nwcs; i++ ) {
val = GetItem( &(store->crval), i, 0, s, NULL, method, class, status );
if( val == AST__BAD ) {
ok = 0;
goto next;
}
sprintf( combuf, "Value at ref. pixel on axis %d", i + 1 );
SetValue( this, FormatKey( "CRVAL", i + 1, -1, s, status ), &val, AST__FLOAT,
combuf, status );
}
/* Allocate memory to indicate if each WCS axis is described by a -TAB
algorithm or not. Initialiss it to zero. */
tabaxis = astCalloc( nwcs, sizeof( int ) );
/* Get and save CTYPE for all WCS axes. These are required, so
pass on if they are not available. */
for( i = 0; i < nwcs; i++ ) {
cval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
if( !cval ) {
ok = 0;
goto next;
}
comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status );
if( !comm ) {
sprintf( combuf, "Type of co-ordinate on axis %d", i + 1 );
comm = combuf;
}
/* Extract the projection type as specified by the last 4 characters
in the CTYPE keyword value. This will be AST__WCSBAD for non-celestial
axes. Note, CTYPE can be more than 8 characters long. */
nc = strlen( cval );
prj = ( nc > 4 ) ? astWcsPrjType( cval + nc - 4 ) : AST__WCSBAD;
/* If the projection type is "TPN" (an AST-specific code) convert it to
standard FITS-WCS code "TAN" and change the prefix for projection
parameters from "PV" to "QV". AST will do the inverse conversions when
reading such a header. Non-AST software will simply ignore the QV
terms and interpret the header as a simple TAN projection. */
if( prj == AST__TPN ) {
strcpy( parprefix, "QV" );
strcpy( type, cval );
(void) strcpy( type + nc - 4, "-TAN" );
cval = type;
}
/* Note if the axis is described by the -TAB algorithm. */
tabaxis[ i ] = ( prj == AST__WCSBAD && strlen( cval ) >= 8 &&
!strncmp( cval + 4, "-TAB", 4 ) );
/* Store the (potentially modified) CTYPE value. */
SetValue( this, FormatKey( "CTYPE", i + 1, -1, s, status ), &cval, AST__STRING,
comm, status );
}
/* Get and save CNAME for all WCS axes. These are NOT required, so
do not pass on if they are not available. Do not include a CNAME
keyword if its value equals the commen or value of the corresponding
CTYPE keyword. */
for( i = 0; i < nwcs; i++ ) {
cval = GetItemC( &(store->cname), i, 0, s, NULL, method, class, status );
if( cval ) {
comm = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
if( !comm || strcmp( comm, cval ) ) {
comm = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status );
if( !comm || strcmp( comm, cval ) ) {
sprintf( combuf, "Description of axis %d", i + 1 );
SetValue( this, FormatKey( "CNAME", i + 1, -1, s, status ), &cval,
AST__STRING, combuf, status );
}
}
}
}
/* Now choose whether to produce CDi_j or CDELT/PCi_j keywords. */
if( astGetCDMatrix( this ) ) {
/* CD matrix. Multiply the row of the PC matrix by the CDELT value. */
for( i = 0; i < nwcs; i++ ) {
cdl = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
if( cdl == AST__BAD ) cdl = 1.0;
for( j = 0; j < nwcs; j++ ){
val = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
if( val == AST__BAD ) val = ( i == j ) ? 1.0 : 0.0;
val *= cdl;
if( val != 0.0 ) {
SetValue( this, FormatKey( "CD", i + 1, j + 1, s, status ), &val,
AST__FLOAT, "Transformation matrix element", status );
}
}
}
/* If producing PC/CDELT keywords... */
} else {
/* CDELT keywords. */
for( i = 0; i < nwcs; i++ ) {
val = GetItem( &(store->cdelt), i, 0, s, NULL, method, class, status );
if( val == AST__BAD ) {
ok = 0;
goto next;
}
sprintf( combuf, "Pixel size on axis %d", i + 1 );
SetValue( this, FormatKey( "CDELT", i + 1, -1, s, status ), &val, AST__FLOAT,
combuf, status );
}
/* PC matrix. */
for( i = 0; i < nwcs; i++ ) {
for( j = 0; j < nwcs; j++ ){
val = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
if( val != AST__BAD ) {
if( i == j ) {
if( EQUAL( val, 1.0 ) ) val = AST__BAD;
} else {
if( EQUAL( val, 0.0 ) ) val = AST__BAD;
}
}
if( val != AST__BAD ) {
SetValue( this, FormatKey( "PC", i + 1, j + 1, s, status ), &val,
AST__FLOAT, "Transformation matrix element", status );
}
}
}
}
/* Get and save CUNIT for all WCS axes. These are NOT required, so
do not pass on if they are not available. */
for( i = 0; i < nwcs; i++ ) {
cval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status );
if( cval ) {
sprintf( combuf, "Units for axis %d", i + 1 );
SetValue( this, FormatKey( "CUNIT", i + 1, -1, s, status ), &cval, AST__STRING,
combuf, status );
}
}
/* Get and save AXREF for all WCS axes. These are NOT required, so do not
pass on if they are not available. Note, AXREF is a non-standard keyword
used by AST to communicate the reference position on any axes described
by the -TAB algorithm and which has no inverse transformation. For all
other cases, the reference position corresponds to the values of CRVAL. */
for( i = 0; i < nwcs; i++ ) {
val = GetItem( &(store->axref), i, 0, s, NULL, method, class, status );
if( val != AST__BAD ) {
sprintf( combuf, "Reference WCS value on axis %d", i + 1 );
SetValue( this, FormatKey( "AXREF", i + 1, -1, s, status ), &val, AST__FLOAT,
combuf, status );
}
}
/* Get and save SREFIS. This is NOT required, so do not return if it is
not available. Note, SREFIS is a non-standard keyword used by AST to
communicate the SkyRefIs attribute in the original SkyFrame. */
cval = GetItemC( &(store->skyrefis), 0, 0, s, NULL, method, class, status );
if( cval ) SetValue( this, FormatKey( "SREFIS", -1, -1, s, status ), &cval,
AST__STRING, "Is SkyRef used as pole or origin?", status );
/* Get and save SREF for all WCS axes. These are NOT required, so do not
pass on if they are not available. Note, SREF is a non-standard keyword
used by AST to communicate the SkyRef position on any axes described
by a offset SkyFrame. */
for( i = 0; i < nwcs; i++ ) {
val = GetItem( &(store->skyref), i, 0, s, NULL, method, class, status );
if( val != AST__BAD ) {
sprintf( combuf, "Sky reference position on axis %d", i + 1 );
SetValue( this, FormatKey( "SREF", i + 1, -1, s, status ), &val, AST__FLOAT,
combuf, status );
}
}
/* Get and save SREFP for all WCS axes. These are NOT required, so do not
pass on if they are not available. Note, SREFP is a non-standard keyword
used by AST to communicate the SkyRefP position on any axes described
by a offset SkyFrame. */
for( i = 0; i < nwcs; i++ ) {
val = GetItem( &(store->skyrefp), i, 0, s, NULL, method, class, status );
if( val != AST__BAD ) {
sprintf( combuf, "Sky primary meridian position on axis %d", i + 1 );
SetValue( this, FormatKey( "SREFP", i + 1, -1, s, status ), &val, AST__FLOAT,
combuf, status );
}
}
/* Date of observation (only allowed for primary axis descriptions). */
if( s == ' ' ) {
val = GetItem( &(store->mjdobs), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) {
SetValue( this, FormatKey( "MJD-OBS", -1, -1, s, status ),
&val, AST__FLOAT, "Modified Julian Date of observation", status );
/* The format used for the DATE-OBS keyword depends on the value of the
keyword. For DATE-OBS < 1999.0, use the old "dd/mm/yy" format.
Otherwise, use the new "ccyy-mm-ddThh:mm:ss[.ssss]" format. */
palCaldj( 99, 1, 1, &mjd99, &jj );
if( val < mjd99 ) {
palDjcal( 0, val, iymdf, &jj );
sprintf( combuf, "%2.2d/%2.2d/%2.2d", iymdf[ 2 ], iymdf[ 1 ],
iymdf[ 0 ] - ( ( iymdf[ 0 ] > 1999 ) ? 2000 : 1900 ) );
} else {
palDjcl( val, iymdf, iymdf+1, iymdf+2, &fd, &jj );
palDd2tf( 3, fd, sign, ihmsf );
sprintf( combuf, "%4.4d-%2.2d-%2.2dT%2.2d:%2.2d:%2.2d.%3.3d",
iymdf[0], iymdf[1], iymdf[2], ihmsf[0], ihmsf[1],
ihmsf[2], ihmsf[3] );
}
/* Now store the formatted string in the FitsChan. */
cval = combuf;
SetValue( this, "DATE-OBS", (void *) &cval, AST__STRING,
"Date of observation", status );
}
val = GetItem( &(store->mjdavg), 0, 0, ' ', NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, "MJD-AVG", &val, AST__FLOAT,
"Average Modified Julian Date of observation", status );
/* Store the timescale in TIMESYS. */
cval = GetItemC( &(store->timesys), 0, 0, s, NULL, method, class, status );
if( cval ) SetValue( this, "TIMESYS", &cval, AST__STRING,
"Timescale for MJD-OBS/MJD-AVG values", status );
}
/* Numerical projection parameters */
maxm = GetMaxJM( &(store->pv), s, status );
for( i = 0; i < nwcs; i++ ){
for( m = 0; m <= maxm; m++ ){
val = GetItem( &(store->pv), i, m, s, NULL, method, class, status );
if( val != AST__BAD ) {
/* If the axis uses the "TAB" algorithm, there may be a PVi_4a parameter
in the FitsStore. This is an AST extension to the published -TAB
algorithm, and is used to hold the interpolation method. To avoid
clashing with any standard use of PV1_4a, rename it to QVi_4a. The
default is zero (linear interpolation) so do not write the QV value
if it zero. */
if( m == 4 && tabaxis[ i ] ) {
if( val != 0.0 ) {
SetValue( this, FormatKey( "QV", i + 1, m, s, status ),
&val, AST__FLOAT, "Use nearest neighbour "
"interpolation", status );
}
/* Just store the parameters for other type of axes. */
} else {
SetValue( this, FormatKey( parprefix, i + 1, m, s, status ), &val,
AST__FLOAT, "Projection parameter", status );
}
}
}
}
/* String projection parameters */
maxm = GetMaxJMC( &(store->ps), s, status );
for( i = 0; i < nwcs; i++ ){
for( m = 0; m <= maxm; m++ ){
cval = GetItemC( &(store->ps), i, m, s, NULL, method, class, status );
if( cval ) {
SetValue( this, FormatKey( "PS", i + 1, m, s, status ), &cval,
AST__STRING, "Projection parameter", status );
}
}
}
/* Keywords specific to celestial axes... */
/* Get and save RADESYS. This is NOT required, so do not return if it is
not available. */
cval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status );
if( cval ) SetValue( this, FormatKey( "RADESYS", -1, -1, s, status ), &cval,
AST__STRING, "Reference frame for RA/DEC values", status );
/* Reference equinox */
val = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, FormatKey( "EQUINOX", -1, -1, s, status ),
&val, AST__FLOAT,
"[yr] Epoch of reference equinox", status );
/* Latitude of native north pole */
val = GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, FormatKey( "LATPOLE", -1, -1, s, status ),
&val, AST__FLOAT,
"[deg] Latitude of native north pole", status );
/* Longitude of native north pole */
val = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, FormatKey( "LONPOLE", -1, -1, s, status ),
&val, AST__FLOAT,
"[deg] Longitude of native north pole", status );
/* Keywords specific to spectral axes... */
/* SPECSYS - the standard of rest for the spectral axis */
cval = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status );
if( cval ) SetValue( this, FormatKey( "SPECSYS", -1, -1, s, status ), &cval,
AST__STRING, "Standard of rest for spectral axis", status );
/* SSYSSRC - the standard of rest in which ZSOURCE is stored. */
cval = GetItemC( &(store->ssyssrc), 0, 0, s, NULL, method, class, status );
if( cval ) SetValue( this, FormatKey( "SSYSSRC", -1, -1, s, status ), &cval,
AST__STRING, "Standard of rest for source redshift", status );
/* ZSOURCE - topocentric optical velocity of source */
val = GetItem( &(store->zsource), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, FormatKey( "ZSOURCE", -1, -1, s, status ),
&val, AST__FLOAT, "[] Redshift of source", status );
/* VELOSYS - topocentric apparent radial velocity of the standard of rest. */
val = GetItem( &(store->velosys), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, FormatKey( "VELOSYS", -1, -1, s, status ),
&val, AST__FLOAT, "[m/s] Topo. apparent velocity of rest frame", status );
/* RESTFRQ - rest frequency */
val = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, FormatKey( "RESTFRQ", -1, -1, s, status ),
&val, AST__FLOAT, "[Hz] Rest frequency", status );
/* RESTWAV - rest wavelength */
val = GetItem( &(store->restwav), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, FormatKey( "RESTWAV", -1, -1, s, status ),
&val, AST__FLOAT, "[m] Rest wavelength", status );
/* The image frequency corresponding to the rest frequency (only used for
double sideband data). This is not part of the FITS-WCS standard but
is added for the benefit of JACH. */
val = GetItem( &(store->imagfreq), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) {
SetValue( this, "IMAGFREQ", &val, AST__FLOAT, "[Hz] Image frequency", status );
}
/* OBSGEO-X/Y/Z - observer's geocentric coords. Note, these always refer
to the primary axes. */
if( s == ' ' ) {
val = GetItem( &(store->obsgeox), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, "OBSGEO-X", &val, AST__FLOAT, "[m] Observatory geocentric X", status );
val = GetItem( &(store->obsgeoy), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, "OBSGEO-Y", &val, AST__FLOAT, "[m] Observatory geocentric Y", status );
val = GetItem( &(store->obsgeoz), 0, 0, s, NULL, method, class, status );
if( val != AST__BAD ) SetValue( this, "OBSGEO-Z", &val, AST__FLOAT, "[m] Observatory geocentric Z", status );
}
/* See if a Frame was sucessfully written to the FitsChan. */
next:
ok = ok && astOK;
/* If so, indicate we have something to return. */
if( ok ) ret = 1;
/* If we are producing secondary axes, clear any error status so we can
continue to produce the next Frame. Retain the error if the primary axes
could not be produced. After the primary axes, do the A axes. */
if( s != ' ' ) {
astClearStatus;
} else {
s = 'A' - 1;
}
/* Remove the secondary "new" flags from the FitsChan. This flag is
associated with cards which have been added to the FitsChan during
this pass through the main loop in this function. If the Frame was
written out succesfully, just clear the flags. If anything went wrong
with this Frame, remove the flagged cards from the FitsChan. */
FixNew( this, NEW2, !ok, method, class, status );
/* Set the current card so that it points to the last WCS-related keyword
in the FitsChan (whether previously read or not). */
FindWcs( this, 1, 1, 0, method, class, status );
/* Free resources. */
tabaxis = astFree( tabaxis );
}
/* Return zero or ret depending on whether an error has occurred. */
return astOK ? ret : 0;
}
static AstMapping *WcsIntWorld( AstFitsChan *this, FitsStore *store, char s,
int naxes, const char *method, const char *class, int *status ){
/*
* Name:
* WcsIntWorld
* Purpose:
* Create a Mapping from pixel coords to intermediate world coords.
* Type:
* Private function.
* Synopsis:
* AstMapping *WcsIntWorld( AstFitsChan *this, FitsStore *store, char s,
* int naxes, const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* This function interprets the contents of the supplied FitsStore
* structure, and creates a Mapping which describes the transformation
* from pixel coordinates to intermediate world coordinates, using the
* FITS World Coordinate System conventions. This is a general linear
* transformation described by the CRPIXj, PCi_j and CDELTi keywords.
* Parameters:
* this
* The FitsChan. ASTWARN cards may be added to this FitsChan if any
* anomalies are found in the keyword values in the FitsStore.
* store
* A structure containing information about the requested axis
* descriptions derived from a FITS header.
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* naxes
* The number of intermediate world coordinate axes (WCSAXES).
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the Mapping.
*/
/* Local Variables: */
AstMapping *mapd1; /* Pointer to first distortion Mapping */
AstMapping *mapd2; /* Pointer to second distortion Mapping */
AstMapping *mapd3; /* Pointer to third distortion Mapping */
AstMapping *mapd4; /* Pointer to fourth distortion Mapping */
AstMapping *map0; /* Pointer to a Mapping */
AstMapping *map1; /* Pointer to a Mapping */
AstMapping *ret; /* Pointer to the returned Mapping */
/* Initialise the pointer to the returned Mapping. */
ret = NULL;
/* Check the global status. */
if ( !astOK ) return ret;
/* First of all, check the CTYPE keywords to see if they contain any known
distortion codes (following the syntax described in FITS-WCS paper IV).
If so, Mappings are returned which represents the distortions to be
applied at each point in the chain of Mappings produced by this function.
Any distortion codes are removed from the CTYPE values in the FitsStore. */
DistortMaps( this, store, s, naxes, &mapd1, &mapd2, &mapd3, &mapd4, method,
class, status );
/* If distortion is to be applied now, initialise the returned Mapping to
be the distortion. */
if( mapd1 ) ret = mapd1;
/* Try to create a WinMap which translates the pixel coordinates so
that they are refered to an origin at the reference pixel. This
subtracts the value of CRPIXi from axis i. */
map1 = (AstMapping *) WcsShift( store, s, naxes, method, class, status );
/* Combine this with any previous Mapping. */
if( ret ) {
map0 = (AstMapping *) astCmpMap( ret, map1, 1, "", status );
ret = astAnnul( ret );
map1 = astAnnul( map1 );
ret = map0;
} else {
ret = map1;
}
/* If distortion is to be applied now, combine the two Mappings. */
if( mapd2 ) {
map0 = (AstMapping *) astCmpMap( ret, mapd2, 1, "", status );
ret = astAnnul( ret );
mapd2 = astAnnul( mapd2 );
ret = map0;
}
/* Now try to create a MatrixMap to implement the PC matrix. Combine it
with the above Mapping. Add a Warning if this mapping cannot be inverted. */
map1 = (AstMapping *) WcsPCMatrix( store, s, naxes, method, class, status );
if( !astGetTranInverse( map1 ) ) {
Warn( this, "badmat", "The pixel rotation matrix in the original FITS "
"header (specified by CD or PC keywords) could not be inverted. "
"This may be because the matrix contains rows or columns which "
"are entirely zero.", method, class, status );
}
map0 = (AstMapping *) astCmpMap( ret, map1, 1, "", status );
ret = astAnnul( ret );
map1 = astAnnul( map1 );
ret = map0;
/* If distortion is to be applied now, combine the two Mappings. */
if( mapd3 ) {
map0 = (AstMapping *) astCmpMap( ret, mapd3, 1, "", status );
ret = astAnnul( ret );
mapd3 = astAnnul( mapd3 );
ret = map0;
}
/* Now try to create a diagonal MatrixMap to implement the CDELT scaling.
Combine it with the above Mapping. */
map1 = (AstMapping *) WcsCDeltMatrix( store, s, naxes, method, class, status );
map0 = (AstMapping *) astCmpMap( ret, map1, 1, "", status );
ret = astAnnul( ret );
map1 = astAnnul( map1 );
ret = map0;
/* If distortion is to be applied now, combine the two Mappings. */
if( mapd4 ) {
map0 = (AstMapping *) astCmpMap( ret, mapd4, 1, "", status );
ret = astAnnul( ret );
mapd4 = astAnnul( mapd4 );
ret = map0;
}
/* Return the result. */
return ret;
}
static AstMapping *WcsMapFrm( AstFitsChan *this, FitsStore *store, char s,
AstFrame **frm, const char *method,
const char *class, int *status ){
/*
* Name:
* WcsMapFrm
* Purpose:
* Create a Mapping and Frame for the WCS transformations described in a
* FITS header.
* Type:
* Private function.
* Synopsis:
* AstMapping *WcsMapFrm( AstFitsChan *this, FitsStore *store, char s,
* AstFrame **frm, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* This function interprets the contents of the supplied FitsStore
* structure, and creates a Mapping which describes the transformation
* from pixel coordinates to world coordinates, using the FITS World
* Coordinate System conventions. It also creates a Frame describing
* the world coordinate axes.
* Parameters:
* this
* The FitsChan.
* store
* A structure containing information about the requested axis
* descriptions derived from a FITS header.
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* frm
* The address of a location at which to store a pointer to the
* Frame describing the world coordinate axes. If the Iwc attribute
* is non-zero, then this is actually a FrameSet in which the current
* Frame is the required WCS system. The FrameSet also contains one
* other Frame which defines the FITS IWC system.
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the Mapping.
*/
/* Local Variables: */
AstFrame *iwcfrm; /* Frame defining IWC system */
AstFrameSet *fs; /* Pointer to returned FrameSet */
AstMapping *map10; /* Pointer to a Mapping */
AstMapping *map1; /* Pointer to a Mapping */
AstMapping *map2; /* Pointer to a Mapping */
AstMapping *map3; /* Pointer to a Mapping */
AstMapping *map4; /* Pointer to a Mapping */
AstMapping *map5; /* Pointer to a Mapping */
AstMapping *map6; /* Pointer to a Mapping */
AstMapping *map7; /* Pointer to a Mapping */
AstMapping *map8; /* Pointer to a Mapping */
AstMapping *map9; /* Pointer to a Mapping */
AstMapping *ret; /* Pointer to the returned Mapping */
AstMapping *tabmap; /* Mapping from psi to WCS (paper III - 6.1.2) */
AstSkyFrame *reffrm; /* SkyFrame defining reflon and reflat */
char id[2]; /* ID string for returned Frame */
char iwc[5]; /* Domain name for IWC Frame */
const char *cc; /* Pointer to Domain */
double dut1; /* UT1-UTC correction in days */
double dval; /* Temporary double value */
double reflat; /* Reference celestial latitude */
double reflon; /* Reference celestial longitude */
int *tabaxis; /* Flags indicating -TAB axes */
int wcsaxes; /* Number of physical axes */
/* Initialise the pointer to the returned Mapping. */
ret = NULL;
/* Check the global status. */
if ( !astOK ) return ret;
/* Identify any axes that use the -TAB algoritm code described in FITS-WCS
paper III, and convert their CTYPE values to describe linear axes
(i.e. just remove "-TAB" from the CTYPE value). This also returns a
Mapping (which includes one or more LutMaps) that should be applied to
the resulting linear axis values in order to generate the final WCS
axis values. A NULL pointer is returned if no axes use -TAB. */
tabmap = TabMapping( this, store, s, &tabaxis, method, class, status );
/* Obtain the number of physical axes in the header. If the WCSAXES header
was specified, use it. Otherwise assume it is the same as the number
of pixel axes. */
dval = GetItem( &(store->wcsaxes), 0, 0, s, NULL, method, class, status );
if( dval != AST__BAD ) {
wcsaxes = (int) dval + 0.5;
} else {
wcsaxes = store->naxis;
}
/* Create a simple Frame to represent IWC coords. */
iwcfrm = astFrame( wcsaxes, "Title=FITS Intermediate World Coordinates", status );
strcpy( iwc, "IWC" );
iwc[ 3 ]= s;
iwc[ 4 ]= 0;
astSetDomain( iwcfrm, iwc );
/* Create a simple Frame which will be used as the initial representation
for the physical axes. This Frame will be changed later (or possibly
replaced by a Frame of another class) when we know what type of
physical axes we are dealing with. Set its Domain to "AST_FITSCHAN"
This value is used to identify axes which have not been changed,
and will be replaced before returning the final FrameSet. */
*frm = astFrame( wcsaxes, "Domain=AST_FITSCHAN", status );
/* Store the coordinate version character as the Ident attribute for the
returned Frame. */
id[ 0 ] = s;
id[ 1 ] = 0;
astSetIdent( *frm, id );
/* Create a Mapping which goes from pixel coordinates to what FITS-WCS
paper I calls "intermediate world coordinates". This stage is the same
for all axes. It uses the CRPIXj, PCi_j and CDELTi headers (and
distortion codes from the CTYPE keywords). */
map1 = WcsIntWorld( this, store, s, wcsaxes, method, class, status );
/* The conversion from intermediate world coordinates to the final world
coordinates depends on the type of axis being converted (as specified
by its CTYPE keyword). Check for each type of axis for which known
conventions exist... */
/* Celestial coordinate axes. The following call returns a Mapping which
transforms any celestial coordinate axes from intermediate world
coordinates to the final celestial coordinates. Other axes are left
unchanged by the Mapping. It also modifies the Frame so that a
SkyFrame is used to describe the celestial axes. */
map2 = WcsCelestial( this, store, s, frm, iwcfrm, &reflon, &reflat,
&reffrm, &tabmap, tabaxis, method, class, status );
/* Spectral coordinate axes. The following call returns a Mapping which
transforms any spectral coordinate axes from intermediate world
coordinates to the final spectral coordinates. Other axes are left
unchanged by the Mapping. It also modifies the Frame so that a
SpecFrame is used to describe the spectral axes. */
map3 = WcsSpectral( this, store, s, frm, iwcfrm, reflon, reflat, reffrm,
method, class, status );
/* Any axes which were not recognized by the above calls are assumed to
be linear. Create a Mapping which adds on the reference value for such
axes, and modify the Frame to desribe the axes. */
map4 = WcsOthers( this, store, s, frm, iwcfrm, method, class, status );
/* If the Frame still has the Domain "AST_FITSCHAN", clear it. */
cc = astGetDomain( *frm );
if( cc && !strcmp( cc, "AST_FITSCHAN" ) ) astClearDomain( *frm );
/* Concatenate the Mappings and simplify the result. */
map5 = (AstMapping *) astCmpMap( map1, map2, 1, "", status );
map6 = (AstMapping *) astCmpMap( map5, map3, 1, "", status );
map7 = (AstMapping *) astCmpMap( map6, map4, 1, "", status );
if( tabmap ) {
map8 = (AstMapping *) astCmpMap( map7, tabmap, 1, "", status );
} else {
map8 = astClone( map7 );
}
ret = astSimplify( map8 );
/* Ensure that the coordinate version character is stored as the Ident
attribute for the returned Frame (the above calls may have changed it). */
astSetIdent( *frm, id );
/* Set the DUT1 value. Note, the JACH store DUT1 in units of days in their
FITS headers, so convert from days to seconds. May need to do somthing
about this if the forthcoming FITS-WCS paper 5 (time axes) defines DUT1
to be in seconds. */
dut1 = GetItem( &(store->dut1), 0, 0, s, NULL, method, class, status );
if( dut1 != AST__BAD ) astSetDut1( *frm, dut1*SPD );
/* The returned Frame is actually a FrameSet in which the current Frame
is the required WCS Frame. The FrameSet contains one other Frame,
which is the Frame representing IWC. Create a FrameSet containing these
two Frames. */
if( astGetIwc( this ) ) {
fs = astFrameSet( iwcfrm, "", status );
astInvert( map1 );
map9 = (AstMapping *) astCmpMap( map1, ret, 1, "", status );
astInvert( map1 );
map10 = astSimplify( map9 );
astAddFrame( fs, AST__BASE, map10, *frm );
/* Return this FrameSet instead of the Frame. */
*frm = astAnnul( *frm );
*frm = (AstFrame *) fs;
/* Free resources */
map9 = astAnnul( map9 );
map10 = astAnnul( map10 );
}
/* Annull temporary resources. */
if( reffrm ) reffrm = astAnnul( reffrm );
if( tabmap ) tabmap = astAnnul( tabmap );
tabaxis = astFree( tabaxis );
iwcfrm = astAnnul( iwcfrm );
map1 = astAnnul( map1 );
map2 = astAnnul( map2 );
map3 = astAnnul( map3 );
map4 = astAnnul( map4 );
map5 = astAnnul( map5 );
map6 = astAnnul( map6 );
map7 = astAnnul( map7 );
map8 = astAnnul( map8 );
/* Annul thre returned objects if an error has occurred. */
if( !astOK ) {
ret = astAnnul( ret );
*frm = astAnnul( *frm );
}
/* Return the result. */
return ret;
}
static AstMatrixMap *WcsPCMatrix( FitsStore *store, char s, int naxes,
const char *method, const char *class, int *status ){
/*
* Name:
* WcsPCMatrix
* Purpose:
* Create a MatrixMap representing the PC matrix.
* Type:
* Private function.
* Synopsis:
* AstMatrixMap *WcsPCMatrix( FitsStore *store, char s, int naxes,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* A MatrixMap representing the FITS "PC" matrix is returned.
* Parameters:
* store
* A structure containing values for FITS keywords relating to
* the World Coordinate System.
* s
* A character s identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* naxes
* The number of intermediate world coordinate axes (WCSAXES).
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the created MatrixMap or a NULL pointer if an
* error occurred.
*/
/* Local Variables: */
AstMatrixMap *new; /* The created MatrixMap */
double *el; /* Pointer to next matrix element */
double *mat; /* Pointer to matrix array */
int i; /* Pixel axis index */
int j; /* Intermediate axis index. */
/* Initialise/ */
new = NULL;
/* Check the global status. */
if ( !astOK ) return new;
/* Allocate memory for the matrix. */
mat = (double *) astMalloc( sizeof(double)*naxes*naxes );
if( astOK ){
/* Fill the matrix with values from the FitsStore. */
el = mat;
for( i = 0; i < naxes; i++ ){
for( j = 0; j < naxes; j++ ){
/* Get the PCj_i value for this axis. Missing terms can be defaulted so
do not report an error if the required value is not present in the
FitsStore. */
*el = GetItem( &(store->pc), i, j, s, NULL, method, class, status );
/* Diagonal terms default to to 1.0, off-diagonal to zero. */
if( *el == AST__BAD ) *el = ( i == j ) ? 1.0: 0.0;
/* Move on to the next matrix element. */
el++;
}
}
/* Create the matrix. */
new = astMatrixMap( naxes, naxes, 0, mat, "", status );
/* Report an error if the inverse transformation is undefined. */
if( !astGetTranInverse( new ) && astOK ) {
astError( AST__BDFTS, "%s(%s): Unusable rotation matrix (PC or CD) found "
"in the FITS-WCS header - the matrix cannot be inverted.", status, method, class );
}
/* Release the memory used to hold the matrix. */
mat = (double *) astFree( (void *) mat );
}
/* If an error has occurred, attempt to annul the returned MatrixMap. */
if( !astOK ) new = astAnnul( new );
/* Return the MatrixMap. */
return new;
}
static AstMapping *WcsNative( AstFitsChan *this, FitsStore *store, char s,
AstWcsMap *wcsmap, int fits_ilon, int fits_ilat,
const char *method, const char *class, int *status ){
/*
* Name:
* WcsNative
* Purpose:
* Create a CmpMap which transforms Native Spherical Coords to
* Celestial Coords.
* Type:
* Private function.
* Synopsis:
* AstMapping *WcsNative( AstFitsChan *this, FitsStore *store, char s,
* AstWcsMap *wcsmap, int fits_ilon, int fits_ilat,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* A CmpMap is created which rotates the supplied Native Spherical Coords
* into Celestial Coords in the standard system specified by the CTYPE
* keywords. Any non-celestial axes are left unchanged.
*
* At the highest level, the returned CmpMap is made up of the following
* Mappings in series (if celestial long/lat axes are present):
* 1 - A PermMap which rearranges the axes so that the longitude axis is
* axis 0, the latitude axis is axis 1, and all other axes are
* stored at higher indices, starting at axis 2.
* 2 - A CmpMap which converts the values on axes 0 and 1 from Native
* Spherical to Celestial coordinates, leaving all other axes
* unchanged.
* 3 - A PermMap which rearranges the axes to put the longitude and
* latitude axes back in their original places. This is just the
* inverse of the PermMap used at stage 1 above.
*
* The CmpMap used at stage 2 above, is made up of two Mappings in
* parallel:
* 4 - A CmpMap which maps axes 0 and 1 from Native Spherical to
* Celestial coordinates.
* 5 - A UnitMap which passes on the values to axes 2, 3, etc,
* without change.
*
* The CmpMap used at stage 4 above, is made up of the following Mappings
* in series:
* 6 - A SphMap which converts the supplied spherical coordinates into
* Cartesian Coordinates.
* 7 - A MatrixMap which rotates the Cartesian coordinates from the
* Native to the Celestial system.
* 8 - A SphMap which converts the resulting Cartesian coordinates back
* to spherical coordinates.
* Parameters:
* this
* The FitsChan in which to store any warning cards. If NULL, no
* warnings are stored.
* store
* A structure containing values for FITS keywords relating to
* the World Coordinate System.
* s
* Co-ordinate version character to use (space means primary axes).
* wcsmap
* A mapping describing the deprojection which is being used. This is
* needed in order to be able to locate the fiducial point within the
* Native Speherical Coordinate system, since it varies from projection
* to projection.
* fits_ilon
* The zero-based FITS WCS axis index corresponding to celestial
* longitude (i.e. one less than the value of "i" in the keyword
* names "CTYPEi", "CRVALi", etc). If -1 is supplied, the index of
* the longitude axis in the supplied WcsMap is used.
* fits_ilat
* The zero-based FITS WCS axis index corresponding to celestial
* latitude (i.e. one less than the value of "i" in the keyword
* names "CTYPEi", "CRVALi", etc). If -1 is supplied, the index of
* the latitude axis in the supplied WcsMap is used.
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the created CmpMap or a NULL pointer if an error occurred.
* Notes:
* - The local variable names correspond to the notation in the papers
* by Greisen & Calabretta describing the FITS WCS system.
*/
/* Local Variables: */
AstCmpMap *cmpmap; /* A CmpMap */
AstMapping *new; /* The returned CmpMap */
AstMatrixMap *matmap2; /* Another MatrixMap */
AstMatrixMap *matmap; /* A MatrixMap */
AstPermMap *permmap; /* A PermMap */
AstSphMap *sphmap; /* A SphMap */
AstUnitMap *unitmap; /* A UnitMap */
char buf[150]; /* Message buffer */
double alpha0; /* Long. of fiduaicl point in standard system */
double alphap; /* Long. of native nth pole in standard system */
double axis[3]; /* Vector giving the axis of rotation */
double delta0; /* Lat. of fiducial point in standard system */
double deltap; /* Lat. of native nth pole in standard system */
double latpole; /* Lat. of native nth pole in standard system if deltap undefined */
double phip; /* Long. of standard nth pole in native system */
double phi0; /* Native longitude at fiducial point */
double theta0; /* Native latitude at fiducial point */
int *inperm; /* Pointer to array of output axis indices */
int *outperm; /* Pointer to array of input axis indices */
int axlat; /* Index of latitude physical axis */
int axlon; /* Index of longitude physical axis */
int i; /* Loop count */
int nax_rem; /* No. of non-astrometric axes */
int naxis; /* No. of axes. */
int new_axlat; /* Index of lat. physical axis after perming */
int tpn; /* Is this a TPN projection? */
/* Check the global status. */
if ( !astOK ) return NULL;
/* Initialise the returned CmpMap pointer. */
new = NULL;
/* Store the number of axes in a local variable. */
naxis = astGetNin( wcsmap );
/* Get the indices of the celestial axes. */
axlon = astGetWcsAxis( wcsmap, 0 );
axlat = astGetWcsAxis( wcsmap, 1 );
/* If the corresponding FITS axis indices were not supplied, use the
WcsMap axes found above. */
if( fits_ilon == -1 ) fits_ilon = axlon;
if( fits_ilat == -1 ) fits_ilat = axlat;
/* If there is no longitude or latitude axis, or if we have a
non-celestial projection, just return a UnitMap. */
if( axlon == axlat || astGetWcsType( wcsmap ) == AST__WCSBAD ){
new = (AstMapping *) astUnitMap( naxis, "", status );
/* If there is a lon/lat axis pair, create the inperm and outperm arrays
which will be needed later to create the PermMap which reorganises
the axes so that axis zero is the longitude axis and axis 1 is the
latitude axis. */
} else {
/* Get storage for the two arrays. */
inperm = (int *) astMalloc( sizeof( int )*(size_t)naxis );
outperm = (int *) astMalloc( sizeof( int )*(size_t)naxis );
if( astOK ){
/* Initialise an array holding the indices of the input axes which are copied
to each output axis. Initially assume that there is no re-arranging of
the axes. */
for( i = 0; i < naxis; i++ ) outperm[ i ] = i;
/* Swap the longitude axis and axis 0. */
i = outperm[ axlon ];
outperm[ axlon ] = outperm[ 0 ];
outperm[ 0 ] = i;
/* If axis 0 was originally the latitude axis, the latitude axis will now
be where the longitude axis was originally (because of the above axis
swap). */
if( axlat == 0 ) {
new_axlat = axlon;
} else {
new_axlat = axlat;
}
/* Swap the latitude axis and axis 1. */
i = outperm[ new_axlat ];
outperm[ new_axlat ] = outperm[ 1 ];
outperm[ 1 ] = i;
/* Create the array holding the output axis index corresponding to
each input axis. */
for( i = 0; i < naxis; i++ ) inperm[ outperm[ i ] ] = i;
}
/* Store the latitude and longitude (in the standard system) of the fiducial
point, in radians. */
delta0 = GetItem( &(store->crval), fits_ilat, 0, s, NULL, method, class, status );
if( delta0 == AST__BAD ) delta0 = 0.0;
delta0 *= AST__DD2R;
alpha0 = GetItem( &(store->crval), fits_ilon, 0, s, NULL, method, class, status );
if( alpha0 == AST__BAD ) alpha0 = 0.0;
alpha0 *= AST__DD2R;
/* Limit the latitude to the range +/- PI/2, issuing a warning if the
supplied CRVAL value is outside this range. The "alphap" variable is used
as workspace here. */
alphap = palDrange( delta0 );
delta0 = alphap;
if ( delta0 > AST__DPIBY2 ){
delta0 = AST__DPIBY2;
} else if ( delta0 < -AST__DPIBY2 ){
delta0 = -AST__DPIBY2;
}
if( alphap != delta0 ) {
sprintf( buf, "The original FITS header specified a fiducial "
"point with latitude %.*g. A value of %.*g is being used "
"instead. ", DBL_DIG, alphap*AST__DR2D, DBL_DIG,
delta0*AST__DR2D );
Warn( this, "badlat", buf, method, class, status );
}
/* Set a flag indicating if we have a TPN projection. The handling or
projection parameters is different for TPN projections. */
tpn = ( astGetWcsType( wcsmap ) == AST__TPN );
/* Store the radian values of the FITS keywords LONPOLE and LATPOLE. Defaults
will be used if either of these items was not supplied. These keyword
values may be stored in projection parameters PVi_3a and PVi_4a for
longitude axis "i" - in which case the "PV" values take precedence over
the "LONPOLE" and "LATPOLE" values. Do not do this for TPN projections
since they use these projection parameters to specify correcton terms. */
if( astTestPV( wcsmap, axlon, 3 ) && !tpn ) {
phip = astGetPV( wcsmap, axlon, 3 );
} else {
phip = GetItem( &(store->lonpole), 0, 0, s, NULL, method, class, status );
if( phip != AST__BAD && !tpn ) astSetPV( wcsmap, axlon, 3, phip );
}
if( phip != AST__BAD ) phip *= AST__DD2R;
if( astTestPV( wcsmap, axlon, 4 ) && !tpn ) {
latpole = astGetPV( wcsmap, axlon, 4 );
} else {
latpole = GetItem( &(store->latpole), 0, 0, s, NULL, method, class, status );
if( latpole != AST__BAD && !tpn ) astSetPV( wcsmap, axlon, 4, latpole );
}
if( latpole != AST__BAD ) latpole *= AST__DD2R;
/* Find the standard Celestial Coordinates of the north pole of the Native
Spherical Coordinate system. Report an error if the position was not
defined. */
if( !WcsNatPole( this, wcsmap, alpha0, delta0, latpole, &phip, &alphap,
&deltap, status ) && astOK ){
astError( AST__BDFTS, "%s(%s): Conversion from FITS WCS native "
"coordinates to celestial coordinates is ill-conditioned.", status,
method, class );
}
/* Create the SphMap which converts spherical coordinates to Cartesian
coordinates (stage 6 in the prologue). This asumes that axis 0 is the
longitude axis, and axis 1 is the latitude axis. This will be ensured
by a PermMap created later. Indicate that the SphMap will only be used
to transform points on a unit sphere. This enables a forward SphMap
to be combined with an inverse SphMap into a UnitMap, and thus aids
simplification. */
sphmap = astSphMap( "UnitRadius=1", status );
astInvert( sphmap );
/* Set the PolarLong attribute of the SphMap so that a longitude of phi0 (the
native longitude of the fiducial point) is returned by the inverse
transformation (cartesian->spherical) at either pole. */
GetFiducialNSC( wcsmap, &phi0, &theta0, status );
astSetPolarLong( sphmap, phi0 );
/* Create a unit MatrixMap to be the basis of the MatrixMap which rotates
Native Spherical Coords to Celestial Coords (stage 7 in the prologue). */
matmap = astMatrixMap( 3, 3, 2, NULL, "", status );
/* Modify the above MatrixMap so that it rotates the Cartesian position vectors
by -phip (i.e. LONPOLE) about the Z axis. This puts the north pole of the
standard system at zero longitude in the rotated system. Then annul the
original MatrixMap and use the new one instead. */
axis[ 0 ] = 0;
axis[ 1 ] = 0;
axis[ 2 ] = 1;
matmap2 = astMtrRot( matmap, -phip, axis );
matmap = astAnnul( matmap );
matmap = matmap2;
/* Now modify the above MatrixMap so that it rotates the Cartesian position
vectors by -(PI/2-deltap) about the Y axis. This puts the north pole of
the standard system as 90 degrees latitude in the rotated system. Then annul
the original MatrixMap and use the new one instead. */
axis[ 0 ] = 0;
axis[ 1 ] = 1;
axis[ 2 ] = 0;
matmap2 = astMtrRot( matmap, deltap - AST__DPIBY2, axis );
matmap = astAnnul( matmap );
matmap = matmap2;
/* Finally modify the above MatrixMap so that it rotates the Cartesian position
vectors (PI+alphap) about the Z axis. This puts the primary meridian of the
standard system at zero longitude in the rotated system. This results in the
rotated system being coincident with the standard system. */
axis[ 0 ] = 0;
axis[ 1 ] = 0;
axis[ 2 ] = 1;
matmap2 = astMtrRot( matmap, AST__DPI + alphap, axis );
matmap = astAnnul( matmap );
matmap = matmap2;
/* Combine the SphMap (stage 6) and MatrixMap (stage 7) in series. */
cmpmap = astCmpMap( sphmap, matmap, 1, "", status );
sphmap = astAnnul( sphmap );
matmap = astAnnul( matmap );
/* Create a new SphMap which converts Cartesian coordinates to spherical
coordinates (stage 8 in the prologue). Indicate that the SphMap will
only be used to transform points on a unit sphere. */
sphmap = astSphMap( "UnitRadius=1", status );
/* Set the PolarLong attribute of the SphMap so that a longitude of alpha0
(the celestial longitude of the fiducial point) is returned by the
forward transformation (cartesian->spherical) at either pole. */
astSetPolarLong( sphmap, alpha0 );
/* Add it to the compound mapping. The CmpMap then corresponds to stage 4
in the prologue. Annul the constituent mappings. */
new = (AstMapping *) astCmpMap( cmpmap, sphmap, 1, "", status );
cmpmap = astAnnul( cmpmap );
sphmap = astAnnul( sphmap );
/* If there are any remaining axes (i.e. axes which do not describe a
Celestial coordinate system), create a UnitMap which passes on their
values unchanged (stage 5 in the prologue), and add it the CmpMap,
putting it in parallel with the earlier mappings. The resulting CmpMap
then corresponds to stage 2 in the prologue. Note, the axis numbering
used by this UnitMap needs to take account of the fact that it is only
applied to the non-celestial axes. The axes are re-ordered by the
PermMap described at stage 1 in the prologue. */
nax_rem = naxis - 2;
if( nax_rem > 0 ){
unitmap = astUnitMap( nax_rem, "", status );
cmpmap = astCmpMap( new, unitmap, 0, "", status );
new = astAnnul( new );
unitmap = astAnnul( unitmap );
new = (AstMapping *) cmpmap;
}
/* Now we need to ensure that axes 0 and 1 correspond to longitude and
latitude. If this is already the case, then the CmpMap can be returned
as it is. Otherwise, a PermMap needs to be created to rearrange the
axes. */
if( axlon != 0 || axlat != 1 ){
/* Create the PermMap using the inperm and outperm arrays created earlier.
This is the mapping described as stage 1 in the prologue. */
permmap = astPermMap( naxis, inperm, naxis, outperm, NULL, "", status );
/* Compound this PermMap and the CmpMap corresponding to stage 2 (created
earlier) in series. */
cmpmap = astCmpMap( permmap, new, 1, "", status );
new = astAnnul( new );
new = (AstMapping *) cmpmap;
/* Now invert the PermMap, so that it re-arranges the axes back into
their original order. This is the mapping described as stage 3 in
the prologue. */
astInvert( permmap );
/* And finally.... add this inverted PermMap onto the end of the CmpMap. */
cmpmap = astCmpMap( new, permmap, 1, "", status );
permmap = astAnnul( permmap );
new = astAnnul( new );
new = (AstMapping *) cmpmap;
}
/* Free the temporary arrays. */
inperm = (int *) astFree( (void *) inperm );
outperm = (int *) astFree( (void *) outperm );
}
/* If an error has occurred, attempt to annul the new CmpMap. */
if( !astOK ) new = astAnnul( new );
/* Return the CmpMap. */
return new;
}
static int WcsNatPole( AstFitsChan *this, AstWcsMap *wcsmap, double alpha0,
double delta0, double latpole, double *phip,
double *alphap, double *deltap, int *status ){
/*
* Name:
* WcsNatPole
* Purpose:
* Find the celestial coordinates of the north pole of the Native Spherical
* Coordinate system.
* Type:
* Private function.
* Synopsis:
* int WcsNatPole( AstFitsChan *this, AstWcsMap *wcsmap, double alpha0,
* double delta0, double latpole, double *phip,
* double *alphap, double *deltap, int *status )
* Class Membership:
* FitsChan
* Description:
* The supplied WcsMap converts projected positions given in
* "Projection Plane Coords" to positions in the "Native Spherical
* Coordinate" system. This function finds the pole of this spherical
* coordinate system in terms of the standard celestial coordinate
* system to which the CRVALi, LONPOLE and LATPOLE keywords refer (this
* system should be identified by characters 5-8 of the CTYPEi
* keywords). It also supplies a default value for LONPOLE if no value
* has been supplied explicitly in the FITS header.
*
* This function implements equations 8, 9 and 10 from the FITS-WCS paper
* II by Calabretta & Greisen (plus the associated treatment of special
* cases). The paper provides more detailed documentation for the
* mathematics implemented by this function.
* Parameters:
* this
* The FitsChan in which to store any warning cards. If NULL, no
* warnings are stored.
* wcsmap
* A mapping describing the deprojection being used (i.e. the
* mapping from Projection Plane Coords to Native Spherical Coords).
* alpha0
* The longitude of the fiducial point in the standard celestial
* coordinate frame (in radians). Note, this fiducial point does
* not necessarily correspond to the point given by keywords CRPIXj.
* delta0
* The celestial latitude (radians) of the fiducial point.
* latpole
* The value of FITS keyword LATPOLE, converted to radians, or the
* symbolic constant AST__BAD if the keyword was not supplied.
* phip
* Pointer to a location at which is stored the longitude of the north
* pole of the standard Celestial coordinate system, as measured in
* the Native Spherical Coordinate system, in radians. This should be
* supplied holding the radian equivalent of the value of the FITS
* keyword LONPOLE, or the symbolic constant AST__BAD if the keyword was
* not supplied (in which case a default value will be returned at the
* given location).
* alphap
* Pointer to a location at which to store the calculated longitude
* of the Native North Pole, in radians.
* deltap
* Pointer to a location at which to store the calculated latitude
* of the Native North Pole, in radians.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A status: non-zero for success, zero if the position of the native
* north pole is undefined.
* Notes:
* - Certain combinations of keyword values result in the latitude of
* the Native North Pole being undefined. In these cases, a value of
* 0 is returned for the function value, but no error is reported.
* - All angular values used by this function are in radians.
* - A value of 0 is returned if an error has already occurred.
*/
/* Local Variables: */
char buf[150]; /* Buffer for warning message */
double cos_theta0; /* Cosine of theta0 */
double cos_phip; /* Cosine of (phip - phi0) */
double cos_delta0; /* Cosine of delta0 */
double cos_deltap; /* Cosine of deltap */
double deltap_1; /* First possible value for deltap */
double deltap_2; /* Second possible value for deltap */
double sin_theta0; /* Sine of theta0 */
double sin_phip; /* Sine of (phip - phi0) */
double sin_delta0; /* Sine of delta0 */
double sin_deltap; /* Sine of deltap */
double t0, t1, t2, t3, t4; /* Intermediate values */
double phi0, theta0; /* Native coords of fiducial point */
/* Check the global status. */
if ( !astOK ) return 0;
/* Get the longitude and latitude of the fiducial point in the native
spherical coordinate frame (in radians). */
GetFiducialNSC( wcsmap, &phi0, &theta0, status );
/* If no value was supplied for the FITS keyword LONPOLE, set up a default
value such that the celestial latitude increases in the same direction
as the native latitude at the fiducial; point. */
if( *phip == AST__BAD ){
if( delta0 >= theta0 ){
*phip = 0.0;
} else {
*phip = AST__DPI;
}
/* Issue a warning that a default lonpole value has been adopted. */
sprintf( buf, "The original FITS header did not specify the "
"longitude of the native north pole. A default value "
"of %.8g degrees was assumed.", (*phip)*AST__DR2D );
Warn( this, "nolonpole", buf, "astRead", "FitsChan", status );
}
/* If the fiducial point is coincident with the Native North Pole, then the
Native North Pole must have the same coordinates as the fiducial
point. Tests for equality include some tolerance to allow for rounding
errors. */
sin_theta0 = sin( theta0 );
if( EQUAL( sin_theta0, 1.0 ) ){
*alphap = alpha0;
*deltap = delta0;
/* If the fiducial point is concident with the Native South Pole, then the
Native North Pole must have the coordinates of the point diametrically
opposite the fiducial point. */
} else if( EQUAL( sin_theta0, -1.0 ) ){
*alphap = alpha0 + AST__DPI;
*deltap = -delta0;
/* For all other cases, go through the procedure described in the WCS paper
by Greisen & Calabretta, to find the position of the Native North Pole.
First store some useful values. */
} else {
cos_theta0 = cos( theta0 );
cos_delta0 = cos( delta0 );
cos_phip = cos( *phip - phi0 );
sin_delta0 = sin( delta0 );
sin_phip = sin( *phip - phi0 );
/* Next, find the two possible values for the latitude of the Native
North Pole (deltap). If any stage of this transformation is
indeterminate, return zero (except for the single special case noted
in item 6 para. 2 of the WCS paper, for which LATPOLE specifies the
values to be used). */
t0 = cos_theta0*cos_phip;
if( fabs( t0 ) < TOL2 && fabs( sin_theta0 ) < TOL2 ){
if( latpole != AST__BAD ) {
*deltap = latpole;
} else {
return 0;
}
} else {
t1 = atan2( sin_theta0, t0 );
t2 = cos_theta0*cos_phip;
t2 *= t2;
t2 += sin_theta0*sin_theta0;
if( t2 <= DBL_MIN ){
return 0;
} else {
t3 = sin_delta0/sqrt( t2 );
if( fabs( t3 ) > 1.0 + TOL1 ){
return 0;
} else {
if( t3 < -1.0 ){
t4 = AST__DPI;
} else if( t3 > 1.0 ){
t4 = 0.0;
} else {
t4 = acos( t3 );
}
deltap_1 = palDrange( t1 + t4 );
deltap_2 = palDrange( t1 - t4 );
/* Select which of these two values of deltap to use. Values outside the
range +/- PI/2 cannot be used. If both values are within this range
use the value which is closest to the supplied value of latpole (or
use the northern most value if the LATPOLE keyword was not supplied. */
if( fabs( deltap_1 ) > AST__DPIBY2 + TOL2 ){
*deltap = deltap_2;
} else if( fabs( deltap_2 ) > AST__DPIBY2 + TOL2 ){
*deltap = deltap_1;
} else {
if( latpole != AST__BAD ){
if( fabs( deltap_1 - latpole ) <
fabs( deltap_2 - latpole ) ){
*deltap = deltap_1;
} else {
*deltap = deltap_2;
}
} else {
if( deltap_1 > deltap_2 ){
*deltap = deltap_1;
} else {
*deltap = deltap_2;
}
/* Issue a warning that a default latpole value has been adopted. */
sprintf( buf, "The original FITS header did not specify "
"the latitude of the native north pole. A "
"default value of %.8g degrees was assumed.",
(*deltap)*AST__DR2D );
Warn( this, "nolatpole", buf, "astRead", "FitsChan", status );
}
}
if( fabs( *deltap ) > AST__DPIBY2 + TOL2 ) {
return 0;
} else if( *deltap < -AST__DPIBY2 ){
*deltap = -AST__DPIBY2;
} else if( *deltap > AST__DPIBY2 ){
*deltap = AST__DPIBY2;
}
}
}
}
/* If a valid value for the latitude (deltap) has been found, find the
longitude of the Native North Pole. */
if( *deltap != AST__BAD ) {
if( fabs( cos_delta0) > TOL2 ){
cos_deltap = cos( *deltap );
sin_deltap = sin( *deltap );
if( fabs( cos_deltap ) > TOL2 ){
t1 = sin_phip*cos_theta0/cos_delta0;
t2 = ( sin_theta0 - sin_deltap*sin_delta0 )
/( cos_delta0*cos_deltap );
if( ( fabs( t1 ) > TOL2 ) || ( fabs( t2 ) > TOL2 ) ){
*alphap = alpha0 - atan2( t1, t2 );
} else {
*alphap = alpha0;
}
} else if( sin_deltap > 0.0 ){
*alphap = alpha0 + (*phip - phi0) - AST__DPI;
} else {
*alphap = alpha0 - (*phip - phi0);
}
} else {
*alphap = alpha0;
}
} else {
*alphap = AST__BAD;
}
}
/* Return a success status if valid latitude and longitude values were
found. */
return (*deltap) != AST__BAD && (*alphap) != AST__BAD ;
}
static AstMapping *WcsOthers( AstFitsChan *this, FitsStore *store, char s,
AstFrame **frm, AstFrame *iwcfrm, const char *method,
const char *class, int *status ){
/*
* Name:
* WcsOthers
* Purpose:
* Create a Mapping from intermediate world coords to any axes
* which are not covered by specialised conventions.
* Type:
* Private function.
* Synopsis:
* AstMapping *WcsOthers( AstFitsChan *this, FitsStore *store, char s,
* AstFrame **frm, AstFrame *iwcfrm, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* This function interprets the contents of the supplied FitsStore
* structure, looking for world coordinate axes for which no
* description has yet been added to the supplied Frame . It is
* assumed that any such axes are simple linear axes. It returns a
* Mapping which simply adds on the CRVAL values to such axes.
* It also modifies the supplied Frame to describe the axes.
* Parameters:
* this
* The FitsChan.
* store
* A structure containing information about the requested axis
* descriptions derived from a FITS header.
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* frm
* The address of a location at which to store a pointer to the
* Frame describing the world coordinate axes.
* iwcfrm
* A pointer to the Frame describing the intermediate world coordinate
* axes. The properties of this Frame may be changed on exit.
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the Mapping.
*/
/* Local Variables: */
AstFrame *pfrm; /* Pointer to primary Frame */
AstFrame *pfrm2; /* Pointer to primary Frame */
AstMapping *map1; /* Pointer to a Mapping */
AstMapping *map2; /* Pointer to a Mapping */
AstMapping *ret; /* The returned Mapping */
char **comms; /* Pointer to array of CTYPE commments */
char buf[ 100 ]; /* Buffer for textual attribute value */
char buf2[ 100 ]; /* Buffer for textual attribute value */
char buf3[ 20 ]; /* Buffer for default CTYPE value */
char *newdom; /* Pointer to new Domain value */
const char *ckeyval; /* Pointer to character keyword value */
int i; /* Axis index */
int j; /* Axis index */
int len; /* Used length of string */
int naxes; /* no. of axes in Frame */
int nother; /* The number of "other" axes */
int paxis; /* Primary axis index */
int usecom; /* Use CTYPE comments as axis Labels? */
/* Initialise the pointer to the returned Mapping. */
ret = NULL;
/* Check the global status. */
if ( !astOK ) return ret;
/* Get the number of physical axes. */
naxes = astGetNaxes( *frm );
/* Assume we will use CTYPE comments as the axis labels. */
usecom = 1;
/* Initialise the count of "other" axes. */
nother = 0;
/* Get the comments associated with the CTYPE keywords for all "other"
axes. */
comms = astMalloc( naxes*sizeof( char * ) );
if( comms ) {
/* Loop round all axes in the Frame, and initialise the pointer to its
comment. */
for( i = 0; i < naxes; i++ ){
comms[ i ] = NULL;
/* Get the Domain for the primary frame containing the axis. This will be
"AST_FITSCHAN" if the axis has not yet been recognised (this Domain is
set up by WcsMapFrm). Only consider the axis further if the Domain has
not been changed. */
astPrimaryFrame( *frm, i, &pfrm, &paxis );
if( !strcmp( astGetDomain( pfrm ), "AST_FITSCHAN" ) ) {
/* Increment the count of "other" axes. */
nother++;
/* Get the comment associated with the CTYPE header. */
ckeyval = GetItemC( &(store->ctype_com), i, 0, s, NULL, method, class, status );
/* If this axis has no CTYPE comment, we will use CTYPE values as axis
labels (if given, the CNAME keyword take precedence). */
if( !ckeyval || astChrLen( ckeyval ) == 0 ) {
usecom = 0;
/* If the CTYPE comment for this axis is the same as any other comment, we
will use CTYPE values as axis labels. */
} else {
for( j = 0; j < nother - 1; j++ ) {
if( comms[ j ] && !strcmp( ckeyval, comms[ j ] ) ) {
usecom = 0;
break;
}
}
}
/* If we are still using comments as axis labels, store a copy of it in the
workspace. */
if( usecom ) comms[ i ] = astStore( NULL, ckeyval,
strlen( ckeyval ) + 1 );
}
pfrm = astAnnul( pfrm );
}
/* Free the workspace holding comments. */
for( i = 0; i < naxes; i++ ) comms[ i ] = astFree( comms[ i ] );
comms = astFree( comms );
}
/* If there are no "other" axes, just return a UnitMap. */
if( nother == 0 ) {
ret = (AstMapping *) astUnitMap( naxes, "", status );
/* Otherwise... */
} else {
/* If we have only a single other axis, use CTYPE value instead of
comment. */
if( nother == 1 ) usecom = 0;
/* Not yet started a new Domain value to replace "AST_FITSCHAN". */
newdom = NULL;
pfrm2 = NULL;
/* Check each axis of the Frame looking for axes which have not yet been
recognised. */
for( i = 0; i < naxes; i++ ) {
/* Get the Domain for the primary frame containing the axis. This will be
"AST_FITSCHAN" if the axis has not yet been recognised (this Domain is
set up by WcsMapFrm). Only consider the axis further if the Domain has
not been changed. */
astPrimaryFrame( *frm, i, &pfrm, &paxis );
if( !strcmp( astGetDomain( pfrm ), "AST_FITSCHAN" ) ) {
/* Save a pointer to the primary Frame which we will use to set the
Domain of the primary Frame. */
if( !pfrm2 ) pfrm2 = astClone( pfrm );
/* Get the CTYPE value. Use a default of "AXISn". */
ckeyval = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
if( !ckeyval ) {
sprintf( buf3, "AXIS%d", i + 1 );
ckeyval = buf3;
}
/* If the CTYPE value ends with "-LOG", assume it is a logarithmically spaced
axis. Get the Mapping from IWC to WCS. Reduce the used length of the
CTYPE string to exlude any trailing "-LOG" string. */
len = strlen( ckeyval );
if( len > 3 && !strcmp( ckeyval + len - 4, "-LOG" ) ){
map1 = LogWcs( store, i, s, method, class, status );
sprintf( buf2, "%.*s", len - 4, ckeyval );
/* Otherwise, assume the axis is linearly spaced. */
} else {
map1 = LinearWcs( store, i, s, method, class, status );
sprintf( buf2, "%.*s", len, ckeyval );
}
/* Append the CTYPE value to the final Domain value for the primary Frame. */
if( ckeyval && astChrLen( ckeyval ) > 0 ) {
if( newdom ) {
sprintf( buf, "%s-%s", newdom, buf2 );
} else {
sprintf( buf, "%s", buf2 );
newdom = buf;
}
}
/* Now modify the axis in the Frame to have appropriate values for the
Unit, Label and Symbol attributes. Also set the Unit attribute for
the corresponding axis in the IWC Frame. */
if( ckeyval ) astSetSymbol( *frm, i, buf2 );
ckeyval = GetItemC( &(store->cname), i, 0, s, NULL, method, class, status );
if( !ckeyval && usecom ) ckeyval = GetItemC( &(store->ctype_com),
i, 0, s, NULL, method, class, status );
if( !ckeyval ) ckeyval = buf2;
if( ckeyval ) astSetLabel( *frm, i, ckeyval );
ckeyval = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status );
if( ckeyval ) {
astSetUnit( *frm, i, ckeyval );
astSetUnit( iwcfrm, i, ckeyval );
}
/* If this axis has been described by an earlier function (because it
uses specialised conventions such as those described in FITS-WCS papers
II or III), then create a UnitMap for this axis. */
} else {
map1 = (AstMapping *) astUnitMap( 1, "", status );
}
/* Annul the pointer to the primary Frame containing the current axis. */
pfrm = astAnnul( pfrm );
/* Add the Mapping for this axis in parallel with the current "running sum"
Mapping (if any). */
if( ret ) {
map2 = (AstMapping *) astCmpMap( ret, map1, 0, "", status );
ret = astAnnul( ret );
map1 = astAnnul( map1 );
ret = map2;
} else {
ret = map1;
}
}
/* Set the Domain name for the primary Frame. It is currently set to
AST_FITSCHAN. We replace it with a value formed by concatenating the
CTYPE values of its axes. */
if( pfrm2 ) {
if( newdom && astChrLen( newdom ) > 0 ) {
astSetDomain( pfrm2, newdom );
} else {
astClearDomain( pfrm2 );
}
pfrm2 = astAnnul( pfrm2 );
}
/* If the header contained a WCSNAME keyword, use it as the Domain name for
the Frame. Also use it to create a title. */
ckeyval = GetItemC( &(store->wcsname), 0, 0, s, NULL, method, class, status );
if( ckeyval ){
astSetDomain( *frm, ckeyval );
sprintf( buf, "%s coordinates", ckeyval );
astSetTitle( *frm, buf );
}
}
/* Return the result. */
return ret;
}
static AstWinMap *WcsShift( FitsStore *store, char s, int naxes,
const char *method, const char *class, int *status ){
/*
* Name:
* WcsShift
* Purpose:
* Create a WinMap which shifts pixels coordinates so that their origin
* is at the reference pixel.
* Type:
* Private function.
* Synopsis:
* AstWinMap *WcsShift( FitsStore *store, char s, int naxes,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* A WinMap is created which implements a shift of origin by subtracting
* the reference pixel coordinates (CRPIXi) from the input pixel
* coordinates.
* Parameters:
* store
* A structure containing values for FITS keywords relating to
* the World Coordinate System.
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* naxes
* The number of intermediate world coordinate axes (WCSAXES).
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the created WinMap or a NULL pointer if an
* error occurred.
* Notes:
* - If an error occurs, a NULL pointer is returned.
*/
/* Local Variables: */
AstWinMap *new; /* The created WinMap */
int j; /* Pixel axis index */
double crpix; /* CRPIX keyword value */
double *c1_in; /* Input corner 1 */
double *c2_in; /* Input corner 2 */
double *c1_out; /* Output corner 1 */
double *c2_out; /* Output corner 2 */
/* Check the global status. */
if ( !astOK ) return NULL;
/* Initialise the returned WinMap pointer. */
new = NULL;
/* Allocate memory to hold the two corners, in both input and output
coordinates. */
c1_in = (double *) astMalloc( sizeof( double )*(size_t) naxes );
c1_out = (double *) astMalloc( sizeof( double )*(size_t) naxes );
c2_in = (double *) astMalloc( sizeof( double )*(size_t) naxes );
c2_out = (double *) astMalloc( sizeof( double )*(size_t) naxes );
/* Check these pointers can be used. */
if( astOK ){
/* Set up two arbitrary corners in the input coordinate system, and the
corresponding values with the CRPIX values subtracted off. */
for( j = 0; j < naxes; j++ ){
/* Get the CRPIX value for this axis. */
crpix = GetItem( &(store->crpix), 0, j, s, NULL, method, class, status );
if( crpix == AST__BAD ) crpix = 0.0;
/* Store the corner co-ordinates. */
c1_in[ j ] = 0.0;
c2_in[ j ] = 1.0;
c1_out[ j ] = -crpix;
c2_out[ j ] = 1.0 - crpix;
}
/* Create the WinMap. */
new = astWinMap( naxes, c1_in, c2_in, c1_out, c2_out, "", status );
/* If an error has occurred, attempt to annul the new WinMap. */
if( !astOK ) new = astAnnul( new );
}
/* Free the memory holding the corners. */
c1_in = (double *) astFree( (void *) c1_in );
c1_out = (double *) astFree( (void *) c1_out );
c2_in = (double *) astFree( (void *) c2_in );
c2_out = (double *) astFree( (void *) c2_out );
/* Return the WinMap. */
return new;
}
static AstSkyFrame *WcsSkyFrame( AstFitsChan *this, FitsStore *store, char s,
int prj, char *sys, int axlon, int axlat,
const char *method, const char *class, int *status ){
/*
* Name:
* WcsSkyFrame
* Purpose:
* Create a SkyFrame to describe a WCS celestial coordinate system.
* Type:
* Private function.
* Synopsis:
* AstSkyFrame *WcsSkyFrame( AstFitsChan this, FitsStore *store, char s, int prj,
* char *sys, int axlon, int axlat, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* A SkyFrame is returned describing the celestial coordinate system
* described by a FITS header. The axes are *not* permuted in the
* returned Frame (that is, axis 0 is longitude and axis 1 is latitude
* in the returned SkyFrame, no matter what values are supplied for
* "axlat" and "axlon").
* Parameters:
* this
* The FitsChan from which the keywords were read. Warning messages
* may be added to this FitsChan.
* store
* A structure containing values for FITS keywords relating to
* the World Coordinate System.
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* prj
* An integer code for the WCS projection being used.
* sys
* A pointer to a string identifying the celestial co-ordinate system
* implied by the CTYPE values in the FitsStore. This will be "EQU" (for
* equatorial), or a one or two character code extracted from the
* CTYPE values.
* axlon
* Zero based index of the longitude axis in the FITS header.
* axlat
* Zero based index of the latitude axis in the FITS header.
* method
* The calling method. Used only in error messages.
* class
* The object class. Used only in error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the SkyFrame.
* Notes:
* - A NULL pointer is returned if an error has already occurred, or
* if this function should fail for any reason.
*/
/* Local Variables: */
AstSkyFrame *ret; /* Returned Frame */
char *ckeyval; /* Pointer to string item value */
char *lattype; /* Pointer to latitude CTYPE value */
char *lontype; /* Pointer to longitude CTYPE value */
char bj; /* Besselian/Julian selector */
char buf[300]; /* Text buffer */
char sym[10]; /* Axis symbol */
double dval; /* Floating point attribute value */
double eqmjd; /* MJD equivalent of equinox */
double equinox; /* EQUINOX value */
double geolat; /* Observer's geodetic latitude */
double geolon; /* Observer's geodetic longitude */
double h; /* Observer's geodetic height */
double mjdobs; /* MJD-OBS value */
double obsgeo[ 3 ]; /* Observer's Cartesian position */
int radesys; /* RADESYS value */
int report; /* Report unknown lon/lat system? */
/* Initialise. */
ret = NULL;
/* Check the global error status. */
if ( !astOK ) return ret;
/* Get the RADESYS keyword from the header, and identify the value.
Store a integer value identifying the system. Report an error if an
unrecognised system is supplied. Store NORADEC if the keyword was
not supplied. */
ckeyval = GetItemC( &(store->radesys), 0, 0, s, NULL, method, class, status );
radesys = NORADEC;
if( ckeyval ){
if( !strncmp( ckeyval, "FK4 ", 4 ) ||
!strcmp( ckeyval, "FK4" ) ){
radesys = FK4;
} else if( !strncmp( ckeyval, "FK4-NO-E", 8 ) ){
radesys = FK4NOE;
} else if( !strncmp( ckeyval, "FK5 ", 4 ) ||
!strcmp( ckeyval, "FK5" ) ){
radesys = FK5;
} else if( !strncmp( ckeyval, "ICRS ", 5 ) ||
!strcmp( ckeyval, "ICRS" ) ){
radesys = ICRS;
} else if( !strncmp( ckeyval, "GAPPT ", 6 ) ||
!strcmp( ckeyval, "GAPPT" ) ){
radesys = GAPPT;
} else if( astOK ){
astError( AST__BDFTS, "%s(%s): FITS keyword '%s' has the "
"unrecognised value '%s'.", status, method, class,
FormatKey( "RADESYS", -1, -1, s, status ), ckeyval );
}
} else {
radesys = NORADEC;
}
/* Get the value of the EQUINOX keyword. */
equinox = GetItem( &(store->equinox), 0, 0, s, NULL, method, class, status );
/* For FK4 and FK4-NO-E any supplied equinox value is Besselian. For all
other systems, the equinox value is Julian. */
bj = 0;
if( equinox != AST__BAD ){
if( radesys == FK4 || radesys == FK4NOE ){
bj = 'B';
} else if( radesys != NORADEC ) {
bj = 'J';
/* If no RADESYS was suppied, but an equinox was, use the IAU 1984 rule
to determine the default RADESYS and equinox type. */
} else {
if( equinox < 1984.0 ){
radesys = FK4;
bj = 'B';
} else {
radesys = FK5;
bj = 'J';
}
/* If an equatorial system is being used, give a warning that a default RADESYS
value is being used. */
if( !strcmp( sys, "EQU" ) ){
sprintf( buf, "The original FITS header did not specify the "
"RA/DEC reference frame. A default value of %s was "
"assumed.", ( radesys == FK4 ) ? "FK4" : "FK5" );
Warn( this, "noradesys", buf, method, class, status );
}
}
/* If no equinox was supplied, use a default equinox value depending
on the frame of reference. For FK4-based systems, use B1950. */
} else {
if( radesys == FK4 || radesys == FK4NOE ){
equinox = 1950.0;
bj = 'B';
/* For FK5-based systems, use J2000. */
} else if( radesys == FK5 ){
equinox = 2000.0;
bj = 'J';
/* If no RADESYS or EQUINOX was supplied, assume either FK4 B1950 or ICRS -
as decided by attribute DefB1950 (GAPPT and ICRS do not use EQUINOX). */
} else if( radesys == NORADEC ) {
if( astGetDefB1950( this ) ) {
equinox = 1950.0;
bj = 'B';
radesys = FK4;
} else {
radesys = ICRS;
}
if( !strcmp( sys, "EQU" ) ){
sprintf( buf, "The original FITS header did not specify the "
"RA/DEC reference frame. A default value of %s was "
"assumed.", ( radesys == FK4 ) ? "FK4" : "ICRS" );
Warn( this, "noradesys", buf, method, class, status );
}
}
/* If we have an equatorial or ecliptic system, issue a warning that a default
equinox has been adopted. */
if( ( !strcmp( sys, "EQU" ) && radesys != ICRS && radesys != GAPPT ) ||
!strcmp( sys, "ECL" ) ){
sprintf( buf, "The original FITS header did not specify the "
"reference equinox. A default value of %c%.8g was "
"assumed.", bj, equinox );
Warn( this, "noequinox", buf, method, class, status );
}
}
/* Convert the equinox to a Modified Julian Date. */
if( equinox != AST__BAD ) {
if( bj == 'B' ) {
eqmjd = palEpb2d( equinox );
} else {
eqmjd = palEpj2d( equinox );
}
} else {
eqmjd = AST__BAD;
}
/* Get a value for the Epoch attribute. If no value is available, use
EQUINOX and issue a warning. */
mjdobs = ChooseEpoch( this, store, s, method, class, status );
if( mjdobs == AST__BAD ) {
mjdobs = eqmjd;
if( mjdobs != AST__BAD ) {
sprintf( buf, "The original FITS header did not specify the "
"date of observation. A default value of %c%.8g was "
"assumed.", bj, equinox );
Warn( this, "nomjd-obs", buf, method, class, status );
}
}
/* Create a SkyFrame for the specified system. */
if( !strcmp( sys, "E" ) ){
ret = astSkyFrame( "System=Ecliptic", status );
} else if( !strcmp( sys, "H" ) ){
ret = astSkyFrame( "System=Helioecliptic", status );
} else if( !(strcmp( sys, "G" ) ) ){
ret = astSkyFrame( "System=Galactic", status );
} else if( !(strcmp( sys, "S" ) ) ){
ret = astSkyFrame( "System=Supergalactic", status );
} else if( !(strcmp( sys, "AZL" ) ) ){
ret = astSkyFrame( "System=AzEl", status );
} else if( !(strcmp( sys, "EQU" ) ) ){
/* For equatorial systems, the specific system is given by the RADESYS
value. */
if( radesys == FK4 ){
ret = astSkyFrame( "System=FK4", status );
} else if( radesys == FK4NOE ){
ret = astSkyFrame( "System=FK4-NO-E", status );
} else if( radesys == FK5 ){
ret = astSkyFrame( "System=FK5", status );
} else if( radesys == ICRS ){
ret = astSkyFrame( "System=ICRS", status );
} else if( radesys == GAPPT ){
ret = astSkyFrame( "System=GAPPT", status );
} else if( astOK ){
astError( AST__INTER, "%s(%s): Internal AST programming "
"error - FITS equatorial coordinate system type %d "
"not yet supported in WcsSkyFrame.", status, method, class, radesys );
}
/* If an unknown celestial co-ordinate system was specified by the CTYPE
keywords, add warning messages to the FitsChan and treat the axes as
a general spherical coordinate system. */
} else if( astOK ){
report = 1;
ret = astSkyFrame( "System=UNKNOWN", status );
strcpy( sym, sys );
if( strlen( sys ) == 1 ) {
strcpy( sym + 1, "LON" );
astSetSymbol( ret, 0, sym );
strcpy( sym + 1, "LAT" );
astSetSymbol( ret, 1, sym );
} else {
strcpy( sym + 2, "LN" );
astSetSymbol( ret, 0, sym );
strcpy( sym + 2, "LT" );
astSetSymbol( ret, 1, sym );
/* The code "OF" is used by AST to describe offset sky coordinates. Set
the Domain to SKY_OFFSETS in these cases, so that we can identify
these Frames later. */
if( !strcmp( sys, "OF" ) ) {
astSetDomain( ret, "SKY_OFFSETS" );
report = 0;
}
}
if( report ) {
lontype = GetItemC( &(store->ctype), axlon, 0, s, NULL, method, class, status );
lattype = GetItemC( &(store->ctype), axlat, 0, s, NULL, method, class, status );
if( lontype && lattype ){
sprintf( buf, "This FITS header contains references to an unknown "
"spherical co-ordinate system specified in the values "
"%s and %s. It may not be possible to convert to "
"other standard co-ordinate systems.", lontype, lattype );
Warn( this, "badcel", buf, method, class, status );
}
}
}
/* If a skyFrame was created... */
if( ret ){
/* Store the projection description. */
if( prj != AST__WCSBAD ) astSetProjection( ret, astWcsPrjDesc( prj ) );
/* Store the epoch of the observation in the SkyFrame. */
if( mjdobs != AST__BAD ) astSetEpoch( ret, mjdobs );
/* For equatorial and ecliptic systems, store the epoch of the reference
equinox in the SkyFrame. */
if( ( !strcmp( sys, "EQU" ) || !strcmp( sys, "ECL" ) ) &&
equinox != AST__BAD ) astSetEquinox( ret, eqmjd );
/* If either of the CNAME keywords is set, use it as the axis label. */
ckeyval = GetItemC( &(store->cname), axlon, 0, s, NULL, method, class, status );
if( ckeyval ) astSetLabel( ret, 0, ckeyval );
ckeyval = GetItemC( &(store->cname), axlat, 0, s, NULL, method, class, status );
if( ckeyval ) astSetLabel( ret, 1, ckeyval );
/* Observer's position (from primary axis descriptions). Get the OBSGEO-X/Y/Z
keywords, convert to geodetic longitude and latitude and store as the
SpecFrame's ObsLat, ObsLon and ObsAlt attributes. */
obsgeo[ 0 ] = GetItem( &(store->obsgeox), 0, 0, ' ', NULL, method, class, status );
obsgeo[ 1 ] = GetItem( &(store->obsgeoy), 0, 0, ' ', NULL, method, class, status );
obsgeo[ 2 ] = GetItem( &(store->obsgeoz), 0, 0, ' ', NULL, method, class, status );
if( obsgeo[ 0 ] != AST__BAD &&
obsgeo[ 1 ] != AST__BAD &&
obsgeo[ 2 ] != AST__BAD ) {
eraGc2gd( 1, obsgeo, &geolon, &geolat, &h );
astSetObsLat( ret, geolat );
astSetObsLon( ret, geolon );
astSetObsAlt( ret, h );
}
/* Store values for the reference point in the SkyFrame. */
dval = GetItem( &(store->skyref), axlon, 0, s, NULL, method, class, status );
if( dval != AST__BAD ) astSetSkyRef( ret, 0, dval );
dval = GetItem( &(store->skyref), axlat, 0, s, NULL, method, class, status );
if( dval != AST__BAD ) astSetSkyRef( ret, 1, dval );
dval = GetItem( &(store->skyrefp), axlon, 0, s, NULL, method, class, status );
if( dval != AST__BAD ) astSetSkyRefP( ret, 0, dval );
dval = GetItem( &(store->skyrefp), axlat, 0, s, NULL, method, class, status );
if( dval != AST__BAD ) astSetSkyRefP( ret, 1, dval );
/* We cannot store the SkyRefIs value yet since this needs to be done
after the SkyFrame has been added into the FrameSet, so that the Frame
will be remapped to represent the intended offsets. SO instance, mark
the Frame by setting the domain to "SKY_POLE" or "SKY_ORIGIN". This
odd Domain value will be cleared later in TidyOffsets. */
ckeyval = GetItemC( &(store->skyrefis), 0, 0, s, NULL, method, class, status );
if( ckeyval ) {
if( !Ustrcmp( "POLE", ckeyval, status ) ) {
astSetDomain( ret, "SKY_POLE" );
} else if( !Ustrcmp( "ORIGIN", ckeyval, status ) ) {
astSetDomain( ret, "SKY_ORIGIN" );
}
}
}
/* If an error has occurred, annul the Frame. */
if( !astOK ) ret = astAnnul( ret );
/* Return the Frame. */
return ret;
}
static AstMapping *WcsSpectral( AstFitsChan *this, FitsStore *store, char s,
AstFrame **frm, AstFrame *iwcfrm, double reflon, double reflat,
AstSkyFrame *reffrm, const char *method,
const char *class, int *status ){
/*
* Name:
* WcsSpectral
* Purpose:
* Create a Mapping from intermediate world coords to spectral coords
* as described in a FITS header.
* Type:
* Private function.
* Synopsis:
* AstMapping *WcsSpectral( AstFitsChan *this, FitsStore *store, char s,
* AstFrame **frm, AstFrame *iwcfrm, double reflon,
* double reflat, AstSkyFrame *reffrm,
* const char *method, const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* This function interprets the contents of the supplied FitsStore
* structure, looking for world coordinate axes which describe positions
* in a spectrum. If such an axis is found, a Mapping is returned which
* transforms the corresponding intermediate world coordinates to
* spectral world coordinates (this mapping leaves any other axes
* unchanged). It also, modifies the supplied Frame to describe the
* axis (again, other axes are left unchanged). If no spectral axis
* is found, a UnitMap is returned, and the supplied Frame is left
* unchanged.
* Parameters:
* this
* The FitsChan.
* store
* A structure containing information about the requested axis
* descriptions derived from a FITS header.
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* frm
* The address of a location at which to store a pointer to the
* Frame describing the world coordinate axes.
* iwcfrm
* A pointer to the Frame describing the intermediate world coordinate
* axes. The properties of this Frame may be changed on exit.
* reflon
* The reference celestial longitude, in the frame given by reffrm.
* reflat
* The reference celestial latitude, in the frame given by reffrm.
* reffrm
* The SkyFrame defining reflon and reflat.
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the Mapping.
*/
/* Local Variables: */
AstFrame *ofrm; /* Pointer to a Frame */
AstMapping *map1; /* Pointer to Mapping */
AstMapping *map2; /* Pointer to Mapping */
AstMapping *ret; /* Pointer to the returned Mapping */
AstSpecFrame *specfrm; /* Pointer to a SpecFrame */
char algcode[ 5 ]; /* Displayed spectral type string */
char stype[ 5 ]; /* Displayed spectral type string */
const char *cname; /* Pointer to CNAME value */
const char *ctype; /* Pointer to CTYPE value */
const char *cunit; /* Pointer to CUNIT value */
const char *defunit; /* Default unit string */
const char *specsys; /* Pointer to SPECSYS value */
const char *ssyssrc; /* Pointer to SSYSSRC value */
double geolat; /* Observer's geodetic latitude */
double geolon; /* Observer's geodetic longitude */
double h; /* Observer's geodetic height */
double mjd; /* Modified Julian Date */
double obscentre; /* Spectral value at observation centre */
double obsgeo[ 3 ]; /* Observer's Cartesian position */
double restfrq; /* RESTFRQ keyword value */
double vsource; /* Source velocity */
int *axes; /* Pointer to axis permutation array */
int i; /* Axis index */
int j; /* Loop count */
int k; /* Loop count */
int kk; /* Loop count */
int naxes; /* No. of axes in Frame */
/* Initialise the pointer to the returned Mapping. */
ret = NULL;
/* Check the global status. */
if ( !astOK ) return ret;
/* Get the number of physical axes. */
naxes = astGetNaxes( *frm );
/* An array to hold a list of axis selections. */
axes = astMalloc( naxes*sizeof( int ) );
/* Loop round checking each axis. */
defunit = NULL;
map1 = NULL;
for( i = 0; i < naxes && astOK; i++ ) {
/* Get the CTYPE value. Pass on to the next axis if no CTYPE is available. */
ctype = GetItemC( &(store->ctype), i, 0, s, NULL, method, class, status );
if( ctype ) {
/* See if this CTYPE describes a spectral axis, and if so, extract the
system code, the algorithm code and get the default units. */
defunit = IsSpectral( ctype, stype, algcode, status );
/* Skip to the next axis if the system type was not a spectral system
type. */
if( defunit ) {
/* Create a SpecFrame or DSBSpecFrame with this system (the FITS type codes
are also legal SpecFrame System values). We use astSetC rather than
astSetSystem because astSetC translates string values into the
corresponding integer system identifiers. */
if( GetItem( &(store->imagfreq), 0, 0, s, NULL, method,
class, status ) == AST__BAD ) {
specfrm = astSpecFrame( "", status );
} else {
specfrm = (AstSpecFrame *) astDSBSpecFrame( "", status );
}
astSetC( specfrm, "System", stype );
/* Set the reference position (attributes RefRA and RefDec), if known. */
if( reffrm ) astSetRefPos( specfrm, reffrm, reflon, reflat );
/* Set the SpecFrame units. Use the value of the CUNIT FITS keyword for this
axis if available, otherwise use the default units for the system, noted
above. */
cunit = GetItemC( &(store->cunit), i, 0, s, NULL, method, class, status );
if( !cunit ) cunit = defunit;
astSetUnit( specfrm, 0, cunit );
/* Set the axis unit in the IWC Frame. */
astSetUnit( iwcfrm, i, cunit );
/* Get a value for the Epoch attribute (the date of observation). */
mjd = ChooseEpoch( this, store, s, method, class, status );
if( mjd != AST__BAD ) astSetEpoch( specfrm, mjd );
/* Set the rest frequency. Use the RESTFRQ keyword (assumed to be in Hz),
or (if RESTFRQ is not available), RESTWAV (assumes to be in m). */
restfrq = GetItem( &(store->restfrq), 0, 0, s, NULL, method, class, status );
if( restfrq == AST__BAD ) {
restfrq = GetItem( &(store->restwav), 0, 0, s, NULL, method, class, status );
if( restfrq != AST__BAD ) restfrq = AST__C/restfrq;
}
astSetRestFreq( specfrm, restfrq );
/* Observer's position (from primary axis descriptions). Get the OBSGEO-X/Y/Z
keywords, convert to geodetic longitude and latitude and store as the
SpecFrame's ObsLat, ObsLon and ObsAlt attributes. */
obsgeo[ 0 ] = GetItem( &(store->obsgeox), 0, 0, ' ', NULL, method, class, status );
obsgeo[ 1 ] = GetItem( &(store->obsgeoy), 0, 0, ' ', NULL, method, class, status );
obsgeo[ 2 ] = GetItem( &(store->obsgeoz), 0, 0, ' ', NULL, method, class, status );
if( obsgeo[ 0 ] != AST__BAD &&
obsgeo[ 1 ] != AST__BAD &&
obsgeo[ 2 ] != AST__BAD ) {
eraGc2gd( 1, obsgeo, &geolon, &geolat, &h );
astSetObsLat( specfrm, geolat );
astSetObsLon( specfrm, geolon );
astSetObsAlt( specfrm, h );
}
/* Source velocity rest frame */
ssyssrc = GetItemC( &(store->ssyssrc), 0, 0, s, NULL, method, class, status );
if( ssyssrc ) astSetC( specfrm, "SourceVRF", ssyssrc );
/* Source velocity. Use the ZSOURCE keyword and convert from redshift to
velocity. */
vsource = GetItem( &(store->zsource), 0, 0, s, NULL, method, class, status );
if( vsource != AST__BAD ) {
vsource += 1.0;
vsource *= vsource;
vsource = AST__C*( vsource - 1.0 )/( vsource + 1.0 );
astSetSourceVel( specfrm, vsource );
}
/* Reference frame. If the SPECSYS keyword is set, use it (the FITS codes
are also legal SpecFrame StdOfRest values). We use astSetC rather than
astSetSystem because astSetC translates string values into the
corresponding integer system identifiers. */
specsys = GetItemC( &(store->specsys), 0, 0, s, NULL, method, class, status );
if( specsys ) astSetC( specfrm, "StdOfRest", specsys );
/* Axis label. If the CNAME keyword is set, use it as the axis label. */
cname = GetItemC( &(store->cname), i, 0, s, NULL, method, class, status );
if( cname ) astSetLabel( specfrm, 0, cname );
/* If the header contains an AXREF value for the spectral axis, use it as the
observation centre in preferences to the CRVAL value. AXREF keywords are
created by the astWrite method for axes described by -TAB algorithm that
have no inverse transformation. */
obscentre = GetItem( &(store->axref), i, 0, s, NULL, method,
class, status );
if( obscentre == AST__BAD ) {
obscentre = GetItem( &(store->crval), i, 0, s, NULL, method,
class, status );
}
/* Now do the extra stuff needed if we are creating a dual sideband
SpecFrame. */
if( astIsADSBSpecFrame( specfrm ) ) {
DSBSetUp( this, store, (AstDSBSpecFrame *) specfrm, s,
obscentre, method, class, status );
}
/* Now branch for each type of algorithm code. Each case returns a 1D
Mapping which converts IWC value into the specified Spectral system. */
/* Linear */
if( strlen( algcode ) == 0 ) {
map1 = LinearWcs( store, i, s, method, class, status );
/* Log-Linear */
} else if( !strcmp( "-LOG", algcode ) ) {
map1 = LogWcs( store, i, s, method, class, status );
/* Non-Linear */
} else if( algcode[ 0 ] == '-' && algcode[ 2 ] == '2' ) {
map1 = NonLinSpecWcs( this, algcode, store, i, s, specfrm, method, class, status );
/* Grism */
} else if( !strcmp( "-GRI", algcode ) ||
!strcmp( "-GRA", algcode ) ) {
map1 = GrismSpecWcs( algcode, store, i, s, specfrm, method, class, status );
} else {
map1 = NULL;
}
if( map1 == NULL && astOK ) {
specfrm = astAnnul( specfrm );
astError( AST__BDFTS, "%s(%s): Cannot implement spectral "
"algorithm code '%s' specified in FITS keyword '%s'.", status,
method, class, ctype + 4, FormatKey( "CTYPE", i + 1, -1, s, status ) );
astError( AST__BDFTS, "%s(%s): Unknown algorithm code or "
"unusable parameter values.", status, method, class );
break;
}
/* Create a Frame by picking all the other (non-spectral) axes from the
supplied Frame. */
j = 0;
for( k = 0; k < naxes; k++ ) {
if( k != i ) axes[ j++ ] = k;
}
/* If there were no other axes, replace the supplied Frame with the
specframe. */
if( j == 0 ) {
(void) astAnnul( *frm );
*frm = (AstFrame *) specfrm;
/* Otherwise pick the other axes from the supplied Frame */
} else {
ofrm = astPickAxes( *frm, j, axes, NULL );
/* Replace the supplied Frame with a CmpFrame made up of this Frame and
the SpecFrame. */
(void) astAnnul( *frm );
*frm = (AstFrame *) astCmpFrame( ofrm, specfrm, "", status );
ofrm = astAnnul( ofrm );
specfrm = astAnnul( specfrm );
}
/* Permute the axis order to put the spectral axis back in its original
position. */
j = 0;
for( kk = 0; kk < naxes; kk++ ) {
if( kk == i ) {
axes[ kk ] = naxes - 1;
} else {
axes[ kk ] = j++;
}
}
astPermAxes( *frm, axes );
}
}
/* If this axis is not a spectral axis, create a UnitMap (the Frame is left
unchanged). */
if( !map1 && astOK ) map1 = (AstMapping *) astUnitMap( 1, "", status );
/* Add the Mapping for this axis in parallel with the Mappings for
previous axes. */
if( ret ) {
map2 = (AstMapping *) astCmpMap( ret, map1, 0, "", status );
ret = astAnnul( ret );
map1 = astAnnul( map1 );
ret = map2;
} else {
ret = map1;
map1 = NULL;
}
}
/* Free the axes array. */
axes= astFree( axes );
/* Return the result. */
return ret;
}
static void WcsToStore( AstFitsChan *this, AstFitsChan *trans,
FitsStore *store, const char *method,
const char *class, int *status ){
/*
* Name:
* WcsToStore
* Purpose:
* Extract WCS information from the supplied FitsChan using a FITSWCS
* encoding, and store it in the supplied FitsStore.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void WcsToStore( AstFitsChan *this, AstFitsChan *trans,
* FitsStore *store, const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* A FitsStore is a structure containing a generalised represention of
* a FITS WCS FrameSet. Functions exist to convert a FitsStore to and
* from a set of FITS header cards (using a specified encoding), or
* an AST FrameSet. In other words, a FitsStore is an encoding-
* independant intermediary staging post between a FITS header and
* an AST FrameSet.
*
* This function extracts FITSWCS keywords from the supplied FitsChan(s),
* and stores the corresponding WCS information in the supplied FitsStore.
* Keywords will be searched for first in "trans", and then, if they
* are not found in "trans", they will be searched for in "this".
* Parameters:
* this
* Pointer to the FitsChan containing the cards read from the
* original FITS header. This may include non-standard keywords.
* trans
* Pointer to a FitsChan containing cards representing standard
* translations of any non-standard keywords in "this". A NULL
* pointer indicates that "this" contains no non-standard keywords.
* store
* Pointer to the FitsStore structure.
* 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.
*/
/* Check the global error status. */
if ( !astOK ) return;
/* Read all usable cards out of the main FitsChan, into the FitsStore. */
WcsFcRead( this, trans, store, method, class, status );
/* If a FitsChan containing standard translations was supplied, read all
cards out of it, into the FitsStore, potentially over-writing the
non-standard values stored in the previous call to WcsFcRead. */
if( trans ) WcsFcRead( trans, NULL, store, method, class, status );
}
static int WorldAxes( AstFitsChan *this, AstMapping *cmap, double *dim, int *perm,
int *status ){
/*
* Name:
* WorldAxes
* Purpose:
* Associate final world axes with pixel axes.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int WorldAxes( AstFitsChan *this, AstMapping *cmap, double *dim, int *perm,
* int *status )
* Class Membership:
* FitsChan
* Description:
* This function finds the association between the axes of the final
* world coordinate system, and those of the pixel coordinate
* system. This may not simply be a 1-to-1 association because the
* Mapping may include a PermMap. Each output axis is associated with
* the input axis which is most nearly aligned with it.
* Parameters:
* this
* Pointer to the FitsChan.
* cmap
* Pointer to the Mapping from pixel coordinates to final world
* coordinates.
* dim
* Pointer to an array with one element for each input of "map",
* supplied holding the no. of pixels in the data cube along the axis, or
* AST__BAD If unknown.
* perm
* Pointer to an array with one element for each output of "map".
* On exit, each element of this array holds the zero-based index of the
* "corresponding" (i.e. most nearly parallel) pixel axis.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Non-zero for success - zero for failure.
*/
/* Local Variables: */
AstMapping *smap;
AstMapping *map;
AstPointSet *pset1;
AstPointSet *pset2;
double **ptr2;
double **ptr1;
double *dw;
double *g0;
double *nwt;
double *ntn;
double *tn;
double *wt;
double *w0;
double dg;
double s;
double sj;
double tnmin;
double wtmax;
int *outs;
int i2;
int i;
int imin;
int j2;
int j;
int jmin;
int nin;
int nout;
int nouts;
int nused;
int ret;
int retain;
int used;
/* Initialise returned value */
ret = 0;
/* Other initialisation to avoid compiler warnings. */
retain = 0;
/* Check the status */
if( !astOK ) return ret;
/* Simplfy the Mapping. */
map = astSimplify( cmap );
/* Get the number of inputs and outputs for the Mapping. */
nin = astGetNin( map );
nout = astGetNout( map );
/* Initialise "perm". */
for( i = 0; i < nout; i++ ) perm[ i ] = i;
/* First deal with Mappings that are defined in both directions. */
if( astGetTranForward( map ) && astGetTranInverse( map ) ) {
/* Use FindBasisVectors to find an input position which coresponds to a
good output position. Store it in a dynamic array pointed to by "g0". */
pset1 = astPointSet( nin+1, nin, "", status );
pset2 = astPointSet( nin+1, nout, "", status );
if( FindBasisVectors( map, nin, nout, dim, pset1, pset2, status ) ) {
g0 = astMalloc( sizeof(double)*nin );
ptr1 = astGetPoints( pset1 );
if( astOK ) {
for( j = 0; j < nin; j++ ) g0[ j ] = ptr1[ j ][ 0 ];
}
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
/* If no basis vectors found, return. */
} else {
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
return ret;
}
/* Create Pointset to hold two input (pixel) points. */
pset1 = astPointSet( 2, nin, "", status );
ptr1 = astGetPoints( pset1 );
/* Create a Pointset to hold the same number of output (world) points. */
pset2 = astPointSet( 2, nout, "", status );
ptr2 = astGetPoints( pset2 );
/* Allocate memory to use as work space */
w0 = astMalloc( sizeof(double)*nout );
dw = astMalloc( sizeof(double)*nout );
tn = astMalloc( sizeof(double)*nout*nin );
wt = astMalloc( sizeof(double)*nout*nin );
/* Check that the pointers can be used. */
if( astOK ) {
/* Transform the grid position found above, plus a position 1 pixel away
along all pixel axes, into world coords. Also set up "dw" to hold
"a small increment" along each world axis. */
for( j = 0; j < nin; j++ ) {
ptr1[ j ] [ 0 ] = g0[ j ];
ptr1[ j ] [ 1 ] = g0[ j ] + 1.0;
}
(void) astTransform( map, pset1, 1, pset2 );
for( i = 0; i < nout; i++ ) {
w0[ i ] = ptr2[ i ] [ 0 ];
if( w0[ i ] != AST__BAD && ptr2[ i ] [ 1 ] != AST__BAD ) {
dw[ i ] = fabs( 0.1*( ptr2[ i ] [ 1 ] - w0[ i ] ) );
if( dw[ i ] <= fabs( 0.001*w0[ i ] ) ) {
if( w0[ i ] != 0.0 ) {
dw[ i ] = fabs( 0.001*w0[ i ] );
} else {
dw[ i ] = 1.0;
}
}
} else {
dw[ i ] = AST__BAD;
}
}
/* Any PermMap in the mapping may result in the the "inverse transformation"
not being a true inverse of the forward transformation (for instance,
constant values fed in for degenerate axis would have this effect). To
ensure that "g0" and "w0" are corresponding positions, transform the
"w0" position back into grid coords and use the resulting grid position
as "g0". */
(void) astTransform( map, pset2, 0, pset1 );
for( j = 0; j < nin; j++ ) {
g0[ j ] = ptr1[ j ] [ 0 ];
}
/* In the next loop we find the tan of the angle between each WCS axis and
each of the pixel axes. Loop round each WCS axis. */
for( i = 0; i < nout; i++ ) {
/* Initialise the tan values for this WCS axis to AST__BAD. */
ntn = tn + i*nin;
nwt = wt + i*nin;
for( j = 0; j < nin; j++ ) ntn[ j ] = AST__BAD;
/* As a side issue, initialise the pixel axis assigned to each WCS axis
to -1, to indicate that no grid axis has yet been associated with this
WCS axis. */
perm[ i ] = -1;
/* Skip over this axis if the increment is bad. */
if( dw[ i ] != AST__BAD ) {
/* Store a WCS position which is offset from the "w0" position by a small
amount along the current WCS axis. The first position in "ptr2" is
currently "w0". */
ptr2[ i ][ 0 ] += dw[ i ];
/* Transform this position into grid coords. */
(void) astTransform( map, pset2, 0, pset1 );
/* Re-instate the original "w0" values within "ptr2", ready for the next
WCS axis. */
ptr2[ i ][ 0 ] = w0[ i ];
/* Consider each pixel axis in turn as a candidate for being assigned to
the current WCS axis. */
for( j = 0; j < nin; j++ ) {
/* Find the tan of the angle between the current ("i"th) WCS axis and the
current ("j"th) pixel axis. This gets stored in tn[j+nin*i]. A
corresponding weight for each angle is stored in nwt[j+nin*i]. This
is the length of the projection of the vector onto the "j"th pixel
axis. */
s = 0.0;
sj = 0.0;
for( j2 = 0; j2 < nin; j2++ ) {
if( ptr1[ j2 ][ 0 ] != AST__BAD ) {
dg = ptr1[ j2 ][ 0 ] - g0[ j2 ];
if( j2 != j ) {
s += dg*dg;
} else {
sj = fabs( dg );
}
} else {
s = AST__BAD;
break;
}
}
if( s != AST__BAD && sj != 0.0 ) {
ntn[ j ] = sqrt( s )/sj;
nwt[ j ] = sj;
}
}
}
}
/* Loop until every grid axes has been assigned to a WCS axis. */
while( 1 ) {
/* Pass through the array of tan values, finding the smallest. Note the
pixel and WCS axis for which the smallest tan value occurs. If the tan
values are equal, favour the one with highest weight. */
ntn = tn;
nwt = wt;
tnmin = AST__BAD;
wtmax = AST__BAD;
imin = 0;
jmin = 0;
for( i = 0; i < nout; i++ ) {
for( j = 0; j < nin; j++ ) {
if( *ntn != AST__BAD ) {
if( tnmin == AST__BAD || *ntn < tnmin ) {
tnmin = *ntn;
wtmax = *nwt;
imin = i;
jmin = j;
} else if( EQUAL( *ntn, tnmin ) && *nwt > wtmax ) {
wtmax = *nwt;
imin = i;
jmin = j;
}
}
ntn++;
nwt++;
}
}
/* Check we found a usable minimum tan value */
if( tnmin != AST__BAD ) {
/* Assign the pixel axis to the WCS axis. */
perm[ imin ] = jmin;
/* Set bad all the tan values for this pixel and WCS axis pair. This ensures
that the pixel axis will not be assigned to another WCS axis, and that
the WCS will not have another pixel axis assigned to it. */
ntn = tn;
for( i = 0; i < nout; i++ ) {
for( j = 0; j < nin; j++ ) {
if( i == imin || j == jmin ) *ntn = AST__BAD;
ntn++;
}
}
/* Leave the loop if no more good tan values were found. */
} else {
break;
}
}
/* The above process may have left some WCS axes with out any assigned
pixel axis. We assign the remaining pixel arbitrarily to such axes,
starting with the first remaining pixel axis. Find the lowest unused
pixel axis. */
for( j = 0; j < nin; j++ ) {
used = 0;
for( i = 0; i < nout; i++ ) {
if( perm[ i ] == j ) {
used = 1;
break;
}
}
if( !used ) break;
}
/* Now check each WCS axis looking for outputs which were not assigned a
pixel axis in the above process. */
for( i = 0; i < nout; i++ ) {
if( perm[ i ] == -1 ) {
/* Use the next unused axis value. */
perm[ i ] = j++;
/* Find the next unused axis value. */
for( ; j < nin; j++ ) {
used = 0;
for( i2 = 0; i2 < nout; i2++ ) {
if( perm[ i2 ] == j ) {
used = 1;
break;
}
}
if( !used ) break;
}
}
}
/* Indicate success. */
if( astOK ) ret = 1;
}
/* Free resources. */
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
g0 = astFree( g0 );
w0 = astFree( w0 );
tn = astFree( tn );
wt = astFree( wt );
dw = astFree( dw );
/* Now, if we can use the TAB algorithm, deal with Mappings that are defined only in the forward direction. */
} else if( astGetTranForward( map ) && astGetTabOK( this ) > 0 ) {
/* Assume success. */
ret = 1;
/* Initialise to indicate no outputs have yet been assigned. */
for( i = 0; i < nout; i++ ) perm[ i ] = -1;
/* Find the output associated with each input. */
for( j = 0; j < nin; j++ ) {
/* Attempt to split off the current input. */
outs = astMapSplit( map, 1, &j, &smap );
/* If successfull, store the index of the corresponding input for each
output. */
if( outs && smap ) {
nouts = astGetNout( smap );
for( i = 0; i < nouts; i++ ) {
if( perm[ outs[ i ] ] == -1 ) {
perm[ outs[ i ] ] = j;
} else {
ret = 0;
}
}
}
/* Free resources. */
outs = astFree( outs );
if( smap ) smap = astAnnul( smap );
}
/* Check all outputs were assigned . */
for( i = 0; i < nout && ret; i++ ) {
if( perm[ i ] == -1 ) ret = 0;
}
/* If succesful, attempt to remove any duplicates from the "perm" array
(i.e. inputs that supply more than one output). First get a list of
the inputs that are currently unused (i.e. do not appear in "perm"). */
if( ret ) {
/* Check each input. */
for( j = 0; j < nin; j++ ) {
/* See how many outputs are fed by this input. */
nused = 0;
for( i = 0; i < nout; i++ ) {
if( perm[ i ] == j ) nused++;
}
/* If it used more than once, we need to remove all but one of the
occurrences. */
if( nused > 1 ) {
/* Choose the occurrence to retain. If the output with the same index as
the input is one of them, use it. Otherwise, use the first occurrence. */
if( perm[ j ] == j ) {
retain = j;
} else {
for( i = 0; i < nout; i++ ) {
if( perm[ i ] == j ) {
retain = i;
break;
}
}
}
/* Loop round all occurrences of this input again. */
for( i = 0; i < nout && ret; i++ ) {
if( perm[ i ] == j ) {
/* Replace all occurrences, except for the one being retained. */
if( i != retain ) {
/* Replace it with the next unused input. */
for( j2 = 0; j2 < nin; j2++ ) {
used = 0;
for( i2 = 0; i2 < nout; i2++ ) {
if( perm[ i2 ] == j2 ) {
used = 1;
break;
}
}
if( ! used ) {
perm[ i ] = j2;
break;
}
}
/* If there were no unused inputs, we cannot do it. */
if( used ) ret = 0;
}
}
}
}
}
}
}
/* Free resources. */
map = astAnnul( map );
/* Return the result. */
return ret;
}
static int Write( AstChannel *this_channel, AstObject *object, int *status ) {
/*
* Name:
* Write
* Purpose:
* Write an Object to a FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* int Write( AstChannel *this, AstObject *object, int *status )
* Class Membership:
* FitsChan member function (over-rides the astWrite method
* inherited from the Channel class).
* Description:
* This function writes an Object to a FitsChan.
* Parameters:
* this
* Pointer to the FitsChan.
* object
* Pointer to the Object which is to be written.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The number of Objects written to the FitsChan by this invocation of
* astWrite.
* Notes:
* - A value of zero will be returned if this function is invoked
* with the AST error status set, or if it should fail for any
* reason.
* - The Base Frame in the FrameSet is used as the pixel Frame, and
* the Current Frame is used to create the primary axis descriptions.
* Attempts are made to create secondary axis descriptions for any
* other Frames in the FrameSet (up to a total of 26).
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
AstFitsChan *this; /* Pointer to the FitsChan structure */
FitsStore *store; /* Intermediate storage for WCS information */
char banner[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ]; /* Buffer for begin/end banner */
const char *class; /* Pointer to string holding object class */
const char *method; /* Pointer to string holding calling method */
double *dim; /* Pointer to array of axis dimensions */
int card0; /* Index of original current card */
int comm; /* Value of Comm attribute */
int encoding; /* FITS encoding scheme to use */
int i; /* Axis index */
int naxis; /* No. of pixel axes */
int ret; /* Number of objects read */
/* Initialise. */
ret = 0;
/* Check the global error status. */
if ( !astOK ) return ret;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this_channel);
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_channel;
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* Store the calling method, and object class. */
method = "astWrite";
class = astGetClass( this );
/* The original current card is re-instated at the end if no object
is written. Save its index. */
card0 = astGetCard( this );
/* Indicate that all cards added to the FitsCHan by this call should be
marked as "new". */
mark_new = 1;
/* Get the encoding scheme used by the FitsChan. */
encoding = astGetEncoding( this );
/* First deal with cases where we are writing to a FitsChan in which AST
objects are encoded using native AST-specific keywords... */
if( encoding == NATIVE_ENCODING ){
/* Increment the nesting level which keeps track of recursive
invocations of this function. */
write_nest++;
/* Initialise the current indentation level for top-level objects. */
if ( !write_nest ) current_indent = 0;
/* Obtain the value of the Comm attribute. */
comm = astGetComment( this );
/* If this is the top-level invocation (i.e. we are about to write out
a new top-level Object), then prefix it with a blank FITS line and
an appropriate banner of FITS comments, unless comments have been
suppressed. */
if ( !write_nest && comm ) {
astSetFitsCom( this, " ", "", 0 );
MakeBanner(
"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++",
"", "", banner, status );
astSetFitsCom( this, "COMMENT", banner, 0 );
if( astIsAFrameSet( object ) ) {
MakeBanner( "WCS information in AST format", "", "", banner, status );
astSetFitsCom( this, "COMMENT", banner, 0 );
MakeBanner( "See http://www.starlink.ac.uk/ast/", "", "", banner, status );
astSetFitsCom( this, "COMMENT", banner, 0 );
}
MakeBanner( HEADER_TEXT, astGetClass( object ), " object", banner, status );
astSetFitsCom( this, "COMMENT", banner, 0 );
MakeBanner(
"................................................................",
"", "", banner, status );
astSetFitsCom( this, "COMMENT", banner, 0 );
}
/* Invoke the parent astWrite method to write out the Object data. */
(*parent_write)( this_channel, object, status );
/* Append a banner of FITS comments to the object data, as above, if
necessary. */
if ( !write_nest && comm ) {
MakeBanner(
"................................................................",
"", "", banner, status );
astSetFitsCom( this, "COMMENT", banner, 0 );
MakeBanner( FOOTER_TEXT, astGetClass( object ), " object", banner, status );
astSetFitsCom( this, "COMMENT", banner, 0 );
MakeBanner(
"----------------------------------------------------------------",
"", "", banner, status );
astSetFitsCom( this, "COMMENT", banner, 0 );
}
/* Return the nesting level to its previous value. */
write_nest--;
/* Indicate that an object has been written. */
ret = 1;
/* Now deal with cases where we are writing to a FitsChan in which AST
objects are encoded using any of the supported foreign encodings... */
} else {
/* Only proceed if the supplied object is a FrameSet. */
if( astIsAFrameSet( object ) ){
/* Note the number of pixel (i.e. Base Frame) axes, and allocate memory to
hold the image dimensions. */
naxis = astGetNin( (AstFrameSet *) object );
dim = (double *) astMalloc( sizeof(double)*naxis );
if( dim ){
/* Note the image dimensions, if known. If not, store AST__BAD values. */
for( i = 0; i < naxis; i++ ){
if( !astGetFitsF( this, FormatKey( "NAXIS", i + 1, -1, ' ', status ),
dim + i ) ) dim[ i ] = AST__BAD;
}
/* Extract the required information from the FrameSet into a standard
intermediary structure called a FitsStore. The indices of any
celestial axes are returned. */
store = FsetToStore( this, (AstFrameSet *) object, naxis, dim,
encoding, method, class, status );
/* If the FrameSet cannot be described in terms of any of the supported
FITS encodings, a null pointer will have been returned. */
if( store ){
/* Now put header cards describing the contents of the FitsStore into the
supplied FitsChan, using the requested encoding. Zero or one is
returned depending on whether the information could be encoded. */
ret = FitsFromStore( this, store, encoding, dim,
(AstFrameSet *) object, method, class, status );
/* Release the resources used by the FitsStore. */
store = FreeStore( store, status );
/* If the Object was written to the FitsChan, set the current card to
end-of-file. */
if( ret ) astSetCard( this, INT_MAX );
}
/* Free workspace holding image dimensions */
dim = (double *) astFree( (void *) dim );
}
}
}
/* If an error has occurred, return zero and remove any new cards added
to the FitsCHan by this call. */
if( !astOK ) ret = 0;
/* Clear the new flag associated with cards which have been added to the
FitsChan as a result of this function. If the object was not added
succesfully to the FitsChan, remove any cards which were added before
the error was discovered. */
FixNew( this, NEW1, !ret, method, class, status );
FixNew( this, NEW2, !ret, method, class, status );
/* Indicate that all cards added to the FitsChan from now on should not be
marked as "new". */
mark_new = 0;
/* If no object was written, re-instate the original current card. */
if( !ret ) astSetCard( this, card0 );
/* Return the answer. */
return ret;
}
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 "fitschan.h"
* void WriteBegin( AstChannel *this, const char *class,
* const char *comment )
* Class Membership:
* FitsChan 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 FitsChan, so as to begin the output of a new
* Object definition.
* Parameters:
* this
* Pointer to the FitsChan.
* 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.
* Notes:
* - The comment supplied may not actually be used, depending on
* the nature of the FitsChan supplied.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
AstFitsChan *this; /* Pointer to the FitsChan structure. */
char buff[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ];
/* Character buffer */
char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */
/* Check the global error status. */
if ( !astOK ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this_channel);
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_channel;
/* Increment the indentation level for comments. */
current_indent += INDENT_INC;
/* If we are not beginning a top-level Object definition, and helpful
information has not been suppressed, generate an indented comment
to mark the "Begin" item and write it to the FitsChan as a comment
card with a blank keyword. */
if ( write_nest && ( astGetFull( this ) >= 0 ) ) {
MakeIndentedComment( current_indent, '+', "Beginning of ", class, buff, status );
astSetFitsCom( this, " ", buff, 0 );
}
/* Create a unique FITS keyword for this "Begin" item, basing it on
"BEGAST". */
CreateKeyword( this, "BEGAST", keyword, status );
/* Generate a pre-quoted version of the class name. */
PreQuote( class, buff, status );
/* Write the "Begin" item to the FitsChan as a keyword and string
value. */
astSetFitsS( this, keyword, buff,
astGetComment( this ) ? comment : NULL, 0 );
/* Clear the count of items written. */
items_written = 0;
}
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 "fitschan.h"
* void WriteDouble( AstChannel *this, const char *name,
* int set, int helpful,
* double value, const char *comment )
* Class Membership:
* FitsChan 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 FitsChan. 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 FitsChan.
* 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
* FitsChan'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 FitsChan supplied and the setting of its
* Comm attribute.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
AstFitsChan *this; /* Pointer to the FitsChan structure. */
char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */
/* Check the global error status. */
if ( !astOK ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this_channel);
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_channel;
/* Use the "set" and "helpful" flags, along with the FitsChan's
attributes to decide whether this value should actually be
written. */
if ( Use( this, set, helpful, status ) ) {
/* Create a unique FITS keyword from the name supplied. */
CreateKeyword( this, name, keyword, status );
/* Write the value to the FitsChan as a keyword and value */
astSetFitsF( this, keyword, value,
astGetComment( this ) ? comment : NULL, 0 );
/* If the value is not "set", replace the card just written by a COMMENT
card containing the text of the card as the comment. */
if( !set ) MakeIntoComment( this, "astWrite", astGetClass( this ), status );
/* Increment the count of items written. */
items_written++;
}
}
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 "fitschan.h"
* void WriteEnd( AstChannel *this, const char *class )
* Class Membership:
* FitsChan 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 FitsChan. This item delimits the end of an
* Object definition.
* Parameters:
* this
* Pointer to the FitsChan.
* class
* Pointer to a constant null-terminated string containing the
* class name of the Object whose definition is being terminated
* by the "End" item.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
AstFitsChan *this; /* Pointer to the FitsChan structure. */
char buff[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ];
/* Character buffer */
char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */
/* Check the global error status. */
if ( !astOK ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this_channel);
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_channel;
/* Create a unique FITS keyword for this "End" item, basing it on
"ENDAST". */
CreateKeyword( this, "ENDAST", keyword, status );
/* Generate a pre-quoted version of the class name. */
PreQuote( class, buff, status );
/* Write the "End" item to the FitsChan as a keyword and string
value. */
astSetFitsS( this, keyword, buff,
astGetComment( this ) ? "End of object definition" : NULL,
0 );
/* If we are not ending a top-level Object definition, and helpful
information has not been suppressed, generate an indented comment
to mark the "End" item and write it to the FitsChan as a comment
card with a blank keyword. */
if ( write_nest && ( astGetFull( this ) >= 0 ) ) {
MakeIndentedComment( current_indent, '-', "End of ", class, buff, status );
astSetFitsCom( this, " ", buff, 0 );
}
/* Decrement the indentation level for comments. */
current_indent -= INDENT_INC;
}
static void WriteFits( AstFitsChan *this, int *status ){
/*
*++
* Name:
c astWriteFits
f AST_WRITEFITS
* Purpose:
* Write out all cards in a FitsChan to the sink function.
* Type:
* Public virtual function.
* Synopsis:
c #include "fitschan.h"
c void astWriteFits( AstFitsChan *this )
f CALL AST_WRITEFITS( THIS, STATUS )
* Class Membership:
* FitsChan method.
* Description:
c This function
f This routine
* writes out all cards currently in the FitsChan. If the SinkFile
* attribute is set, they will be written out to the specified sink file.
* Otherwise, they will be written out using the sink function specified
* when the FitsChan was created. All cards are then deleted from the
* FitsChan.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the FitsChan.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Notes:
* - If the SinkFile is unset, and no sink function is available, this
* method simply empties the FitsChan, and is then equivalent to
c astEmptyFits.
f AST_EMPTYFITS.
* - This method attempt to execute even if an error has occurred
* previously.
*--
*/
/* Ensure a FitsChan was supplied. */
if( this ) {
/* Ensure the source function has been called */
ReadFromSource( this, status );
/* We can usefully use the local destructor function to do the work,
since it only frees resources used within teh FitsChan, rather than
freeing the FitsChan itself. */
Delete( (AstObject *) this, status );
}
}
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 int value to a data sink.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void WriteInt( AstChannel *this, const char *name,
* int set, int helpful,
* int value, const char *comment )
* Class Membership:
* FitsChan member function (over-rides the protected
* astWriteInt method inherited from the Channel class).
* Description:
* This function writes a named int value, representing the
* value of a class instance variable, to the data sink associated
* with a FitsChan. 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 FitsChan.
* 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
* FitsChan'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 FitsChan supplied and the setting of its
* Comm attribute.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
AstFitsChan *this; /* Pointer to the FitsChan structure. */
char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */
/* Check the global error status. */
if ( !astOK ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this_channel);
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_channel;
/* Use the "set" and "helpful" flags, along with the FitsChan's
attributes to decide whether this value should actually be
written. */
if ( Use( this, set, helpful, status ) ) {
/* Create a unique FITS keyword from the name supplied. */
CreateKeyword( this, name, keyword, status );
/* Write the value to the FitsChan as a keyword and value */
astSetFitsI( this, keyword, value,
astGetComment( this ) ? comment : NULL, 0 );
/* If the value is not "set", replace the card just written by a COMMENT
card containing the text of the card as the comment. */
if( !set ) MakeIntoComment( this, "astWrite", astGetClass( this ), status );
/* Increment the count of items written. */
items_written++;
}
}
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 "fitschan.h"
* void WriteIsA( AstChannel *this, const char *class,
* const char *comment )
* Class Membership:
* FitsChan 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 FitsChan. 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 FitsChan.
* 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.
* Notes:
* - The comment supplied may not actually be used, depending on
* the nature of the FitsChan supplied.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
AstFitsChan *this; /* Pointer to the FitsChan structure. */
char buff[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN + 1 ];
/* Character buffer */
char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */
/* Check the global error status. */
if ( !astOK ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this_channel);
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_channel;
/* 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 ( items_written || astGetFull( this ) > 0 ) {
/* Create a unique FITS keyword for this "IsA" item, basing it on
"ISA". */
CreateKeyword( this, "ISA", keyword, status );
/* Generate a pre-quoted version of the class name. */
PreQuote( class, buff, status );
/* Write the "IsA" item to the FitsChan as a keyword and string
value. */
astSetFitsS( this, keyword, buff,
astGetComment( this ) ? comment : NULL, 0 );
/* If helpful information has not been suppressed, generate an
indented comment to mark the "IsA" item and write it to the
FitsChan as a comment card with a blank keyword. */
if ( astGetFull( this ) >= 0 ) {
MakeIndentedComment( current_indent, '.', "Class boundary", "",
buff, status );
astSetFitsCom( this, " ", buff, 0 );
}
}
/* Clear the count of items written. */
items_written = 0;
}
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 value to a data sink.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void WriteObject( AstChannel *this, const char *name,
* int set, int helpful,
* AstObject *value, const char *comment )
* Class Membership:
* FitsChan member function (over-rides the protected
* astWriteObject method inherited from the Channel class).
* Description:
* This function writes a named Object value, representing the
* value of a class instance variable, to the data sink associated
* with a FitsChan. 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 FitsChan.
* 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
* FitsChan'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 FitsChan supplied and the setting of its
* Comm attribute.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
AstFitsChan *this; /* Pointer to the FitsChan structure. */
char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */
/* Check the global error status. */
if ( !astOK ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this_channel);
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_channel;
/* Use the "set" and "helpful" flags, along with the FitsChan's
attributes to decide whether this value should actually be
written. */
if ( Use( this, set, helpful, status ) ) {
/* Create a unique FITS keyword from the name supplied. */
CreateKeyword( this, name, keyword, status );
/* Write the value to the FitsChan as a keyword and a blank string value,
not pre-quoted (this "null" value indicates that an Object description
follows). */
astSetFitsS( this, keyword, "",
astGetComment( this ) ? comment : NULL, 0 );
/* If the value is "set", write out the Object description. */
if ( set ) {
astWrite( this, value );
/* If the value is not set, replace the card just written to the FitsChan
by COMENT card containing the keyword and blank string value (do not
write out the Object description). */
} else {
MakeIntoComment( this, "astWrite", astGetClass( this ), status );
}
/* Increment the count of items written. */
items_written++;
}
}
static void WriteToSink( AstFitsChan *this, int *status ){
/*
* Name:
* WriteToSink
* Purpose:
* Write the contents of the FitsChan out to the sink file or function.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* void WriteToSink( AstFitsChan *this, int *status )
* Class Membership:
* FitsChan member function.
* Description:
* If the SinkFile attribute is set, each card in the FitsChan is
* written out to the sink file. Otherwise, the cards are passed in
* turn to the sink function specified when the FitsChan was created.
* If no sink function was provided, the cards are not written out.
* Cards marked as having been read into an AST object are not written
* out.
* Parameters:
* this
* Pointer to the FitsChan.
* status
* Pointer to the inherited status variable.
* Notes:
* - The current card is left unchanged.
*/
/* Local Constants: */
#define ERRBUF_LEN 80
/* Local Variables: */
FILE *fd; /* File descriptor for sink file */
astDECLARE_GLOBALS /* Declare the thread specific global data */
char *errstat; /* Pointer for system error message */
char card[ AST__FITSCHAN_FITSCARDLEN + 1]; /* Buffer for header card */
char errbuf[ ERRBUF_LEN ]; /* Buffer for system error message */
const char *sink_file; /* Path to output sink file */
int icard; /* Current card index on entry */
int old_ignore_used; /* Original value of external variable ignore_used */
/* Check the global status. */
if( !astOK ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this);
/* If the SinkFile attribute is set, open the file. */
fd = NULL;
if( astTestSinkFile( this ) ) {
sink_file = astGetSinkFile( this );
fd = fopen( sink_file, "w" );
if( !fd ) {
if ( errno ) {
#if HAVE_STRERROR_R
strerror_r( errno, errbuf, ERRBUF_LEN );
errstat = errbuf;
#else
errstat = strerror( errno );
#endif
astError( AST__WRERR, "astDelete(%s): Failed to open output "
"SinkFile '%s' - %s.", status, astGetClass( this ),
sink_file, errstat );
} else {
astError( AST__WRERR, "astDelete(%s): Failed to open output "
"SinkFile '%s'.", status, astGetClass( this ),
sink_file );
}
}
}
/* Only proceed if a file was opened, or sink function and wrapper were supplied. */
if( fd || ( this->sink && this->sink_wrap ) ){
/* Store the current card index. */
icard = astGetCard( this );
/* Indicate that cards which have been read into an AST object should skipped
over by the functions which navigate the linked list of cards. */
old_ignore_used = ignore_used;
ignore_used = 1;
/* Ensure that the first card in the FitsChan will be the next one to be
read. */
astSetCard( this, 1 );
/* Loop round obtaining and writing out each card, until all cards have been
processed. */
while( !astFitsEof( this ) && astOK ){
/* Get the current card, and write it out through the sink function.
The call to astFindFits increments the current card. */
if( astFindFits( this, "%f", card, 1 ) ) {
/* If s sink file was opened, write the card out to it. */
if( fd ) {
fprintf( fd, "%s\n", card );
/* Otherwise, use the isnk function. The sink function is an externally
supplied function which may not be thread-safe, so lock a mutex first.
Also store the channel data pointer in a global variable so that it can
be accessed in the sink function using macro astChannelData. */
} else {
astStoreChannelData( this );
LOCK_MUTEX3;
( *this->sink_wrap )( *this->sink, card, status );
UNLOCK_MUTEX3;
}
}
}
/* Re-instate the original flag indicating if cards marked as having been
read should be skipped over. */
ignore_used = old_ignore_used;
/* Set the current card index back to what it was on entry. */
astSetCard( this, icard );
}
/* Close the sink file. */
if( fd ) fclose( fd );
}
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 "fitschan.h"
* void WriteString( AstChannel *this, const char *name,
* int set, int helpful,
* const char *value, const char *comment )
* Class Membership:
* FitsChan 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 FitsChan. 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 FitsChan.
* 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
* FitsChan'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 FitsChan supplied and the setting of its
* Comm attribute.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
AstFitsChan *this; /* Pointer to the FitsChan structure. */
char *c; /* Pointer to next buffer character */
char buff1[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ]; /* Buffer for a single substring */
char buff2[ AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 3 ]; /* Buffer for pre-quoted string */
char cc; /* Next character */
char keyword[ FITSNAMLEN + 1 ]; /* Buffer for FITS keyword */
const char *start; /* Pointer to start of substring */
int first; /* Is this the first sub-string? */
int nc; /* No. of available columns remaining */
/* Check the global error status. */
if ( !astOK ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this_channel);
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_channel;
/* Use the "set" and "helpful" flags, along with the FitsChan's
attributes to decide whether this value should actually be
written. */
if ( Use( this, set, helpful, status ) ) {
/* Create a unique FITS keyword from the name supplied. */
CreateKeyword( this, name, keyword, status );
/* Store a pointer to the start of the next sub-string (i.e. the
beggining of the string), and then loop round until the end of the
string is reached. */
start = value;
first = 1;
while( *start && astOK ){
/* Store the number of characters available in the 80 column header card
for the next substring, leaving room for the "= " string at the start,
and the delimiting quotes. Also reserve 2 characters to allow for the
possibility of double quotes being needed to protect trailing white space
(see function PreQuote). */
nc = AST__FITSCHAN_FITSCARDLEN - FITSNAMLEN - 6;
/* If this is the first sub-string reserve room for any comment. */
if( first ){
if( comment && comment[0] ) nc -= ChrLen( comment, status ) + 3;
/* If the first card will be turned into a comment card, we need to leave room
for the keyword name and equals sign, etc, within the 80 columns. */
if( !set ) nc -= FITSNAMLEN + 5;
}
/* We need to check the sub-string for single quotes since these will
take up 2 characters each instead of 1 when encoded since single quotes
within a string are doubled. Search through from the starting
character, copying the sub-string into a buffer, and reducing the number
of available characters remaining in the card for each character. */
c = buff1;
while( *start && nc > 0 ){
cc = *(start++);
*(c++) = cc;
if( cc == '\'' ) {
nc -= 2;
} else {
nc -= 1;
}
}
/* If the last character in the substring was a single quote, there may
not have been room for the extra quote which is added when the
sub-string is encoded. In this case we need to back up a character in
order to remove the single quote frin this substring and move it into
the next sub-string. */
if( nc < 0 ){
start--;
c--;
}
/* If the supplied value has not been exhausted, append an ampersand to
the string. In this case we need to move the last character in the
substring into the next substring to make room for the ampersand. */
if( *start ) {
start--;
c--;
*(c++) = '&';
}
/* Terminate the buffer. */
*c = 0;
/* The FITS standard considers trailing white space is be insignificant,
and so we need to guard against external applications throwing away
significant trailing white space. This is done by encosing the string,
including trailing white space, in double quotes. */
PreQuote( buff1, buff2, status );
/* On the first pass through this loop, write the value to the FitsChan as
a keyword and value */
if( first ){
astSetFitsS( this, keyword, buff2,
astGetComment( this ) ? comment : NULL, 0 );
/* If the value is not "set", replace the card just written by a COMMENT
card containing the text of the card as the comment. */
if( !set ) MakeIntoComment( this, "astWrite", astGetClass( this ), status );
/* On subsequent passes through the loop, store the string using a CONTINUE
keyword, with type AST__CONTINUE (this type is like AST__STRING but is
formatted without an equals sign). */
} else {
astSetFitsCN( this, "CONTINUE", buff2, NULL, 0 );
}
first = 0;
}
/* Increment the count of items written. */
items_written++;
}
}
static AstMapping *ZPXMapping( AstFitsChan *this, FitsStore *store, char s,
int naxes, int zpxaxes[2], const char *method,
const char *class, int *status ){
/*
* Name:
* ZPXMapping
* Purpose:
* Create a Mapping descriping "-ZPX" (IRAF) distortion.
* Type:
* Private function.
* Synopsis:
* AstMapping *ZPXMapping( AstFitsChan *this, FitsStore *store, char s,
* int naxes, int zpxaxes[2], const char *method,
* const char *class, int *status )
* Class Membership:
* FitsChan
* Description:
* This function uses the values in the supplied FitsStore to create a
* Mapping which implements the "-ZPX" distortion code, produced by
* the IRAF project. See:
*
* http://iraf.noao.edu/projects/ccdmosaic/zpx.html
*
* Note, the Mapping created by this function implements the "lngcor"
* and "latcor" corrections described in the WAT... keywords. The
* basic ZPN projection code is handled in the normal way, as any
* other projection is handled.
* Parameters:
* store
* A structure containing information about the requested axis
* descriptions derived from a FITS header.
* s
* A character identifying the co-ordinate version to use. A space
* means use primary axis descriptions. Otherwise, it must be an
* upper-case alphabetical characters ('A' to 'Z').
* naxes
* The number of intermediate world coordinate axes (WCSAXES).
* zpxaxes
* The zero-based indices of the two IWC axes that use the ZPX projection.
* method
* A pointer to a string holding the name of the calling method.
* This is used only in the construction of error messages.
* class
* A pointer to a string holding the class of the object being
* read. This is used only in the construction of error messages.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the Mapping.
*/
/* Local Variables: */
AstMapping *ret;
char *watstr;
double *cvals[ 2 ];
int *mvals[ 2 ];
int ncoeff[ 2 ];
int i;
int icoeff;
int ok;
/* Initialise the pointer to the returned Mapping. */
ret = NULL;
/* Check the global status. */
if ( !astOK ) return ret;
/* Check both axes */
for( i = 0; i < 2; i++ ){
mvals[ i ] = NULL;
cvals[ i ] = NULL;
ncoeff[ i ] = 0;
/* Concatenate all the IRAF "WAT" keywords together for this axis. These
keywords are marked as having been used, so that they are not written
out when the FitsChan is deleted. */
watstr = ConcatWAT( this, zpxaxes[ i ], method, class, status );
/* Extract the polynomial coefficients from the concatenated WAT string.
These are returned in the form of a list of PVi_m values for a TPN
projection. */
ncoeff[ i ] = WATCoeffs( watstr, i, cvals + i, mvals + i, &ok, status );
/* If the current axis of the ZPX projection uses features not supported
by AST, do not do any more axes. */
if( !ok ) break;
/* Free the WAT string. */
watstr = astFree( watstr );
}
/* If we can handle the ZPX projection, store the polynomial coefficients in
a new inverted TPN WcsMap. This WcsMap is used as a correction to the ZPN
WcsMap to be created later, therefore set its FITSProj value to zero so
that it is not used as the FITS projection when written out via
astWrite. Also set TPNTan to zero to indicate that the TAN part of the
TPN projection should not be used (i.e. just use the polynomial part). */
if( ok && astOK ) {
if( ncoeff[ 0 ] || ncoeff[ 1 ] ) {
ret = (AstMapping *) astWcsMap( naxes, AST__TPN, zpxaxes[ 0 ] + 1,
zpxaxes[ 1 ] + 1, "Invert=1",
status );
astSetFITSProj( ret, 0 );
astSetTPNTan( ret, 0 );
for( i = 0; i < 2; i++ ){
for( icoeff = 0; icoeff < ncoeff[ i ]; icoeff++ ) {
astSetPV( ret, zpxaxes[ i ], (mvals[ i ])[ icoeff ],
(cvals[ i ])[ icoeff ] );
}
}
} else {
ret = (AstMapping *) astUnitMap( naxes, " ", status );
}
/* If the TNX cannot be represented in FITS-WCS (within our restrictions), add
warning keywords to the FitsChan. */
} else {
Warn( this, "zpx", "This FITS header includes, or was "
"derived from, a ZPX projection which requires "
"unsupported IRAF-specific corrections. The WCS "
"information may therefore be incorrect.", method, class,
status );
}
/* Return the result. */
return ret;
}
/* 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). */
/* Card. */
/* ===== */
/*
*att++
* Name:
* Card
* Purpose:
* Index of current FITS card in a FitsChan.
* Type:
* Public attribute.
* Synopsis:
* Integer.
* Description:
* This attribute gives the index of the "current" FITS header card
* within a FitsChan, the first card having an index of 1. The
c choice of current card affects the behaviour of functions that
f choice of current card affects the behaviour of routines that
c access the contents of the FitsChan, such as astDelFits,
c astFindFits and astPutFits.
f access the contents of the FitsChan, such as AST_DELFITS,
f AST_FINDFITS and AST_PUTFITS.
*
* A value assigned to Card will position the FitsChan at any
* desired point, so that a particular card within it can be
* accessed. Alternatively, the value of Card may be enquired in
* order to determine the current position of a FitsChan.
*
* The default value of Card is 1. This means that clearing
c this attribute (using astClear) effectively "rewinds" the
f this attribute (using AST_CLEAR) effectively "rewinds" the
* FitsChan, so that the first card is accessed next. If Card is
* set to a value which exceeds the total number of cards in the
* FitsChan (as given by its Ncard attribute), it is regarded as
* pointing at the "end-of-file". In this case, the value returned
* in response to an enquiry is always one more than the number of
* cards in the FitsChan.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
/* Encoding. */
/* ========= */
/*
*att++
* Name:
* Encoding
* Purpose:
* System for encoding Objects as FITS headers.
* Type:
* Public attribute.
* Synopsis:
* String.
* Description:
* This attribute specifies the encoding system to use when AST
* Objects are stored as FITS header cards in a FitsChan. It
c affects the behaviour of the astWrite and astRead functions when
f affects the behaviour of the AST_WRITE and AST_READ routines when
* they are used to transfer any AST Object to or from an external
* representation consisting of FITS header cards (i.e. whenever a
* write or read operation is performed using a FitsChan as the I/O
* Channel).
*
* There are several ways (conventions) by which coordinate system
* information may be represented in the form of FITS headers and
* the Encoding attribute is used to specify which of these should
* be used. The encoding options available are outlined in the
* "Encodings Available" section below, and in more detail in the
* sections which follow.
*
* Encoding systems differ in the range of possible Objects
* (e.g. classes) they can represent, in the restrictions they
* place on these Objects (e.g. compatibility with some
* externally-defined coordinate system model) and in the number of
* Objects that can be stored together in any particular set of
* FITS header cards (e.g. multiple Objects, or only a single
* Object). The choice of encoding also affects the range of
* external applications which can potentially read and interpret
* the FITS header cards produced.
*
* The encoding options available are not necessarily mutually
* exclusive, and it may sometimes be possible to store multiple
* Objects (or the same Object several times) using different
* encodings within the same set of FITS header cards. This
* possibility increases the likelihood of other applications being
* able to read and interpret the information.
*
* By default, a FitsChan will attempt to determine which encoding
* system is already in use, and will set the default Encoding
* value accordingly (so that subsequent I/O operations adopt the
* same conventions). It does this by looking for certain critical
* FITS keywords which only occur in particular encodings. For
* details of how this works, see the "Choice of Default Encoding"
* section below. If you wish to ensure that a particular encoding
* system is used, independently of any FITS cards already present,
* you should set an explicit Encoding value yourself.
* Encodings Available:
* The Encoding attribute can take any of the following (case
* insensitive) string values to select the corresponding encoding
* system:
*
* - "DSS": Encodes coordinate system information in FITS header
* cards using the convention developed at the Space Telescope
* Science Institute (STScI) for the Digitised Sky Survey (DSS)
* astrometric plate calibrations. The main advantages of this
* encoding are that FITS images which use it are widely available
* and it is understood by a number of important and
* well-established astronomy applications. For further details,
* see the section "The DSS Encoding" below.
*
* - "FITS-WCS": Encodes coordinate system information in FITS
* header cards using the conventions described in the FITS
* world coordinate system (FITS-WCS) papers by E.W. Greisen,
* M. Calabretta, et al. The main advantages of this encoding are that
* it should be understood by any FITS-WCS compliant application and
* is likely to be adopted widely for FITS data in future. For further
* details, see the section "The FITS-WCS Encoding" below.
*
* - "FITS-PC": Encodes coordinate system information in FITS
* header cards using the conventions described in an earlier draft
* of the FITS world coordinate system papers by E.W. Greisen and
* M. Calabretta. This encoding uses a combination of CDELTi and
* PCiiijjj keywords to describe the scale and rotation of the pixel
* axes. This encoding is included to support existing data and
* software which uses these now superceded conventions. In general,
* the "FITS-WCS" encoding (which uses CDi_j or PCi_j keywords to
* describe the scale and rotation) should be used in preference to
* "FITS-PC".
*
* - "FITS-IRAF": Encodes coordinate system information in FITS
* header cards using the conventions described in the document
* "World Coordinate Systems Representations Within the FITS
* Format" by R.J. Hanisch and D.G. Wells, 1988. This encoding is
* currently employed by the IRAF data analysis facility, so its
* use will facilitate data exchange with IRAF. Its main advantages
* are that it is a stable convention which approximates to a
* subset of the propsed FITS-WCS encoding (above). This makes it
* suitable as an interim method for storing coordinate system
* information in FITS headers until the FITS-WCS encoding becomes
* stable. Since many datasets currently use the FITS-IRAF
* encoding, conversion of data from FITS-IRAF to the final form of
* FITS-WCS is likely to be well supported.
*
* - "FITS-AIPS": Encodes coordinate system information in FITS
* header cards using the conventions originally introduced by the
* AIPS data analysis facility. This is base on the use of CDELTi and
* CROTAi keuwords to desribe the scale and rotation of each axis.
* These conventions have been superceded but are still widely used.
*
* - "FITS-AIPS++": Encodes coordinate system information in FITS
* header cards using the conventions used by the AIPS++ project.
* This is an extension of FITS-AIPS which includes some of the
* features of FITS-IRAF and FITS-PC.
*
* - "FITS-CLASS": Encodes coordinate system information in FITS
* header cards using the conventions used by the CLASS project.
* CLASS is a software package for reducing single-dish radio and
* sub-mm spectroscopic data. See the section "CLASS FITS format" at
* http://www.iram.fr/IRAMFR/GILDAS/doc/html/class-html/.
*
* - "NATIVE": Encodes AST Objects in FITS header cards using a
* convention which is private to the AST library (but adheres to
* the general FITS standard) and which uses FITS keywords that
* will not clash with other encoding systems. The main advantages
* of this are that any class of AST Object may be encoded, and any
* (reasonable) number of Objects may be stored sequentially in the
* same FITS header. This makes FITS headers an almost loss-less
* communication path for passing AST Objects between applications
* (although all such applications must, of course, make use of the
* AST library to interpret the information). For further details,
* see the section "The NATIVE Encoding" below.
* Choice of Default Encoding:
* If the Encoding attribute of a FitsChan is not set, the default
* value it takes is determined by the presence of certain critical
* FITS keywords within the FitsChan. The sequence of decisions
* used to arrive at the default value is as follows:
*
* - If the FitsChan contains any keywords beginning with the
* string "BEGAST", then NATIVE encoding is used,
* - Otherwise, FITS-CLASS is used if the FitsChan contains a DELTAV
* keyword and a keyword of the form VELO-xxx, where xxx indicates one
* of the rest frames used by class (e.g. "VELO-LSR"), or "VLSR".
* - Otherwise, if the FitsChan contains a CTYPE keyword which
* represents a spectral axis using the conventions of the AIPS and
* AIPS++ projects (e.g. "FELO-LSR", etc), then one of FITS-AIPS or
* FITS-AIPS++ encoding is used. FITS-AIPS++ is used if any of the
* keywords CDi_j, PROJP, LONPOLE or LATPOLE are
* found in the FitsChan. Otherwise FITS-AIPS is used.
* - Otherwise, if the FitsChan contains a keyword of the form
* "PCiiijjj", where "i" and "j" are single digits, then
* FITS-PC encoding is used,
* - Otherwise, if the FitsChan contains a keyword of the form
* "CDiiijjj", where "i" and "j" are single digits, then
* FITS-IRAF encoding is used,
* - Otherwise, if the FitsChan contains a keyword of the form
* "CDi_j", and at least one of RADECSYS, PROJPi, or CjVALi
* where "i" and "j" are single digits, then FITS-IRAF encoding is
* used.
* - Otherwise, if the FitsChan contains any keywords of the form
* PROJPi, CjVALi or RADECSYS, where "i" and "j" are single digits,
* then FITS-PC encoding is used.
* - Otherwise, if the FitsChan contains a keyword of the form
* CROTAi, where "i" is a single digit, then FITS-AIPS encoding is
* used.
* - Otherwise, if the FitsChan contains a keyword of the form
* CRVALi, where "i" is a single digit, then FITS-WCS encoding is
* used.
* - Otherwise, if the FitsChan contains the "PLTRAH" keyword, then
* DSS encoding is used,
* - Otherwise, if none of these conditions is met (as would be the
* case when using an empty FitsChan), then NATIVE encoding is
* used.
*
* Except for the NATIVE and DSS encodings, all the above checks
* also require that the header contains at least one CTYPE, CRPIX and
* CRVAL keyword (otherwise the checking process continues to the next
* case).
*
* Setting an explicit value for the Encoding attribute always
* over-rides this default behaviour.
*
* Note that when writing information to a FitsChan, the choice of
* encoding will depend greatly on the type of application you
* expect to be reading the information in future. If you do not
* know this, there may sometimes be an advantage in writing the
* information several times, using a different encoding on each
* occasion.
* The DSS Encoding:
* The DSS encoding uses FITS header cards to store a multi-term
* polynomial which relates pixel positions on a digitised
* photographic plate to celestial coordinates (right ascension and
* declination). This encoding may only be used to store a single
* AST Object in any set of FITS header cards, and that Object must
* be a FrameSet which conforms to the STScI/DSS coordinate system
* model (this means the Mapping which relates its base and current
* Frames must include either a DssMap or a WcsMap with type
* AST__TAN or AST__TPN).
*
c When reading a DSS encoded Object (using astRead), the FitsChan
f When reading a DSS encoded Object (using AST_READ), the FitsChan
* concerned must initially be positioned at the first card (its
* Card attribute must equal 1) and the result of the read, if
* successful, will always be a pointer to a FrameSet. The base
* Frame of this FrameSet represents DSS pixel coordinates, and the
* current Frame represents DSS celestial coordinates. Such a read
* is always destructive and causes the FITS header cards required
* for the construction of the FrameSet to be removed from the
* FitsChan, which is then left positioned at the "end-of-file". A
* subsequent read using the same encoding will therefore not
* return another FrameSet, even if the FitsChan is rewound.
*
c When astWrite is used to store a FrameSet using DSS encoding,
f When AST_WRITE is used to store a FrameSet using DSS encoding,
* an attempt is first made to simplify the FrameSet to see if it
* conforms to the DSS model. Specifically, the current Frame must
* be a FK5 SkyFrame; the projection must be a tangent plane
* (gnomonic) projection with polynomial corrections conforming to
* DSS requirements, and north must be parallel to the second base
* Frame axis.
*
* If the simplification process succeeds, a description of the
* FrameSet is written to the FitsChan using appropriate DSS FITS
* header cards. The base Frame of the FrameSet is used to form the
* DSS pixel coordinate system and the current Frame gives the DSS
* celestial coordinate system. A successful write operation will
* over-write any existing DSS encoded data in the FitsChan, but
* will not affect other (non-DSS) header cards. If a destructive
* read of a DSS encoded Object has previously occurred, then an
* attempt will be made to store the FITS header cards back in
* their original locations.
*
* If an attempt to simplify a FrameSet to conform to the DSS model
* fails (or if the Object supplied is not a FrameSet), then no
c data will be written to the FitsChan and astWrite will return
f data will be written to the FitsChan and AST_WRITE will return
* zero. No error will result.
* The FITS-WCS Encoding:
* The FITS-WCS convention uses FITS header cards to describe the
* relationship between pixels in an image (not necessarily
* 2-dimensional) and one or more related "world coordinate systems".
* The FITS-WCS encoding may only be used to store a single AST Object
* in any set of FITS header cards, and that Object must be a FrameSet
* which conforms to the FITS-WCS model (the FrameSet may, however,
* contain multiple Frames which will be result in multiple FITS
* "alternate axis descriptions"). Details of the use made by this
* Encoding of the conventions described in the FITS-WCS papers are
* given in the appendix "FITS-WCS Coverage" of this document. A few
* main points are described below.
*
* The rotation and scaling of the intermediate world coordinate system
* can be specified using either "CDi_j" keywords, or "PCi_j" together
* with "CDELTi" keywords. When writing a FrameSet to a FitsChan, the
* the value of the CDMatrix attribute of the FitsChan determines
* which system is used.
*
* In addition, this encoding supports the "TAN with polynomial correction
* terms" projection which was included in a draft of the FITS-WCS paper,
* but was not present in the final version. A "TAN with polynomial
* correction terms" projection is represented using a WcsMap with type
* AST__TPN (rather than AST__TAN which is used to represent simple
* TAN projections). When reading a FITS header, a CTYPE keyword value
* including a "-TAN" code results in an AST__TPN projection if there are
* any projection parameters (given by the PVi_m keywords) associated with
* the latitude axis, or if there are projection parameters associated
* with the longitude axis for m greater than 4. When writing a
* FrameSet to a FITS header, an AST__TPN projection gives rise to a
* CTYPE value including the normal "-TAN" code, but the projection
* parameters are stored in keywords with names "QVi_m", instead of the
* usual "PVi_m". Since these QV parameters are not part of the
* FITS-WCS standard they will be ignored by other non-AST software,
* resulting in the WCS being interpreted as a simple TAN projection
* without any corrections. This should be seen as an interim solution
* until such time as an agreed method for describing projection
* distortions within FITS-WCS has been published.
*
* AST extends the range of celestial coordinate systems which may be
* described using this encoding by allowing the inclusion of
* "AZ--" and "EL--" as the coordinate specification within CTYPE
* values. These form a longitude/latitude pair of axes which describe
* azimuth and elevation. The geographic position of the observer
* should be supplied using the OBSGEO-X/Y/Z keywords described in FITS-WCS
* paper III. Currently, a simple model is used which includes diurnal
* aberration, but ignores atmospheric refraction, polar motion, etc.
* These may be added in a later release.
*
* If an AST SkyFrame that represents offset rather than absolute
* coordinates (see attribute SkyRefIs) is written to a FitsChan using
* FITS-WCS encoding, two alternate axis descriptions will be created.
* One will describe the offset coordinates, and will use "OFLN" and
* "OFLT" as the axis codes in the CTYPE keywords. The other will
* describe absolute coordinates as specified by the System attribute
* of the SkyFrame, using the usual CTYPE codes ("RA--"/"DEC-", etc).
* In addition, the absolute coordinates description will contain
* AST-specific keywords (SREF1/2, SREFP1/2 and SREFIS) that allow the
* header to be read back into AST in order to reconstruct the original
* SkyFrame.
*
c When reading a FITS-WCS encoded Object (using astRead), the FitsChan
f When reading a FITS-WCS encoded Object (using AST_READ), the FitsChan
* concerned must initially be positioned at the first card (its
* Card attribute must equal 1) and the result of the read, if
* successful, will always be a pointer to a FrameSet. The base
* Frame of this FrameSet represents FITS-WCS pixel coordinates,
* and the current Frame represents the physical coordinate system
* described by the FITS-WCS primary axis descriptions. If
* secondary axis descriptions are also present, then the FrameSet
* may contain additional (non-current) Frames which represent
* these. Such a read is always destructive and causes the FITS
* header cards required for the construction of the FrameSet to be
* removed from the FitsChan, which is then left positioned at the
* "end-of-file". A subsequent read using the same encoding will
* therefore not return another FrameSet, even if the FitsChan is
* rewound.
*
c When astWrite is used to store a FrameSet using FITS-WCS
f When AST_WRITE is used to store a FrameSet using FITS-WCS
* encoding, an attempt is first made to simplify the FrameSet to
* see if it conforms to the FITS-WCS model. If this simplification
* process succeeds (as it often should, as the model is reasonably
* flexible), a description of the FrameSet is written to the
* FitsChan using appropriate FITS header cards. The base Frame of
* the FrameSet is used to form the FITS-WCS pixel coordinate
* system and the current Frame gives the physical coordinate
* system to be described by the FITS-WCS primary axis
* descriptions. Any additional Frames in the FrameSet may be used
* to construct secondary axis descriptions, where appropriate.
*
* A successful write operation will over-write any existing
* FITS-WCS encoded data in the FitsChan, but will not affect other
* (non-FITS-WCS) header cards. If a destructive read of a FITS-WCS
* encoded Object has previously occurred, then an attempt will be
* made to store the FITS header cards back in their original
* locations. Otherwise, the new cards will be inserted following
* any other FITS-WCS related header cards present or, failing
* that, in front of the current card (as given by the Card
* attribute).
*
* If an attempt to simplify a FrameSet to conform to the FITS-WCS
* model fails (or if the Object supplied is not a FrameSet), then
c no data will be written to the FitsChan and astWrite will
f no data will be written to the FitsChan and AST_WRITE will
* return zero. No error will result.
* The FITS-IRAF Encoding:
* The FITS-IRAF encoding can, for most purposes, be considered as
* a subset of the FITS-WCS encoding (above), although it differs
* in the details of the FITS keywords used. It is used in exactly
* the same way and has the same restrictions, but with the
* addition of the following:
*
* - The only celestial coordinate systems that may be represented
* are equatorial, galactic and ecliptic,
* - Sky projections can be represented only if any associated
* projection parameters are set to their default values.
* - Secondary axis descriptions are not supported, so when writing
* a FrameSet to a FitsChan, only information from the base and
* current Frames will be stored.
*
* Note that this encoding is provided mainly as an interim measure to
* provide a more stable alternative to the FITS-WCS encoding until the
* FITS standard for encoding WCS information is finalised. The name
* "FITS-IRAF" indicates the general keyword conventions used and does
* not imply that this encoding will necessarily support all features of
* the WCS scheme used by IRAF software. Nevertheless, an attempt has
* been made to support a few such features where they are known to be
* used by important sources of data.
*
* When writing a FrameSet using the FITS-IRAF encoding, axis rotations
* are specified by a matrix of FITS keywords of the form "CDi_j", where
* "i" and "j" are single digits. The alternative form "CDiiijjj", which
* is also in use, is recognised when reading an Object, but is never
* written.
*
* In addition, the experimental IRAF "ZPX" and "TNX" sky projections will
* be accepted when reading, but will never be written (the corresponding
* FITS "ZPN" or "distorted TAN" projection being used instead). However,
* there are restrictions on the use of these experimental projections.
* For "ZPX", longitude and latitude correction surfaces (appearing as
* "lngcor" or "latcor" terms in the IRAF-specific "WAT" keywords) are
* not supported. For "TNX" projections, only cubic surfaces encoded as
* simple polynomials with "half cross-terms" are supported. If an
* un-usable "TNX" or "ZPX" projection is encountered while reading
* from a FitsChan, a simpler form of TAN or ZPN projection is used
* which ignores the unsupported features and may therefore be
* inaccurate. If this happens, a warning message is added to the
* contents of the FitsChan as a set of cards using the keyword "ASTWARN".
*
* You should not normally attempt to mix the foreign FITS encodings within
* the same FitsChan, since there is a risk that keyword clashes may occur.
* The FITS-PC Encoding:
* The FITS-PC encoding can, for most purposes, be considered as
* equivalent to the FITS-WCS encoding (above), although it differs
* in the details of the FITS keywords used. It is used in exactly
* the same way and has the same restrictions.
* The FITS-AIPS Encoding:
* The FITS-AIPS encoding can, for most purposes, be considered as
* equivalent to the FITS-WCS encoding (above), although it differs
* in the details of the FITS keywords used. It is used in exactly
* the same way and has the same restrictions, but with the
* addition of the following:
*
* - The only celestial coordinate systems that may be represented
* are equatorial, galactic and ecliptic,
* - Spectral axes can only be represented if they represent
* frequency, radio velocity or optical velocity, and are linearly
* sampled in frequency. In addition, the standard of rest
* must be LSRK, LSRD, barycentric or geocentric.
* - Sky projections can be represented only if any associated
* projection parameters are set to their default values.
* - The AIT, SFL and MER projections can only be written if the CRVAL
* keywords are zero for both longitude and latitude axes.
* - Secondary axis descriptions are not supported, so when writing
* a FrameSet to a FitsChan, only information from the base and
* current Frames will be stored.
* - If there are more than 2 axes in the base and current Frames, any
* rotation must be restricted to the celestial plane, and must involve
* no shear.
* The FITS-AIPS++ Encoding:
* The FITS-AIPS++ encoding is based on the FITS-AIPS encoding, but
* includes some features of the FITS-IRAF and FITS-PC encodings.
* Specifically, any celestial projections supported by FITS-PC may be
* used, including those which require parameterisation, and the axis
* rotation and scaling may be specified using CDi_j keywords. When
* writing a FITS header, rotation will be specified using CROTA/CDELT
* keywords if possible, otherwise CDi_j keywords will be used instead.
* The FITS-CLASS Encoding:
* The FITS-CLASS encoding uses the conventions of the CLASS project.
* These are described in the section "Developer Manual"/"CLASS FITS
* Format" contained in the CLASS documentation at:
*
* http://www.iram.fr/IRAMFR/GILDAS/doc/html/class-html/class.html.
*
* This encoding is similar to FITS-AIPS with the following restrictions:
*
* - When a SpecFrame is created by reading a FITS-CLASS header, the
* attributes describing the observer's position (ObsLat, ObsLon and
* ObsAlt) are left unset because the CLASS encoding does not specify
* these values. Conversions to or from the topocentric standard of rest
* will therefore be inaccurate (typically by up to about 0.5 km/s)
* unless suitable values are assigned to these attributes after the
* FrameSet has been created.
* - When writing a FrameSet to a FITS-CLASS header, the current Frame
* in the FrameSet must have at least 3 WCS axes, of which one must be
* a linear spectral axis. The spectral axis in the created header will
* always describe frequency. If the spectral axis in the supplied
* FrameSet refers to some other system (e.g. radio velocity, etc),
* then it will be converted to frequency.
* - There must be a pair of celestial axes - either (RA,Dec) or
* (GLON,GLAT). RA and Dec must be either FK4/B1950 or FK5/J2000.
* - A limited range of projection codes (TAN, ARC, STG, AIT, SFL, SIN)
* can be used. For AIT and SFL, the reference point must be at the
* origin of longitude and latitude. For SIN, the associated projection
* parameters must both be zero.
* - No rotation of the celestial axes is allowed, unless the spatial
* axes are degenerate (i.e. cover only a single pixel).
* - The frequency axis in the created header will always describe
* frequency in the source rest frame. If the supplied FrameSet uses
* some other standard of rest then suitable conversion will be applied.
* - The source velocity must be defined. In other words, the SpecFrame
* attributes SourceVel and SourceVRF must have been assigned values.
* - The frequency axis in a FITS-CLASS header does not represent
* absolute frequency, but instead represents offsets from the rest
* frequency in the standard of rest of the source.
*
* When writing a FrameSet out using FITS-CLASS encoding, the current
* Frame may be temporarily modified if this will allow the header
* to be produced. If this is done, the associated pixel->WCS Mapping
* will also be modified to take account of the changes to the Frame.
* The modifications performed include re-ordering axes (WCS axes, not
* pixel axes), changing spectral coordinate system and standard of
* rest, changing the celestial coordinate system and reference equinox,
* and changing axis units.
* The NATIVE Encoding:
* The NATIVE encoding may be used to store a description of any
* class of AST Object in the form of FITS header cards, and (for
* most practical purposes) any number of these Object descriptions
* may be stored within a single set of FITS cards. If multiple
* Object descriptions are stored, they are written and read
* sequentially. The NATIVE encoding makes use of unique FITS
* keywords which are designed not to clash with keywords that have
* already been used for other purposes (if a potential clash is
* detected, an alternative keyword is constructed to avoid the
* clash).
*
* When reading a NATIVE encoded object from a FitsChan (using
c astRead), FITS header cards are read, starting at the current
f AST_READ), FITS header cards are read, starting at the current
* card (as determined by the Card attribute), until the start of
* the next Object description is found. This description is then
* read and converted into an AST Object, for which a pointer is
* returned. Such a read is always destructive and causes all the
* FITS header cards involved in the Object description to be
* removed from the FitsChan, which is left positioned at the
* following card.
*
* The Object returned may be of any class, depending on the
* description that was read, and other AST routines may be used to
* validate it (for example, by examining its Class or ID attribute
c using astGetC). If further NATIVE encoded Object descriptions
f using AST_GETC). If further NATIVE encoded Object descriptions
c exist in the FitsChan, subsequent calls to astRead will return
f exist in the FitsChan, subsequent calls to AST_READ will return
* the Objects they describe in sequence (and destroy their
* descriptions) until no more remain between the current card and
* the "end-of-file".
*
c When astWrite is used to write an Object using NATIVE encoding,
f When AST_WRITE is used to write an Object using NATIVE encoding,
* a description of the Object is inserted immediately before the
* current card (as determined by the Card attribute). Multiple
* Object descriptions may be written in this way and are stored
* separately (and sequentially if the Card attribute is not
* modified between the writes). A write operation using the NATIVE
* encoding does not over-write previously written Object
* descriptions. Note, however, that subsequent behaviour is
* undefined if an Object description is written inside a
* previously-written description, so this should be avoided.
*
* When an Object is written to a FitsChan using NATIVE encoding,
c astWrite should (barring errors) always transfer data and
f AST_WRITE should (barring errors) always transfer data and
* return a value of 1.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
astMAKE_CLEAR(FitsChan,Encoding,encoding,UNKNOWN_ENCODING)
astMAKE_SET(FitsChan,Encoding,int,encoding,(
value == NATIVE_ENCODING ||
value == FITSPC_ENCODING ||
value == FITSWCS_ENCODING ||
value == FITSIRAF_ENCODING ||
value == FITSAIPS_ENCODING ||
value == FITSAIPSPP_ENCODING ||
value == FITSCLASS_ENCODING ||
value == DSS_ENCODING ? value :
(astError( AST__BADAT, "astSetEncoding: Unknown encoding system %d "
"supplied.", status, value ), UNKNOWN_ENCODING )))
astMAKE_TEST(FitsChan,Encoding,( this->encoding != UNKNOWN_ENCODING ))
/* DefB1950 */
/* ======== */
/*
*att++
* Name:
* DefB1950
* Purpose:
* Use FK4 B1950 as defaults?
* Type:
* Public attribute.
* Synopsis:
* Integer (boolean).
* Description:
* This attribute is a boolean value which specifies a default equinox
* and reference frame to use when reading a FrameSet from a FitsChan
* with a foreign (i.e. non-native) encoding. It is only used if the FITS
* header contains RA and DEC axes but contains no information about the
* reference frame or equinox. If this is the case, then values of FK4 and
* B1950 are assumed if the DefB1950 attribute has a non-zero value and
* ICRS is assumed if DefB1950 is zero. The default value for DefB1950
* depends on the value of the Encoding attribute: for FITS-WCS encoding
* the default is zero, and for all other encodings it is one.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
astMAKE_CLEAR(FitsChan,DefB1950,defb1950,-1)
astMAKE_GET(FitsChan,DefB1950,int,1,(this->defb1950 == -1 ? (astGetEncoding(this)== FITSWCS_ENCODING?0:1): this->defb1950))
astMAKE_SET(FitsChan,DefB1950,int,defb1950,( value ? 1 : 0 ))
astMAKE_TEST(FitsChan,DefB1950,( this->defb1950 != -1 ))
/* TabOK */
/* ===== */
/*
*att++
* Name:
* TabOK
* Purpose:
* Should the FITS-WCS -TAB algorithm be recognised?
* Type:
* Public attribute.
* Synopsis:
* Integer.
* Description:
* This attribute is an integer value which indicates if the "-TAB"
* algorithm, defined in FITS-WCS paper III, should be supported by
* the FitsChan. The default value is zero. A zero or negative value
* results in no support for -TAB axes (i.e. axes that have "-TAB"
* in their CTYPE keyword value). In this case, the
c astWrite
f AST_WRITE
* method will return zero if the write operation would required the
* use of the -TAB algorithm, and the
c astRead
f AST_READ
* method will return
c a NULL pointer
f AST__NULL
* if any axis in the supplied header uses the -TAB algorithm.
* If TabOK is set to a non-zero positive integer, these methods will
* recognise and convert axes described by the -TAB algorithm, as
* follows:
*
c The astWrite
f The AST_WRITE
* method will generate headers that use the -TAB algorithm (if
* possible) if no other known FITS-WCS algorithm can be used to
* describe the supplied FrameSet. This will result in a table of
* coordinate values and index vectors being stored in the FitsChan.
* After the write operation, the calling application should check to
* see if such a table has been stored in the FitsChan. If so, the
* table should be retrived from the FitsChan using the
c astGetTables
f AST_GETTABLES
* method, and the data (and headers) within it copied into a new
* FITS binary table extension. See
c astGetTables
f AST_GETTABLES
* for more information. The FitsChan uses a FitsTable object to store
* the table data and headers. This FitsTable will contain the required
* columns and headers as described by FITS-WCS paper III - the
* coordinates array will be in a column named "COORDS", and the index
* vector(s) will be in columns named "INDEX" (where is the index
* of the corresponding FITS WCS axis). Note, index vectors are only
* created if required. The EXTNAME value will be set to the value of the
* AST__TABEXTNAME constant (currently "WCS-TAB"). The EXTVER header
* will be set to the positive integer value assigned to the TabOK
* attribute. No value will be stored for the EXTLEVEL header, and should
* therefore be considered to default to 1.
*
c The astRead
f The AST_READ
* method will generate a FrameSet from headers that use the -TAB
* algorithm so long as the necessary FITS binary tables are made
* available. There are two ways to do this: firstly, if the application
* knows which FITS binary tables will be needed, then it can create a
* Fitstable describing each such table and store it in the FitsChan
* (using method
c astPutTables or astPutTable) before invoking the astRead method.
f AST_PUTTABLES or AST_PUTTABLE) before invoking the AST_READ method.
* Secondly, if the application does not know which FITS binary tables
* will be needed by
c astRead,
f AST_READ,
* then it can register a call-back function with the FitsChan using
* method
c astTableSource.
f AST_TABLESOURCE.
* This call-back function will be called from within
c astRead
f AST_READ
* if and when a -TAB header is encountered. When called, its arguments
* will give the name, version and level of the FITS extension containing
* a required table. The call-back function should read this table from
* an external FITS file, and create a corresponding FitsTable which
* it should then return to
c astRead. Note, currently astRead
f AST_READ. Note, currently AST_READ
* can only handle -TAB headers that describe 1-dimensional (i.e.
* separable) axes.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
astMAKE_CLEAR(FitsChan,TabOK,tabok,-INT_MAX)
astMAKE_GET(FitsChan,TabOK,int,0,(this->tabok == -INT_MAX ? 0 : this->tabok))
astMAKE_SET(FitsChan,TabOK,int,tabok,value)
astMAKE_TEST(FitsChan,TabOK,( this->tabok != -INT_MAX ))
/* CarLin */
/* ====== */
/*
*att++
* Name:
* CarLin
* Purpose:
* Ignore spherical rotations on CAR projections?
* Type:
* Public attribute.
* Synopsis:
* Integer (boolean).
* Description:
* This attribute is a boolean value which specifies how FITS "CAR"
* (plate carree, or "Cartesian") projections should be treated when
* reading a FrameSet from a foreign encoded FITS header. If zero (the
* default), it is assumed that the CAR projection conforms to the
* conventions described in the FITS world coordinate system (FITS-WCS)
* paper II "Representation of Celestial Coordinates in FITS" by
* M. Calabretta & E.W. Greisen. If CarLin is non-zero, then these
* conventions are ignored, and it is assumed that the mapping from pixel
* coordinates to celestial coordinates is a simple linear transformation
* (hence the attribute name "CarLin"). This is appropriate for some older
* FITS data which claims to have a "CAR" projection, but which in fact do
* not conform to the conventions of the FITS-WCS paper.
*
* The FITS-WCS paper specifies that headers which include a CAR projection
* represent a linear mapping from pixel coordinates to "native spherical
* coordinates", NOT celestial coordinates. An extra mapping is then
* required from native spherical to celestial. This mapping is a 3D
* rotation and so the overall Mapping from pixel to celestial coordinates
* is NOT linear. See the FITS-WCS papers for further details.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
astMAKE_CLEAR(FitsChan,CarLin,carlin,-1)
astMAKE_GET(FitsChan,CarLin,int,1,(this->carlin == -1 ? 0 : this->carlin))
astMAKE_SET(FitsChan,CarLin,int,carlin,( value ? 1 : 0 ))
astMAKE_TEST(FitsChan,CarLin,( this->carlin != -1 ))
/* PolyTan */
/* ======= */
/*
*att++
* Name:
* PolyTan
* Purpose:
* Use PVi_m keywords to define distorted TAN projection?
* Type:
* Public attribute.
* Synopsis:
* Integer.
* Description:
* This attribute is a boolean value which specifies how FITS "TAN"
* projections should be treated when reading a FrameSet from a foreign
* encoded FITS header. If zero, the projection is assumed to conform
* to the published FITS-WCS standard. If positive, the convention
* for a distorted TAN projection included in an early draft version
* of FITS-WCS paper II are assumed. In this convention the
* coefficients of a polynomial distortion to be applied to
* intermediate world coordinates are specified by the PVi_m keywords.
* This convention was removed from the paper before publication and so
* does not form part of the standard. Indeed, it is incompatible with
* the published standard because it re-defines the meaning of the
* first five PVi_m keywords on the longitude axis, which are reserved
* by the published standard for other purposes. However, headers that
* use this convention are still to be found, for instance the SCAMP
* utility (http://www.astromatic.net/software/scamp) creates them.
*
* The default value for the PolyTan attribute is -1. A negative
* values causes the used convention to depend on the contents
* of the FitsChan. If the FitsChan contains any PVi_m keywords for
* the latitude axis, or if it contains PVi_m keywords for the
* longitude axis with "m" greater than 4, then the distorted TAN
* convention is used. Otherwise, the standard convention is used.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
astMAKE_CLEAR(FitsChan,PolyTan,polytan,-INT_MAX)
astMAKE_SET(FitsChan,PolyTan,int,polytan,value)
astMAKE_TEST(FitsChan,PolyTan,( this->polytan != -INT_MAX ))
astMAKE_GET(FitsChan,PolyTan,int,-1,(this->polytan == -INT_MAX ? -1 : this->polytan))
/* Iwc */
/* === */
/*
*att++
* Name:
* Iwc
* Purpose:
* Include a Frame representing FITS-WCS intermediate world coordinates?
* Type:
* Public attribute.
* Synopsis:
* Integer (boolean).
* Description:
* This attribute is a boolean value which is used when a FrameSet is
* read from a FitsChan with a foreign FITS encoding (e.g. FITS-WCS) using
c astRead.
f AST_READ.
* If it has a non-zero value then the returned FrameSet will include
* Frames representing "intermediate world coordinates" (IWC). These
* Frames will have Domain name "IWC" for primary axis descriptions, and
* "IWCa" for secondary axis descriptions, where "a" is replaced by
* the single alternate axis description character, as used in the
* FITS-WCS header. The default value for "Iwc" is zero.
*
* FITS-WCS paper 1 defines IWC as a Cartesian coordinate system with one
* axis for each WCS axis, and is the coordinate system produced by the
* rotation matrix (represented by FITS keyword PCi_j, CDi_j, etc).
* For instance, for a 2-D FITS-WCS header describing projected
* celestial longitude and latitude, the intermediate world
* coordinates represent offsets in degrees from the reference point
* within the plane of projection.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
astMAKE_CLEAR(FitsChan,Iwc,iwc,-1)
astMAKE_GET(FitsChan,Iwc,int,1,(this->iwc == -1 ? 0 : this->iwc))
astMAKE_SET(FitsChan,Iwc,int,iwc,( value ? 1 : 0 ))
astMAKE_TEST(FitsChan,Iwc,( this->iwc != -1 ))
/*
*att++
* Name:
* CDMatrix
* Purpose:
* Use CDi_j keywords to represent pixel scaling, rotation, etc?
* Type:
* Public attribute.
* Synopsis:
* Integer (boolean).
* Description:
* This attribute is a boolean value which specifies how the linear
* transformation from pixel coordinates to intermediate world
* coordinates should be represented within a FitsChan when using
* FITS-WCS encoding. This transformation describes the scaling,
* rotation, shear, etc., of the pixel axes.
*
* If the attribute has a non-zero value then the transformation is
* represented by a set of CDi_j keywords representing a square matrix
* (where "i" is the index of an intermediate world coordinate axis
* and "j" is the index of a pixel axis). If the attribute has a zero
* value the transformation is represented by a set of PCi_j keywords
* (which also represent a square matrix) together with a corresponding
* set of CDELTi keywords representing the axis scalings. See FITS-WCS
* paper II "Representation of Celestial Coordinates in FITS" by
* M. Calabretta & E.W. Greisen, for a complete description of these two
* schemes.
*
* The default value of the CDMatrix attribute is determined by the
* contents of the FitsChan at the time the attribute is accessed. If
* the FitsChan contains any CDi_j keywords then the default value is
* non-zero. Otherwise it is zero. Note, reading a FrameSet from a
* FitsChan will in general consume any CDi_j keywords present in the
* FitsChan. Thus the default value for CDMatrix following a read will
* usually be zero, even if the FitsChan originally contained some
* CDi_j keywords. This behaviour is similar to that of the Encoding
* attribute, the default value for which is determined by the contents
* of the FitsChan at the time the attribute is accessed. If you wish
* to retain the original value of the CDMatrix attribute (that is,
* the value before reading the FrameSet) then you should enquire the
* default value before doing the read, and then set that value
* explicitly.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
astMAKE_CLEAR(FitsChan,CDMatrix,cdmatrix,-1)
astMAKE_SET(FitsChan,CDMatrix,int,cdmatrix,( value ? 1 : 0 ))
astMAKE_TEST(FitsChan,CDMatrix,( this->cdmatrix != -1 ))
/* Clean */
/* ===== */
/*
*att++
* Name:
* Clean
* Purpose:
* Remove cards used whilst reading even if an error occurs?
* Type:
* Public attribute.
* Synopsis:
* Integer (boolean).
* Description:
* This attribute indicates whether or not cards should be removed from
* the FitsChan if an error occurs within
c astRead.
f AST_READ.
* A succesful read on a FitsChan always results in the removal of
* the cards which were involved in the description of the returned
* Object. However, in the event of an error during the read (for instance
* if the cards in the FitsChan have illegal values, or if some required
* cards are missing) no cards will be removed from the FitsChan if
* the Clean attribute is zero (the default). If Clean is non-zero then
* any cards which were used in the aborted attempt to read an object
* will be removed.
*
* This provides a means of "cleaning" a FitsChan of WCS related cards
* which works even in the event of the cards not forming a legal WCS
* description.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
astMAKE_CLEAR(FitsChan,Clean,clean,-1)
astMAKE_SET(FitsChan,Clean,int,clean,( value ? 1 : 0 ))
astMAKE_TEST(FitsChan,Clean,( this->clean != -1 ))
/*
*att++
* Name:
* FitsAxisOrder
* Purpose:
* Frame title.
* Type:
* Public attribute.
* Synopsis:
* String.
* Description:
* This attribute specifies the order for the WCS axes in any new
* FITS-WCS headers created using the
c astWrite
f AST_WRITE
* method.
*
* The value of the FitsAxisOrder attribute can be either ""
* (the default value), "" or a space-separated list of axis
* symbols:
*
* "": causes the WCS axis order to be chosen automatically so that
* the i'th WCS axis in the new FITS header is the WCS axis which is
* more nearly parallel to the i'th pixel axis.
*
* "": causes the WCS axis order to be set so that the i'th WCS
* axis in the new FITS header is the i'th WCS axis in the current
* Frame of the FrameSet being written out to the header.
*
* "Sym1 Sym2...": the space-separated list is seached in turn for
* the Symbol attribute of each axis in the current Frame of the
* FrameSet. The order in which these Symbols occur within the
* space-separated list defines the order of the WCS axes in the
* new FITS header. An error is reported if Symbol for a current
* Frame axis is not present in the supplied list. However, no error
* is reported if the list contains extra words that do not correspond
* to the Symbol of any current Frame axis.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
astMAKE_CLEAR(FitsChan,FitsAxisOrder,fitsaxisorder,astFree( this->fitsaxisorder ))
astMAKE_GET(FitsChan,FitsAxisOrder,const char *,NULL,(this->fitsaxisorder ? this->fitsaxisorder : "" ))
astMAKE_SET(FitsChan,FitsAxisOrder,const char *,fitsaxisorder,astStore( this->fitsaxisorder, value, strlen( value ) + (size_t) 1 ))
astMAKE_TEST(FitsChan,FitsAxisOrder,( this->fitsaxisorder != NULL ))
/* FitsDigits. */
/* =========== */
/*
*att++
* Name:
* FitsDigits
* Purpose:
* Digits of precision for floating point FITS values.
* Type:
* Public attribute.
* Synopsis:
* Integer.
* Description:
* This attribute gives the number of significant decimal digits to
* use when formatting floating point values for inclusion in the
* FITS header cards within a FitsChan.
*
* By default, a positive value is used which results in no loss of
c information, assuming that the value's precision is double.
f information, assuming that the value is double precision.
* Usually, this causes no problems.
*
* However, to adhere strictly to the recommendations of the FITS
* standard, the width of the formatted value (including sign,
* decimal point and exponent) ought not to be more than 20
* characters. If you are concerned about this, you should set
* FitsDigits to a negative value, such as -15. In this case, the
* absolute value (+15) indicates the maximum number of significant
* digits to use, but the actual number used may be fewer than this
* to ensure that the FITS recommendations are satisfied. When
* using this approach, the resulting number of significant digits
* may depend on the value being formatted and on the presence of
* any sign, decimal point or exponent.
*
* The value of this attribute is effective when FITS header cards
* are output, either using
c astFindFits or by the action of the FitsChan's sink function
f AST_FINDFITS or by the action of the FitsChan's sink routine
* when it is finally deleted.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
astMAKE_CLEAR(FitsChan,FitsDigits,fitsdigits,DBL_DIG)
astMAKE_GET(FitsChan,FitsDigits,int,DBL_DIG,this->fitsdigits)
astMAKE_SET(FitsChan,FitsDigits,int,fitsdigits,value)
astMAKE_TEST(FitsChan,FitsDigits,( this->fitsdigits != DBL_DIG ))
/* CardComm */
/* ======== */
/*
*att++
* Name:
* CardComm
* Purpose:
* The comment for the current card in a FitsChan.
* Type:
* Public attribute.
* Synopsis:
* String, read-only.
* Description:
* This attribute gives the comment for the current card of the
* FitsChan. A zero-length string is returned if the card has no comment.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
/* CardName */
/* ======== */
/*
*att++
* Name:
* CardName
* Purpose:
* The keyword name of the current card in a FitsChan.
* Type:
* Public attribute.
* Synopsis:
* String, read-only.
* Description:
* This attribute gives the name of the keyword for the
* current card of the FitsChan.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
/* CardType */
/* ======== */
/*
*att++
* Name:
* CardType
* Purpose:
* The data type of the current card in a FitsChan.
* Type:
* Public attribute.
* Synopsis:
* Integer, read-only.
* Description:
* This attribute gives the data type of the keyword value for the
* current card of the FitsChan. It will be one of the following
* integer constants: AST__NOTYPE, AST__COMMENT, AST__INT, AST__FLOAT,
* AST__STRING, AST__COMPLEXF, AST__COMPLEXI, AST__LOGICAL,
* AST__CONTINUE, AST__UNDEF.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
/* Ncard */
/* ===== */
/*
*att++
* Name:
* Ncard
* Purpose:
* Number of FITS header cards in a FitsChan.
* Type:
* Public attribute.
* Synopsis:
* Integer, read-only.
* Description:
* This attribute gives the total number of FITS header cards
* stored in a FitsChan. It is updated as cards are added or
* deleted.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
/* Nkey */
/* ==== */
/*
*att++
* Name:
* Nkey
* Purpose:
* Number of unique FITS keywords in a FitsChan.
* Type:
* Public attribute.
* Synopsis:
* Integer, read-only.
* Description:
* This attribute gives the total number of unique FITS keywords
* stored in a FitsChan. It is updated as cards are added or
* deleted. If no keyword occurrs more than once in the FitsChan, the
* Ncard and Nkey attributes will be equal. If any keyword occurrs
* more than once, the Nkey attribute value will be smaller than
* the Ncard attribute value.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
/* Warnings. */
/* ======== */
/*
*att++
* Name:
* Warnings
* Purpose:
* Controls the issuing of warnings about various conditions.
* Type:
* Public attribute.
* Synopsis:
* String
* Description:
* This attribute controls the issuing of warnings about selected
* conditions when an Object or keyword is read from or written to a
* FitsChan. The value supplied for the Warnings attribute should
* consist of a space separated list of condition names (see the
* AllWarnings attribute for a list of the currently defined names).
* Each name indicates a condition which should be reported. The default
* value for Warnings is the string "BadKeyName BadKeyValue Tnx Zpx
* BadCel BadMat BadPV BadCTYPE".
*
* The text of any warning will be stored within the FitsChan in the
* form of one or more new header cards with keyword ASTWARN. If
* required, applications can check the FitsChan for ASTWARN cards
c (using astFindFits) after the call to astRead or astWrite has been
f (using AST_FINDFITS) after the call to AST_READ or AST_WRITE has been
* performed, and report the text of any such cards to the user. ASTWARN
* cards will be propagated to any output header unless they are
c deleted from the FitsChan using astDelFits.
f deleted from the FitsChan using astDelFits.
* Notes:
* This attribute only controls the warnings that are to be stored as
* a set of header cards in the FitsChan as described above. It has no
* effect on the storage of warnings in the parent Channel structure.
* All warnings are stored in the parent Channel structure, from where
* they can be retrieved using the
c astWarnings
f AST_WARNINGS
* function.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
/* Clear the Warnings value by freeing the allocated memory and assigning
a NULL pointer. */
astMAKE_CLEAR(FitsChan,Warnings,warnings,astFree( this->warnings ))
/* If the Warnings value is not set, supply a default in the form of a
pointer to the constant string "BadKeyName BadKeyValue Tnx Zpx BadCel BadMat BadCTYPE". */
astMAKE_GET(FitsChan,Warnings,const char *,NULL,( this->warnings ? this->warnings :
"BadKeyName BadKeyValue Tnx Zpx BadPV BadCel BadMat BadCTYPE" ))
/* Set a Warnings value by freeing any previously allocated memory, allocating
new memory, storing the string and saving the pointer to the copy.
First check that the list does not contain any unknown conditions. If
it does, an error is reported by GoodWarns and the current attribute value
is retained. */
astMAKE_SET(FitsChan,Warnings,const char *,warnings,( GoodWarns( value, status ) ?
astStore( this->warnings, value, strlen( value ) + (size_t) 1 ) :
this->warnings))
/* The Warnings value is set if the pointer to it is not NULL. */
astMAKE_TEST(FitsChan,Warnings,( this->warnings != NULL ))
/* AllWarnings. */
/* ============ */
/*
*att++
* Name:
* AllWarnings
* Purpose:
* A list of all currently available condition names.
* Type:
* Public attribute.
* Synopsis:
* String, read-only
* Description:
* This read-only attribute is a space separated list of all the conditions
* names recognized by the Warnings attribute. The names are listed
* below.
* Conditions:
* The following conditions are currently recognised (all are
* case-insensitive):
*
* - "BadCel": This condition arises when reading a FrameSet from a
* non-Native encoded FitsChan if an unknown celestial co-ordinate
* system is specified by the CTYPE keywords.
*
* - "BadCTYPE": This condition arises when reading a FrameSet from a
* non-Native encoded FitsChan if an illegal algorithm code is specified
* by a CTYPE keyword, and the illegal code can be converted to an
* equivalent legal code.
*
* - "BadKeyName": This condition arises if a FITS keyword name is
* encountered that contains an illegal character (i.e. one not allowed
* by the FITS standard).
*
* - "BadKeyValue": This condition arises if the value of a FITS keyword
* cannot be determined from the content of the header card.
*
* - "BadLat": This condition arises when reading a FrameSet from a
* non-Native encoded FitsChan if the latitude of the reference point
* has an absolute value greater than 90 degrees. The actual absolute
* value used is set to exactly 90 degrees in these cases.
*
* - "BadMat": This condition arises if the matrix describing the
* transformation from pixel offsets to intermediate world coordinates
* cannot be inverted. This matrix describes the scaling, rotation, shear,
* etc., applied to the pixel axes, and is specified by keywords such as
* PCi_j, CDi_j, CROTA, etc. For example, the matrix will not be invertable
* if any rows or columns consist entirely of zeros. The FITS-WCS Paper I
* "Representation of World Coordinates in FITS" by Greisen & Calabretta
* requires that this matrix be invertable. Many operations (such as
* grid plotting) will not be possible if the matrix cannot be inverted.
*
* - "BadPV": This condition arises when reading a FrameSet from a
* non-Native encoded FitsChan. It is issued if a PVi_m header is found
* that refers to a projection parameter that is not used by the
* projection type specified by CTYPE, or the PV values are otherwise
* inappropriate for the projection type.
*
* - "BadVal": This condition arises when reading a FrameSet from a
* non-Native encoded FitsChan if it is not possible to convert the
* value of a FITS keywords to the expected type. For instance, this
* can occur if the FITS header contains a string value for a keyword
* which should have a floating point value, or if the keyword has no
* value at all (i.e. is a comment card).
*
* - "Distortion": This condition arises when reading a FrameSet from a
* non-Native encoded FitsChan if any of the CTYPE keywords specify an
* unsupported distortion code using the "4-3-3" format specified in
* FITS-WCS paper IV. Such distortion codes are ignored.
*
* - "NoCTYPE": This condition arises if a default CTYPE value is used
c within astRead, due to no value being present in the supplied FitsChan.
f within AST_READ, due to no value being present in the supplied FitsChan.
* This condition is only tested for when using non-Native encodings.
*
* - "NoEquinox": This condition arises if a default equinox value is used
c within astRead, due to no value being present in the supplied FitsChan.
f within AST_READ, due to no value being present in the supplied FitsChan.
* This condition is only tested for when using non-Native encodings.
*
* - "NoRadesys": This condition arises if a default reference frame is
c used for an equatorial co-ordinate system within astRead, due to no
f used for an equatorial co-ordinate system within AST_READ, due to no
* value being present in the supplied FitsChan. This condition is only
* tested for when using non-Native encodings.
*
* - "NoLonpole": This condition arises if a default value is used for
c the LONPOLE keyword within astRead, due to no value being present
f the LONPOLE keyword within AST_READ, due to no value being present
* in the supplied FitsChan. This condition is only tested for when
* using non-Native encodings.
*
* - "NoLatpole": This condition arises if a default value is used for
c the LATPOLE keyword within astRead, due to no value being present
f the LATPOLE keyword within AST_READ, due to no value being present
* in the supplied FitsChan. This condition is only tested for when
* using non-Native encodings.
*
* - "NoMjd-obs": This condition arises if a default value is used for
c the date of observation within astRead, due to no value being present
f the date of observation within AST_READ, due to no value being present
* in the supplied FitsChan. This condition is only tested for when using
* non-Native encodings.
*
* - "Tnx": This condition arises if a FrameSet is read from a FITS
* header containing an IRAF "TNX" projection which includes terms
* not supproted by AST. Such terms are ignored and so the resulting
* FrameSet may be inaccurate.
*
* - "Zpx": This condition arises if a FrameSet is read from a FITS
* header containing an IRAF "ZPX" projection which includes "lngcor"
* or "latcor" correction terms. These terms are not supported by AST
* and are ignored. The resulting FrameSet may therefore be inaccurate.
* Applicability:
* FitsChan
* All FitsChans have this attribute.
*att--
*/
/* Copy constructor. */
/* ----------------- */
static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
/*
* Name:
* Copy
* Purpose:
* Copy constructor for FitsChan objects.
* Type:
* Private function.
* Synopsis:
* void Copy( const AstObject *objin, AstObject *objout, int *status )
* Description:
* This function implements the copy constructor for FitsChan objects.
* Parameters:
* objin
* Pointer to the FitsChan to be copied.
* objout
* Pointer to the FitsChan being constructed.
* status
* Pointer to the inherited status variable.
* Notes:
* - The source and sink functions are not propagated (i.e. the
* pointers are set NULL in the output FitsChan).
* - This constructor makes a deep copy, including a copy of the
* keyword values.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Declare the thread specific global data */
const char *class; /* Pointer to object class */
AstFitsChan *in; /* Pointer to input FitsChan */
AstFitsChan *out; /* Pointer to output FitsChan */
int *flags;
int icard;
int old_ignore_used; /* Original value of external variable ignore_used */
/* Check the global error status. */
if ( !astOK ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(objin);
/* Obtain pointers to the input and output FitsChans. */
in = (AstFitsChan *) objin;
out = (AstFitsChan *) objout;
/* Nullify all pointers in the output FitsChan so that the input
data will not be deleted in the event of an error occurring. */
out->card = NULL;
out->head = NULL;
out->keyseq = NULL;
out->keywords = NULL;
out->source = NULL;
out->saved_source = NULL;
out->source_wrap = NULL;
out->sink = NULL;
out->sink_wrap = NULL;
out->warnings = NULL;
out->tabsource = NULL;
out->tabsource_wrap = NULL;
/* Store the object class. */
class = astGetClass( in );
/* Ensure all cards are copied, including those already read by astRead. */
old_ignore_used = ignore_used;
ignore_used = 0;
/* Save the current card index in the input FitsChan. */
icard = astGetCard( in );
/* Rewind the input FitsChan. */
astClearCard( in );
/* Copy all the FitsCard structures from input to output. */
while( !astFitsEof( in ) && astOK ){
/* Get a pointer to the flags mask for this card. */
flags = CardFlags( in, status );
/* Store a new card in the output, holding the same information as the
input card. */
NewCard( out, CardName( in, status ), CardType( in, status ), CardData( in, NULL, status ),
CardComm( in, status ), (flags?(*flags):0), status );
/* Move on to the next input card. */
MoveCard( in, 1, "astCopy", class, status );
}
/* Set the current card in both input and output to the current input
card on entry. */
astSetCard( in, icard );
astSetCard( out, icard );
/* Copy the list of keyword sequence numbers used. */
if( in->keyseq ) out->keyseq = astCopy( in->keyseq );
/* Copy the Warnings attribute value */
if( in->warnings ) out->warnings = astStore( NULL, in->warnings,
strlen( in->warnings ) + 1 );
/* Copy any tables currently in the FitsChan structure. */
if( in->tables ) out->tables = astCopy( in->tables );
/* Reinstate the original setting of the external ignore_used variable. */
ignore_used = old_ignore_used;
/* If an error occurred, delete the contents of the output Object. */
if( !astOK ) Delete( objout, status );
}
/* Destructor. */
/* ----------- */
static void Delete( AstObject *obj, int *status ) {
/*
* Name:
* Delete
* Purpose:
* Destructor for FitsChan objects.
* Type:
* Private function.
* Synopsis:
* void Delete( AstObject *obj, int *status )
* Description:
* This function implements the destructor for FitsChan objects.
* Parameters:
* obj
* Pointer to the FitsChan 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: */
AstFitsChan *this; /* Pointer to FitsChan */
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) obj;
/* Write out the contents of the FitsChan using the sink function
provided when it was created. */
WriteToSink( this, status );
/* Remove all cards from the FitsChan. */
EmptyFits( this, status );
}
/* Dump function. */
/* -------------- */
static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
/*
* Name:
* Dump
* Purpose:
* Dump function for FitsChan 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 FitsChan class to an output Channel.
* Parameters:
* this
* Pointer to the FitsChan whose data are being written.
* channel
* Pointer to the Channel to which the data are being written.
* status
* Pointer to the inherited status variable.
*/
#define KEY_LEN 50 /* Maximum length of a keyword */
/* Local Variables: */
AstFitsChan *this; /* Pointer to the FitsChan structure */
astDECLARE_GLOBALS /* Declare the thread specific global data */
char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */
const char *class; /* Object class */
const char *sval; /* Pointer to string value */
int cardtype; /* Keyword data type */
int flags; /* Keyword flags */
int icard; /* Index of current card */
int ival; /* Integer value */
int ncard; /* No. of cards dumped so far */
int old_ignore_used; /* Original value of external variable ignore_used */
int set; /* Attribute value set? */
void *data; /* Pointer to keyword data value */
/* Check the global error status. */
if ( !astOK ) return;
/* Get a pointer to the structure holding thread-specific global data. */
astGET_GLOBALS(this_object);
/* Obtain a pointer to the FitsChan structure. */
this = (AstFitsChan *) this_object;
/* Store the object class. */
class = astGetClass( this );
/* Save the index of ht ecurrent card. */
icard = astGetCard( this );
/* Write out values representing the instance variables for the
FitsChan class. Accompany these with appropriate comment strings,
possibly depending on the values being written.*/
/* Card. */
/* ----- */
astWriteInt( channel, "Card", 1, 1, icard, "Index of current card" );
/* Encoding. */
/* --------- */
set = TestEncoding( this, status );
ival = set ? GetEncoding( this, status ) : astGetEncoding( this );
if( ival > UNKNOWN_ENCODING && ival <= MAX_ENCODING ) {
astWriteString( channel, "Encod", set, 1, xencod[ival], "Encoding system" );
} else {
astWriteString( channel, "Encod", set, 1, UNKNOWN_STRING, "Encoding system" );
}
/* FitsAxisOrder. */
/* -------------- */
set = TestFitsAxisOrder( this, status );
sval = set ? GetFitsAxisOrder( this, status ) : astGetFitsAxisOrder( this );
astWriteString( channel, "FAxOrd", set, 1, sval,
"Order of WCS axes in new FITS headers" );
/* FitsDigits. */
/* ----------- */
set = TestFitsDigits( this, status );
ival = set ? GetFitsDigits( this, status ) : astGetFitsDigits( this );
astWriteInt( channel, "FitsDg", set, 1, ival, "No. of digits for floating point values" );
/* DefB1950 */
/* -------- */
set = TestDefB1950( this, status );
ival = set ? GetDefB1950( this, status ) : astGetDefB1950( this );
astWriteInt( channel, "DfB1950", set, 1, ival, (ival ? "Default to FK4 B1950": "Default to ICRS") );
/* TabOK */
/* ----- */
set = TestTabOK( this, status );
ival = set ? GetTabOK( this, status ) : astGetTabOK( this );
astWriteInt( channel, "TabOK", set, 1, ival, ( ival > 0 ? "EXTVER value for -TAB headers": "Do not support -TAB CTYPE codes") );
/* CDMatrix */
/* -------- */
set = TestCDMatrix( this, status );
ival = set ? GetCDMatrix( this, status ) : astGetCDMatrix( this );
astWriteInt( channel, "CdMat", set, 1, ival, (ival ? "Use CD Matrix":"Use PC matrix") );
/* CarLin */
/* ------ */
set = TestCarLin( this, status );
ival = set ? GetCarLin( this, status ) : astGetCarLin( this );
astWriteInt( channel, "CarLin", set, 1, ival, (ival ? "Use simple linear CAR projections": "Use full FITS-WCS CAR projections") );
/* PolyTan */
/* ------- */
set = TestPolyTan( this, status );
ival = set ? GetPolyTan( this, status ) : astGetPolyTan( this );
astWriteInt( channel, "PolyTan", set, 0, ival, (ival ? "Use distorted TAN convention": "Use standard TAN convention") );
/* Iwc */
/* --- */
set = TestIwc( this, status );
ival = set ? GetIwc( this, status ) : astGetIwc( this );
astWriteInt( channel, "Iwc", set, 1, ival, (ival ? "Include an IWC Frame": "Do not include an IWC Frame") );
/* Clean */
/* ----- */
set = TestClean( this, status );
ival = set ? GetClean( this, status ) : astGetClean( this );
astWriteInt( channel, "Clean", set, 0, ival, "Always remove used cards?" );
/* Warnings. */
/* --------- */
set = TestWarnings( this, status );
sval = set ? GetWarnings( this, status ) : astGetWarnings( this );
astWriteString( channel, "Warn", set, 1, sval, "Warnings to be reported" );
/* Now do instance variables which are not attributes. */
/* =================================================== */
/* Ensure all cards are copied, including those already read by astRead. */
old_ignore_used = ignore_used;
ignore_used = 0;
/* Rewind the FitsChan. */
astClearCard( this );
/* Dump each card. */
ncard = 1;
while( !astFitsEof( this ) && astOK ){
/* Write out the keyword name. */
if( CardName( this, status ) ){
(void) sprintf( buff, "Nm%d", ncard );
astWriteString( channel, buff, 1, 1, CardName( this, status ),
"FITS keyword name" );
}
/* Write out the keyword type. */
cardtype = CardType( this, status );
(void) sprintf( buff, "Ty%d", ncard );
astWriteString( channel, buff, 1, 1, type_names[ cardtype ],
"FITS keyword data type" );
/* Write out the flag values if any are non-zero. */
flags = *CardFlags( this, status );
if( flags ){
(void) sprintf( buff, "Fl%d", ncard );
astWriteInt( channel, buff, 1, 1, flags, "FITS keyword flags" );
}
/* Write out the data value, if defined, using the appropriate data type. */
data = CardData( this, NULL, status );
if( data && cardtype != AST__UNDEF ){
if( cardtype == AST__FLOAT ){
(void) sprintf( buff, "Dt%d", ncard );
astWriteDouble( channel, buff, 1, 1, *( (double *) data ),
"FITS keyword value" );
} else if( cardtype == AST__STRING || cardtype == AST__CONTINUE ){
(void) sprintf( buff, "Dt%d", ncard );
astWriteString( channel, buff, 1, 1, (char *) data,
"FITS keyword value" );
} else if( cardtype == AST__INT ){
(void) sprintf( buff, "Dt%d", ncard );
astWriteInt( channel, buff, 1, 1, *( (int *) data ),
"FITS keyword value" );
} else if( cardtype == AST__LOGICAL ){
(void) sprintf( buff, "Dt%d", ncard );
astWriteInt( channel, buff, 1, 1, *( (int *) data ),
"FITS keyword value" );
} else if( cardtype == AST__COMPLEXF ){
(void) sprintf( buff, "Dr%d", ncard );
astWriteDouble( channel, buff, 1, 1, *( (double *) data ),
"FITS keyword real value" );
(void) sprintf( buff, "Di%d", ncard );
astWriteDouble( channel, buff, 1, 1, *( ( (double *) data ) + 1 ),
"FITS keyword imaginary value" );
} else if( cardtype == AST__COMPLEXI ){
(void) sprintf( buff, "Dr%d", ncard );
astWriteInt( channel, buff, 1, 1, *( (int *) data ),
"FITS keyword real value" );
(void) sprintf( buff, "Di%d", ncard );
astWriteInt( channel, buff, 1, 1, *( ( (int *) data ) + 1 ),
"FITS keyword imaginary value" );
}
}
/* Write out the keyword comment. */
if( CardComm( this, status ) ){
(void) sprintf( buff, "Cm%d", ncard );
astWriteString( channel, buff, 1, 1, CardComm( this, status ),
"FITS keyword comment" );
}
/* Move on to the next card. */
ncard++;
MoveCard( this, 1, "astDump", class, status );
}
/* Dump any FitTables. */
if( this->tables ) {
astWriteObject( channel, "Tables", 1, 1, this->tables,
"A KeyMap holding associated binary tables" );
}
/* Reinstate the original setting of the external ignore_used variable. */
ignore_used = old_ignore_used;
/* Reinstate the original current card. */
astSetCard( this, icard );
#undef KEY_LEN
}
/* Standard class functions. */
/* ========================= */
/* Implement the astIsAFitsChan and astCheckFitsChan functions using the macros
defined for this purpose in the "object.h" header file. */
astMAKE_ISA(FitsChan,Channel)
astMAKE_CHECK(FitsChan)
AstFitsChan *astFitsChan_( const char *(* source)( void ),
void (* sink)( const char * ),
const char *options, int *status, ...) {
/*
*++
* Name:
c astFitsChan
f AST_FITSCHAN
* Purpose:
* Create a FitsChan.
* Type:
* Public function.
* Synopsis:
c #include "fitschan.h"
c AstFitsChan *astFitsChan( const char *(* source)( void ),
c void (* sink)( const char * ),
c const char *options, ... )
f RESULT = AST_FITSCHAN( SOURCE, SINK, OPTIONS, STATUS )
* Class Membership:
* FitsChan constructor.
* Description:
* This function creates a new FitsChan and optionally initialises
* its attributes.
*
* A FitsChan is a specialised form of Channel which supports I/O
* operations involving the use of FITS (Flexible Image Transport
* System) header cards. Writing an Object to a FitsChan (using
c astWrite) will, if the Object is suitable, generate a
f AST_WRITE) will, if the Object is suitable, generate a
* description of that Object composed of FITS header cards, and
* reading from a FitsChan will create a new Object from its FITS
* header card description.
*
* While a FitsChan is active, it represents a buffer which may
* contain zero or more 80-character "header cards" conforming to
* FITS conventions. Any sequence of FITS-conforming header cards
* may be stored, apart from the "END" card whose existence is
* merely implied. The cards may be accessed in any order by using
* the FitsChan's integer Card attribute, which identifies a "current"
* card, to which subsequent operations apply. Searches
c based on keyword may be performed (using astFindFits), new
c cards may be inserted (astPutFits, astPutCards, astSetFits) and
c existing ones may be deleted (astDelFits) or changed (astSetFits).
f based on keyword may be performed (using AST_FINDFITS), new
f cards may be inserted (AST_PUTFITS, AST_PUTCARDS, AST_SETFITS) and
f existing ones may be deleted (AST_DELFITS) or changed (AST_SETFITS).
*
* When you create a FitsChan, you have the option of specifying
* "source" and "sink" functions which connect it to external data
* stores by reading and writing FITS header cards. If you provide
* a source function, it is used to fill the FitsChan with header cards
* when it is accessed for the first time. If you do not provide a
* source function, the FitsChan remains empty until you explicitly enter
c data into it (e.g. using astPutFits, astPutCards, astWrite
f data into it (e.g. using AST_PUTFITS, AST_PUTCARDS, AST_WRITE
* or by using the SourceFile attribute to specifying a text file from
* which headers should be read). When the FitsChan is deleted, any
* remaining header cards in the FitsChan can be saved in either of
* two ways: 1) by specifying a value for the SinkFile attribute (the
* name of a text file to which header cards should be written), or 2)
* by providing a sink function (used to to deliver header cards to an
* external data store). If you do not provide a sink function or a
* value for SinkFile, any header cards remaining when the FitsChan
* is deleted will be lost, so you should arrange to extract them
* first if necessary
c (e.g. using astFindFits or astRead).
f (e.g. using AST_FINDFITS or AST_READ).
*
* Coordinate system information may be described using FITS header
* cards using several different conventions, termed
* "encodings". When an AST Object is written to (or read from) a
* FitsChan, the value of the FitsChan's Encoding attribute
* determines how the Object is converted to (or from) a
* description involving FITS header cards. In general, different
* encodings will result in different sets of header cards to
* describe the same Object. Examples of encodings include the DSS
* encoding (based on conventions used by the STScI Digitised Sky
* Survey data), the FITS-WCS encoding (based on a proposed FITS
* standard) and the NATIVE encoding (a near loss-less way of
* storing AST Objects in FITS headers).
*
* The available encodings differ in the range of Objects they can
* represent, in the number of Object descriptions that can coexist
* in the same FitsChan, and in their accessibility to other
* (external) astronomy applications (see the Encoding attribute
* for details). Encodings are not necessarily mutually exclusive
* and it may sometimes be possible to describe the same Object in
* several ways within a particular set of FITS header cards by
* using several different encodings.
*
c The detailed behaviour of astRead and astWrite, when used with
f The detailed behaviour of AST_READ and AST_WRITE, when used with
* a FitsChan, depends on the encoding in use. In general, however,
c all use of astRead is destructive, so that FITS header cards
f all use of AST_READ is destructive, so that FITS header cards
* are consumed in the process of reading an Object, and are
* removed from the FitsChan (this deletion can be prevented for
* specific cards by calling the
c astRetainFits function).
f AST_RETAINFITS routine).
*
* If the encoding in use allows only a single Object description
* to be stored in a FitsChan (e.g. the DSS, FITS-WCS and FITS-IRAF
c encodings), then write operations using astWrite will
f encodings), then write operations using AST_WRITE will
* over-write any existing Object description using that
* encoding. Otherwise (e.g. the NATIVE encoding), multiple Object
* descriptions are written sequentially and may later be read
* back in the same sequence.
* Parameters:
c source
f SOURCE = FUNCTION (Given)
c Pointer to a source function which takes no arguments and
c returns a pointer to a null-terminated string. This function
c will be used by the FitsChan to obtain input FITS header
c cards. On each invocation, it should read the next input card
c from some external source (such as a FITS file), and return a
c pointer to the (null-terminated) contents of the card. It
c should return a NULL pointer when there are no more cards to
c be read.
c
c If "source" is NULL, the FitsChan will remain empty until
c cards are explicitly stored in it (e.g. using astPutCards,
c astPutFits or via the SourceFile attribute).
f A source routine, which is a function taking two arguments: a
f character argument of length 80 to contain a FITS card, and an
f integer error status argument. It should return an integer value.
f This function will be used by the FitsChan to obtain input
f FITS header cards. On each invocation, it should read the
f next input card from some external source (such as a FITS
f file), and return the contents of the card via its character
f argument. It should return a function result of one unless
f there are no more cards to be read, in which case it should
f return zero. If an error occurs, it should set its error
f status argument to an error value before returning.
f
f If the null routine AST_NULL is supplied as the SOURCE value,
f the FitsChan will remain empty until cards are explicitly
f stored in it (e.g. using AST_PUTCARDS, AST_PUTFITS or via the
f SourceFile attribute).
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. If
c no value has been set for the SinkFile attribute, this
c function will be used by the FitsChan to deliver any FITS
c header cards it contains when it is finally deleted. On
c each invocation, it should deliver the contents of the character
c string passed to it as a FITS header card to some external
c data store (such as a FITS file).
f A sink routine, which is a subroutine which takes two
f arguments: a character argument of length 80 to contain a
f FITS card, and an integer error status argument. If no
f value has been set for the SinkFile attribute, this routine
f will be used by the FitsChan to deliver any FITS header cards
f it contains when it is finally deleted. On each invocation,
f it should deliver the contents of the character string passed
f to it as a FITS header card to some external data store (such
f as a FITS file). If an error occurs, it should set its error
f status argument to an error value before returning.
*
c If "sink" is NULL,
f If the null routine AST_NULL is supplied as the SINK value,
* and no value has been set for the SinkFile attribute, the
* contents of the FitsChan will be lost when it is deleted.
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 FitsChan. 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 FitsChan. 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).
*
* Note, the FITSCHAN_OPTIONS environment variable may be used
* to specify default options for all newly created FitsChans.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Returned Value:
c astFitsChan()
f AST_FITSCHAN = INTEGER
* A pointer to the new FitsChan.
* 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_FITSCHAN. 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).
c - No FITS "END" card will be written via the sink function. You
f - No FITS "END" card will be written via the sink routine. You
* should add this card yourself after the FitsChan has been
* deleted.
* - 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.
* Status Handling:
* The protected interface to this function includes an extra
* parameter at the end of the parameter list descirbed above. This
* parameter is a pointer to the integer inherited status
* variable: "int *status".
*--
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstFitsChan *new; /* Pointer to new FitsChan */
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 FitsChan, 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 = astInitFitsChan( NULL, sizeof( AstFitsChan ), !class_init,
&class_vtab, "FitsChan", source, SourceWrap,
sink, SinkWrap );
/* If successful, note that the virtual function table has been
initialised. */
if ( astOK ) {
class_init = 1;
/* Apply any default options specified by "_OPTIONS" environment
variable. */
astEnvSet( new );
/* Obtain the variable argument list and pass it along with the
options string to the astVSet method to initialise the new
FitsChan'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 FitsChan. */
return new;
}
AstFitsChan *astFitsChanId_( const char *(* source)( void ),
void (* sink)( const char * ),
const char *options, ... ) {
/*
* Name:
* astFitsChanId_
* Purpose:
* Create a FitsChan.
* Type:
* Private function.
* Synopsis:
* #include "fitschan.h"
* AstFitsChan *astFitsChanId_( const char *(* source)( void ),
* void (* sink)( const char * ),
* const char *options, ... )
* Class Membership:
* FitsChan constructor.
* Description:
* This function implements the external (public) C interface to the
* astFitsChan constructor function. Another function (astFitsChanForId)
* should be called to create a FitsChan 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 astFitsChan_ 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 astFitsChan_ 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 astFitsChan_.
* Returned Value:
* The ID value associated with the new FitsChan.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstFitsChan *new; /* Pointer to new FitsChan */
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 FitsChan, 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 = astInitFitsChan( NULL, sizeof( AstFitsChan ), !class_init,
&class_vtab, "FitsChan", source, SourceWrap,
sink, SinkWrap );
/* If successful, note that the virtual function table has been
initialised. */
if ( astOK ) {
class_init = 1;
/* Apply any default options specified by "_OPTIONS" environment
variable. */
astEnvSet( new );
/* Obtain the variable argument list and pass it along with the
options string to the astVSet method to initialise the new
FitsChan'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 FitsChan. */
return astMakeId( new );
}
AstFitsChan *astFitsChanForId_( 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:
* astFitsChanFor
* Purpose:
* Initialise a FitsChan from a foreign language interface.
* Type:
* Public function.
* Synopsis:
* #include "fitschan.h"
* AstFitsChan *astFitsChanFor( 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:
* FitsChan constructor.
* Description:
* This function creates a new FitsChan from a foreign language
* interface and optionally initialises its attributes.
*
* A FitsChan implements FITS input/output for the AST library.
* Writing an Object to a FitsChan (using astWrite) will generate a
* textual representation of that Object in terms of FITS header cards,
* and reading from a FitsChan (using astRead) will create a new Object
* from its FITS representation.
*
* Normally, when you use a FitsChan, 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.
* Parameters:
* source
* Pointer to a "source" function which will be used to obtain
* FITS header cards. 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 FitsChan will remain empty until
* cards are added explicitly (e.g. using astPutCards or astPutFits).
* 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 FITS header card.
* 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" is NULL, the FitsChan will remain empty until
* cards are added explicitly (e.g. using astPutCards or astPutFits).
* sink
* Pointer to a "sink" function which will be used to deliver
* FITS header cards. 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 FitsChan 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 contents of the FitsChan will not be
* written out before being deleted.
* options
* Pointer to a null-terminated string containing an optional
* comma-separated list of attribute assignments to be used for
* initialising the new FitsChan. 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:
* astFitsChanFor()
* A pointer to the new FitsChan.
* 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 FitsChan 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 astFitsChanId_, 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 */
AstFitsChan *new; /* Pointer to new FitsChan */
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 FitsChan, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitFitsChan( NULL, sizeof( AstFitsChan ), !class_init,
&class_vtab, "FitsChan", source, source_wrap,
sink, sink_wrap );
/* If successful, note that the virtual function table has been
initialised. */
if ( astOK ) {
class_init = 1;
/* Apply any default options specified by "_OPTIONS" environment
variable. */
astEnvSet( new );
/* Obtain the variable argument list and pass it along with the
options string to the astVSet method to initialise the new
FitsChan'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 FitsChan. */
return astMakeId( new );
}
AstFitsChan *astInitFitsChan_( void *mem, size_t size, int init,
AstFitsChanVtab *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:
* astInitFitsChan
* Purpose:
* Initialise a FitsChan.
* Type:
* Protected function.
* Synopsis:
* #include "fitschan.h"
* AstFitsChan *astInitFitsChan_( void *mem, size_t size, int init,
* AstFitsChanVtab *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:
* FitsChan initialiser.
* Description:
* This function is provided for use by class implementations to
* initialise a new FitsChan object. It allocates memory (if
* necessary) to accommodate the FitsChan plus any additional data
* associated with the derived class. It then initialises a
* FitsChan structure at the start of this memory. If the "init"
* flag is set, it also initialises the contents of a virtual
* function table for a FitsChan at the start of the memory passed
* via the "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory in which the FitsChan is to be
* initialised. This must be of sufficient size to accommodate
* the FitsChan data (sizeof(FitsChan)) 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 FitsChan (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 FitsChan structure, so a valid value must be
* supplied even if not required for allocating memory.
* init
* A boolean flag indicating if the FitsChan'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 FitsChan.
* 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
* FITS header cards. 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 FitsChan will remain empty until
* cards are added explicitly (e.g. using astPutCards or astPutFits).
* 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 FITS header card.
* 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" is NULL, the FitsChan will remain empty until
* cards are added explicitly (e.g. using astPutCards or astPutFits).
* sink
* Pointer to a "sink" function which will be used to deliver
* FITS header cards. 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 FitsChan 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 contents of the FitsChan will not be
* written out before being deleted.
* Returned Value:
* A pointer to the new FitsChan.
* 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: */
AstFitsChan *new; /* Pointer to new FitsChan */
/* Check the global status. */
if ( !astOK ) return NULL;
/* If necessary, initialise the virtual function table. */
if ( init ) astInitFitsChanVtab( vtab, name );
/* Initialise a Channel structure (the parent class) as the first
component within the FitsChan structure, allocating memory if
necessary. I am not sure why FitsChan has its own source_wrap and
sink_wrap items, rather than just using those inherited from Channel.
It may be possible to do away with the fitschan wrappers and just use
the channel wrapper, but I have not yet tried this. Old mail from RFWS
suggests that it may be because the F77 FitsChan source and sink
interfaces handle fixed length strings (80 characters), whereas
Channel sournce and sink handle variable length strings. This needs
investigating. */
new = (AstFitsChan *) astInitChannel( mem, size, 0,
(AstChannelVtab *) vtab, name,
NULL, NULL, NULL, NULL );
if ( astOK ) {
/* Initialise the FitsChan data. */
/* ---------------------------- */
new->head = NULL;
new->card = NULL;
new->keyseq = NULL;
new->keywords = NULL;
new->defb1950 = -1;
new->tabok = -INT_MAX;
new->cdmatrix = -1;
new->carlin = -1;
new->polytan = -INT_MAX;
new->iwc = -1;
new->clean = -1;
new->fitsdigits = DBL_DIG;
new->fitsaxisorder = NULL;
new->encoding = UNKNOWN_ENCODING;
new->warnings = NULL;
new->tables = NULL;
/* Save the pointers to the source and sink functions and the wrapper
functions that invoke them. */
new->source = source;
new->saved_source = NULL;
new->source_wrap = source_wrap;
new->sink = sink;
new->sink_wrap = sink_wrap;
new->tabsource = NULL;
new->tabsource_wrap = NULL;
/* Rewind the FitsChan so that the next read operation will return the
first card. */
new->card = new->head;
/* 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;
}
AstFitsChan *astLoadFitsChan_( void *mem, size_t size,
AstFitsChanVtab *vtab, const char *name,
AstChannel *channel, int *status ) {
/*
*+
* Name:
* astLoadFitsChan
* Purpose:
* Load a FitsChan.
* Type:
* Protected function.
* Synopsis:
* #include "fitschan.h"
* AstFitsChan *astLoadFitsChan( void *mem, size_t size,
* AstFitsChanVtab *vtab, const char *name,
* AstChannel *channel )
* Class Membership:
* FitsChan loader.
* Description:
* This function is provided to load a new FitsChan 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
* FitsChan 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 a FitsChan at the start of the memory
* passed via the "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory into which the FitsChan is to be
* loaded. This must be of sufficient size to accommodate the
* FitsChan data (sizeof(FitsChan)) 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 FitsChan (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 FitsChan 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(AstFitsChan) is used instead.
* vtab
* Pointer to the start of the virtual function table to be
* associated with the new FitsChan. If this is NULL, a pointer
* to the (static) virtual function table for the FitsChan 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 "FitsChan" is used instead.
* Returned Value:
* A pointer to the new FitsChan.
* 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.
*-
*/
#define KEY_LEN 50 /* Maximum length of a keyword */
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstFitsChan *new; /* Pointer to the new FitsChan */
char *comment; /* Pointer to keyword comment */
char *keynm; /* Keyword name */
char *text; /* Textual version of integer value */
char buff[ KEY_LEN + 1 ]; /* Buffer for keyword string */
double dval[2]; /* Double precision data values */
int flags; /* Keyword flags */
int free_data; /* Should data memory be freed? */
int ival[2]; /* Integer data values */
int ncard; /* No. of FitsCards read so far */
int type; /* Keyword type */
void *data; /* Pointer to keyword data 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 FitsChan. In this case the
FitsChan belongs to this class, so supply appropriate values to be
passed to the parent class loader (and its parent, etc.). */
if ( !vtab ) {
size = sizeof( AstFitsChan );
vtab = &class_vtab;
name = "FitsChan";
/* If required, initialise the virtual function table for this class. */
if ( !class_init ) {
astInitFitsChanVtab( 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 FitsChan. */
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, "FitsChan" );
/* Initialise the KeyMap holding the keywords in the FitsChan. */
new->keywords = NULL;
/* Initialise the list of keyword sequence numbers. */
new->keyseq = NULL;
/* Set the pointers to the source and sink functions, and their
wrapper functions, to NULL (we cannot restore these since they
refer to process-specific addresses). */
new->source = NULL;
new->saved_source = NULL;
new->source_wrap = NULL;
new->sink = NULL;
new->sink_wrap = NULL;
new->tabsource = NULL;
new->tabsource_wrap = NULL;
/* Now read each individual data item from this list and use it to
initialise the appropriate instance variable(s) for this class. */
/* Encoding. */
/* --------- */
text = astReadString( channel, "encod", UNKNOWN_STRING );
if( text && strcmp( text, UNKNOWN_STRING ) ) {
new->encoding = FindString( MAX_ENCODING + 1, xencod, text,
"the FitsChan component 'Encod'",
"astRead", astGetClass( channel ), status );
} else {
new->encoding = UNKNOWN_ENCODING;
}
if ( TestEncoding( new, status ) ) SetEncoding( new, new->encoding, status );
text = astFree( text );
/* FitsAxisOrder. */
/* -------------- */
new->fitsaxisorder = astReadString( channel, "faxord", NULL );
/* FitsDigits. */
/* ----------- */
new->fitsdigits = astReadInt( channel, "fitsdg", DBL_DIG );
if ( TestFitsDigits( new, status ) ) SetFitsDigits( new, new->fitsdigits, status );
/* DefB1950 */
/* -------- */
new->defb1950 = astReadInt( channel, "dfb1950", -1 );
if ( TestDefB1950( new, status ) ) SetDefB1950( new, new->defb1950, status );
/* TabOK */
/* ----- */
new->tabok = astReadInt( channel, "tabok", -INT_MAX );
if ( TestTabOK( new, status ) ) SetTabOK( new, new->tabok, status );
/* CDMatrix */
/* -------- */
new->cdmatrix = astReadInt( channel, "cdmat", -1 );
if ( TestCDMatrix( new, status ) ) SetCDMatrix( new, new->cdmatrix, status );
/* CarLin */
/* ------ */
new->carlin = astReadInt( channel, "carlin", -1 );
if ( TestCarLin( new, status ) ) SetCarLin( new, new->carlin, status );
/* PolyTan */
/* ------- */
new->polytan = astReadInt( channel, "polytan", -1 );
if ( TestPolyTan( new, status ) ) SetPolyTan( new, new->polytan, status );
/* Iwc */
/* --- */
new->iwc = astReadInt( channel, "iwc", -1 );
if ( TestIwc( new, status ) ) SetIwc( new, new->iwc, status );
/* Clean */
/* ----- */
new->clean = astReadInt( channel, "clean", -1 );
if ( TestClean( new, status ) ) SetClean( new, new->clean, status );
/* Warnings. */
/* --------- */
new->warnings = astReadString( channel, "warn", NULL );
/* Card. */
/* ----- */
/* Initialise the index of the card to be read next. */
ncard = 1;
new->card = NULL;
new->head = NULL;
/* Load each card. */
type = AST__NOTYPE + 1;
while( type != AST__NOTYPE && astOK ){
/* Get the keyword type. */
(void) sprintf( buff, "ty%d", ncard );
text = astReadString( channel, buff, " " );
if( strcmp( text, " " ) ) {
type = FindString( 9, type_names, text,
"a FitsChan keyword data type",
"astRead", astGetClass( channel ), status );
} else {
type = AST__NOTYPE;
}
text = astFree( text );
/* Only proceed if the keyword type was found. */
if( type != AST__NOTYPE ){
/* Get the keyword name. Use a default blank name. */
(void) sprintf( buff, "nm%d", ncard );
keynm = astReadString( channel, buff, " " );
/* Get the data value, using the appropriate data type, unless the
keyword is a comment keyword or is undefined. */
free_data = 0;
if( type == AST__FLOAT ){
(void) sprintf( buff, "dt%d", ncard );
dval[ 0 ] = astReadDouble( channel, buff, AST__BAD );
data = (void *) dval;
} else if( type == AST__STRING || type == AST__CONTINUE ){
(void) sprintf( buff, "dt%d", ncard );
data = (void *) astReadString( channel, buff, "" );
free_data = 1;
} else if( type == AST__INT ){
(void) sprintf( buff, "dt%d", ncard );
ival[ 0 ] = astReadInt( channel, buff, 0 );
data = (void *) ival;
} else if( type == AST__LOGICAL ){
(void) sprintf( buff, "dt%d", ncard );
ival[ 0 ] = astReadInt( channel, buff, 0 );
data = (void *) ival;
} else if( type == AST__COMPLEXF ){
(void) sprintf( buff, "dr%d", ncard );
dval[ 0 ] = astReadDouble( channel, buff, AST__BAD );
(void) sprintf( buff, "di%d", ncard );
dval[ 1 ] = astReadDouble( channel, buff, AST__BAD );
data = (void *) dval;
} else if( type == AST__COMPLEXI ){
(void) sprintf( buff, "dr%d", ncard );
ival[ 0 ] = astReadInt( channel, buff, 0 );
(void) sprintf( buff, "di%d", ncard );
ival[ 1 ] = astReadInt( channel, buff, 0 );
data = (void *) ival;
} else {
data = NULL;
}
/* Get the keyword flags (only written by versions of AST later than
V1.4). These are packed into an int. */
(void) sprintf( buff, "fl%d", ncard );
flags = astReadInt( channel, buff, 0 );
/* If the flags were not found, use the keyword deletion flag written by
AST V1.4 and earlier. */
if( !flags ) {
(void) sprintf( buff, "dl%d", ncard );
flags = astReadInt( channel, buff, 0 );
}
/* Get the keyword comment. */
(void) sprintf( buff, "cm%d", ncard );
comment = astReadString( channel, buff, NULL );
/* Append a new card to the output FitsChan. */
NewCard( new, keynm, type, data, comment, flags, status );
/* Free the character strings, and data (if required). */
comment = (char *) astFree( (void *) comment );
keynm = (char *) astFree( (void *) keynm );
if( free_data ) data = astFree( data );
}
/* Move on to the next card. */
ncard++;
}
/* Set up the current card index. */
astSetCard( new, astReadInt( channel, "card", 0 ) );
/* Load any FitTables. */
new->tables = astReadObject( channel, "tables", NULL );
}
/* If an error occurred, clean up by deleting the new FitsChan. */
if ( !astOK ) new = astDelete( new );
/* Return the new FitsChan 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. */
void astWriteFits_( AstFitsChan *this, int *status ){
if( !this ) return;
(**astMEMBER(this,FitsChan,WriteFits))(this, status );
}
void astReadFits_( AstFitsChan *this, int *status ){
if( !astOK ) return;
(**astMEMBER(this,FitsChan,ReadFits))(this, status );
}
void astEmptyFits_( AstFitsChan *this, int *status ){
if( !this ) return;
(**astMEMBER(this,FitsChan,EmptyFits))(this, status );
}
void astShowFits_( AstFitsChan *this, int *status ){
if( !this ) return;
(**astMEMBER(this,FitsChan,ShowFits))(this, status );
}
void astPutCards_( AstFitsChan *this, const char *cards, int *status ){
if( !astOK ) return;
(**astMEMBER(this,FitsChan,PutCards))(this,cards, status );
}
void astPutFits_( AstFitsChan *this, const char *card, int overwrite, int *status ){
if( !astOK ) return;
(**astMEMBER(this,FitsChan,PutFits))(this,card,overwrite, status );
}
void astDelFits_( AstFitsChan *this, int *status ){
if( !astOK ) return;
(**astMEMBER(this,FitsChan,DelFits))(this, status );
}
void astPurgeWCS_( AstFitsChan *this, int *status ){
if( !astOK ) return;
(**astMEMBER(this,FitsChan,PurgeWCS))(this, status );
}
AstKeyMap *astGetTables_( AstFitsChan *this, int *status ){
if( !astOK ) return NULL;
return (**astMEMBER(this,FitsChan,GetTables))(this, status );
}
void astPutTables_( AstFitsChan *this, AstKeyMap *tables, int *status ){
if( !astOK ) return;
(**astMEMBER(this,FitsChan,PutTables))(this, tables, status );
}
void astPutTable_( AstFitsChan *this, AstFitsTable *table, const char *extnam,
int *status ){
if( !astOK ) return;
(**astMEMBER(this,FitsChan,PutTable))(this, table, extnam, status );
}
void astRemoveTables_( AstFitsChan *this, const char *key, int *status ){
if( !astOK ) return;
(**astMEMBER(this,FitsChan,RemoveTables))(this, key, status );
}
void astRetainFits_( AstFitsChan *this, int *status ){
if( !astOK ) return;
(**astMEMBER(this,FitsChan,RetainFits))(this, status );
}
int astFitsEof_( AstFitsChan *this, int *status ){
if( !this ) return 1;
return (**astMEMBER(this,FitsChan,FitsEof))( this, status );
}
void astSetFitsCom_( AstFitsChan *this, const char *name,
const char *comment, int overwrite, int *status ) {
if ( !astOK ) return;
(**astMEMBER(this,FitsChan,SetFitsCom))( this, name, comment, overwrite, status );
}
void astSetFitsI_( AstFitsChan *this, const char *name, int value,
const char *comment, int overwrite, int *status ) {
if ( !astOK ) return;
(**astMEMBER(this,FitsChan,SetFitsI))( this, name, value, comment, overwrite, status );
}
void astSetFitsF_( AstFitsChan *this, const char *name, double value,
const char *comment, int overwrite, int *status ) {
if ( !astOK ) return;
(**astMEMBER(this,FitsChan,SetFitsF))( this, name, value, comment, overwrite, status );
}
void astSetFitsS_( AstFitsChan *this, const char *name, const char *value,
const char *comment, int overwrite, int *status ) {
if ( !astOK ) return;
(**astMEMBER(this,FitsChan,SetFitsS))( this, name, value, comment, overwrite, status );
}
void astSetFitsCN_( AstFitsChan *this, const char *name, const char *value,
const char *comment, int overwrite, int *status ) {
if ( !astOK ) return;
(**astMEMBER(this,FitsChan,SetFitsCN))( this, name, value, comment, overwrite, status );
}
void astSetFitsCF_( AstFitsChan *this, const char *name, double *value,
const char *comment, int overwrite, int *status ) {
if ( !astOK ) return;
(**astMEMBER(this,FitsChan,SetFitsCF))( this, name, value, comment, overwrite, status );
}
void astSetFitsCI_( AstFitsChan *this, const char *name, int *value,
const char *comment, int overwrite, int *status ) {
if ( !astOK ) return;
(**astMEMBER(this,FitsChan,SetFitsCI))( this, name, value, comment, overwrite, status );
}
void astSetFitsL_( AstFitsChan *this, const char *name, int value,
const char *comment, int overwrite, int *status ) {
if ( !astOK ) return;
(**astMEMBER(this,FitsChan,SetFitsL))( this, name, value, comment, overwrite, status );
}
void astSetFitsU_( AstFitsChan *this, const char *name, const char *comment,
int overwrite, int *status ) {
if ( !astOK ) return;
(**astMEMBER(this,FitsChan,SetFitsU))( this, name, comment, overwrite, status );
}
void astSetFitsCM_( AstFitsChan *this, const char *comment, int overwrite, int *status ) {
if ( !astOK ) return;
(**astMEMBER(this,FitsChan,SetFitsCM))( this, comment, overwrite, status );
}
void astClearCard_( AstFitsChan *this, int *status ){
if( !this ) return;
(**astMEMBER(this,FitsChan,ClearCard))( this, status );
}
void astSetCard_( AstFitsChan *this, int card, int *status ){
if( !this ) return;
(**astMEMBER(this,FitsChan,SetCard))( this, card, status );
}
int astTestCard_( AstFitsChan *this, int *status ){
if( !this ) return 0;
return (**astMEMBER(this,FitsChan,TestCard))( this, status );
}
int astGetCard_( AstFitsChan *this, int *status ){
if( !this ) return 0;
return (**astMEMBER(this,FitsChan,GetCard))( this, status );
}
int astGetNcard_( AstFitsChan *this, int *status ){
if( !this ) return 0;
return (**astMEMBER(this,FitsChan,GetNcard))( this, status );
}
int astGetCardType_( AstFitsChan *this, int *status ){
if( !this ) return AST__NOTYPE;
return (**astMEMBER(this,FitsChan,GetCardType))( this, status );
}
const char *astGetCardComm_( AstFitsChan *this, int *status ){
if( !this ) return NULL;
return (**astMEMBER(this,FitsChan,GetCardComm))( this, status );
}
const char *astGetCardName_( AstFitsChan *this, int *status ){
if( !this ) return NULL;
return (**astMEMBER(this,FitsChan,GetCardName))( this, status );
}
int astGetNkey_( AstFitsChan *this, int *status ){
if( !this ) return 0;
return (**astMEMBER(this,FitsChan,GetNkey))( this, status );
}
int astGetClean_( AstFitsChan *this, int *status ){
if( !this ) return 0;
return (**astMEMBER(this,FitsChan,GetClean))( this, status );
}
const char *astGetAllWarnings_( AstFitsChan *this, int *status ){
if( !this ) return NULL;
return (**astMEMBER(this,FitsChan,GetAllWarnings))( this, status );
}
int astGetFitsCF_( AstFitsChan *this, const char *name, double *value, int *status ){
if( !astOK ) return 0;
return (**astMEMBER(this,FitsChan,GetFitsCF))( this, name, value, status );
}
int astGetFitsCI_( AstFitsChan *this, const char *name, int *value, int *status ){
if( !astOK ) return 0;
return (**astMEMBER(this,FitsChan,GetFitsCI))( this, name, value, status );
}
int astGetFitsF_( AstFitsChan *this, const char *name, double *value, int *status ){
if( !astOK ) return 0;
return (**astMEMBER(this,FitsChan,GetFitsF))( this, name, value, status );
}
int astGetFitsI_( AstFitsChan *this, const char *name, int *value, int *status ){
if( !astOK ) return 0;
return (**astMEMBER(this,FitsChan,GetFitsI))( this, name, value, status );
}
int astGetFitsL_( AstFitsChan *this, const char *name, int *value, int *status ){
if( !astOK ) return 0;
return (**astMEMBER(this,FitsChan,GetFitsL))( this, name, value, status );
}
int astTestFits_( AstFitsChan *this, const char *name, int *there, int *status ){
if( there ) *there = 0;
if( !astOK ) return 0;
return (**astMEMBER(this,FitsChan,TestFits))( this, name, there, status );
}
int astGetFitsS_( AstFitsChan *this, const char *name, char **value, int *status ){
if( !astOK ) return 0;
return (**astMEMBER(this,FitsChan,GetFitsS))( this, name, value, status );
}
int astGetFitsCN_( AstFitsChan *this, const char *name, char **value, int *status ){
if( !astOK ) return 0;
return (**astMEMBER(this,FitsChan,GetFitsCN))( this, name, value, status );
}
int astFitsGetCom_( AstFitsChan *this, const char *name, char **comment, int *status ){
if( !astOK ) return 0;
return (**astMEMBER(this,FitsChan,FitsGetCom))( this, name, comment, status );
}
int astKeyFields_( AstFitsChan *this, const char *filter, int maxfld,
int *ubnd, int *lbnd, int *status ){
if( !astOK ) return 0;
return (**astMEMBER(this,FitsChan,KeyFields))( this, filter, maxfld,
ubnd, lbnd, status );
}
int astFindFits_( AstFitsChan *this, const char *name, char *card, int inc, int *status ){
if( !astOK ) return 0;
return (**astMEMBER(this,FitsChan,FindFits))( this, name, card, inc, status );
}
int astGetEncoding_( AstFitsChan *this, int *status ){
if( !astOK ) return UNKNOWN_ENCODING;
return (**astMEMBER(this,FitsChan,GetEncoding))( this, status );
}
int astGetCDMatrix_( AstFitsChan *this, int *status ){
if( !astOK ) return 0;
return (**astMEMBER(this,FitsChan,GetCDMatrix))( this, status );
}
void astSetTableSource_( AstFitsChan *this,
void (*tabsource)( void ),
void (*tabsource_wrap)( void (*)( void ),
AstFitsChan *, const char *,
int, int, int * ),
int *status ){
if( !astOK ) return;
(**astMEMBER(this,FitsChan,SetTableSource))( this, tabsource,
tabsource_wrap, status );
}
void astTableSource_( AstFitsChan *this,
void (* tabsource)( AstFitsChan *, const char *,
int, int, int * ),
int *status ){
if( !astOK ) return;
(**astMEMBER(this,FitsChan,TableSource))( this, tabsource, status );
}
/*
* A diagnostic function which lists the contents of a FitsChan to
* standard output.
*/
/*
static void ListFC( AstFitsChan *, const char * );
static void ListFC( AstFitsChan *this, const char *ttl ) {
FitsCard *cardo;
char card[ 81 ];
printf("%s\n----------------------------------------\n", ttl );
cardo = (FitsCard *) this->card;
astClearCard( this );
while( !astFitsEof( this ) && astOK ){
FormatCard( this, card, "List" );
if( this->card == cardo ) {
printf( "%s <<<<< currrent card <<<<< \n", card );
} else {
printf( "%s\n", card );
}
MoveCard( this, 1, "List", "FitsChan" );
}
this->card = cardo;
}
*/
ast-8.0.7/fskyframe.c 0000664 0001750 0001750 00000006447 12610415012 011376 0000000 0000000 /*
*+
* Name:
* fskyframe.c
* Purpose:
* Define a FORTRAN 77 interface to the AST SkyFrame class.
* Type of Module:
* C source file.
* Description:
* This file defines FORTRAN 77-callable C functions which provide
* a public FORTRAN 77 interface to the SkyFrame class.
* Routines Defined:
* AST_ISASKYFRAME
* AST_SKYFRAME
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* 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
* .
* Authors:
* RFWS: R.F. Warren-Smith (Starlink)
* History:
* 23-JUL-1996 (RFWS):
* Original version.
*/
/* Define the astFORTRAN77 macro which prevents error messages from
AST C functions from reporting the file and line number where the
error occurred (since these would refer to this file, they would
not be useful). */
#define astFORTRAN77
/* Header files. */
/* ============= */
#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */
#include "c2f77.h" /* F77 <-> C support functions/macros */
#include "error.h" /* Error reporting facilities */
#include "memory.h" /* Memory handling facilities */
#include "skyframe.h" /* C interface to the SkyFrame class */
F77_LOGICAL_FUNCTION(ast_isaskyframe)( INTEGER(THIS),
INTEGER(STATUS) ) {
GENPTR_INTEGER(THIS)
F77_LOGICAL_TYPE(RESULT);
astAt( "AST_ISASKYFRAME", NULL, 0 );
astWatchSTATUS(
RESULT = astIsASkyFrame( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE;
)
return RESULT;
}
F77_INTEGER_FUNCTION(ast_skyframe)( CHARACTER(OPTIONS),
INTEGER(STATUS)
TRAIL(OPTIONS) ) {
GENPTR_CHARACTER(OPTIONS)
F77_INTEGER_TYPE(RESULT);
int i;
char *options;
astAt( "AST_SKYFRAME", NULL, 0 );
astWatchSTATUS(
options = astString( OPTIONS, OPTIONS_length );
/* Truncate the options string to exlucde any trailing spaces. */
astChrTrunc( options );
/* Change ',' to '\n' (see AST_SET in fobject.c for why). */
if ( astOK ) {
for ( i = 0; options[ i ]; i++ ) {
if ( options[ i ] == ',' ) options[ i ] = '\n';
}
}
RESULT = astP2I( astSkyFrame( "%s", options ) );
astFree( options );
)
return RESULT;
}
F77_INTEGER_FUNCTION(ast_skyoffsetmap)( INTEGER(THIS),
INTEGER(STATUS) ) {
GENPTR_INTEGER(THIS)
F77_INTEGER_TYPE(RESULT);
astAt( "AST_SKYOFFSETMAP", NULL, 0 );
astWatchSTATUS(
RESULT = astP2I( astSkyOffsetMap( astI2P( *THIS ) ) );
)
return RESULT;
}
ast-8.0.7/timemap.c 0000664 0001750 0001750 00000604240 12610415012 011036 0000000 0000000 /*
*class++
* Name:
* TimeMap
* Purpose:
* Sequence of time coordinate conversions.
* Constructor Function:
c astTimeMap (also see astTimeAdd)
f AST_TIMEMAP (also see AST_TIMEADD)
* Description:
* A TimeMap is a specialised form of 1-dimensional Mapping which can be
* used to represent a sequence of conversions between standard time
* coordinate systems.
*
* When a TimeMap is first created, it simply performs a unit
c (null) Mapping. Using the astTimeAdd
f (null) Mapping. Using the AST_TIMEADD
c function, a series of coordinate conversion steps may then be
f routine, a series of coordinate conversion steps may then be
* added. This allows multi-step conversions between a variety of
* time coordinate systems to be assembled out of a set of building
* blocks.
*
* For details of the individual coordinate conversions available,
c see the description of the astTimeAdd function.
f see the description of the AST_TIMEADD routine.
* Inheritance:
* The TimeMap class inherits from the Mapping class.
* Attributes:
* The TimeMap class does not define any new attributes beyond those
* which are applicable to all Mappings.
* Functions:
c In addition to those functions applicable to all Mappings, the
c following function may also be applied to all TimeMaps:
f In addition to those routines applicable to all Mappings, the
f following routine may also be applied to all TimeMaps:
*
c - astTimeAdd: Add a time coordinate conversion to an TimeMap
f - AST_TIMEADD: Add a time coordinate conversion to an TimeMap
* 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
* .
* Authors:
* NG: Norman Gray (Starlink)
* DSB: David Berry (Starlink)
* History:
* 5-Sep-2003 (NG):
* Original version (drawing heavily on specmap.c)
* 25-MAY-2005 (DSB):
* Extensive modifications to make it more AST-like.
* 10-AUG-2005 (DSB):
* Add 2006 leap second.
* 14-FEB-2006 (DSB):
* Override astGetObjSize.
* 10-MAY-2006 (DSB):
* Override astEqual.
* 15-OCT-2006 (DSB):
* Add conversions between UT1 and UTC (UTTOUTC and UTCTOUT).
* 3-APR-2008 (DSB):
* Only call memcpy if the source and destination pointers are
* different.
* 15-APR-2008 (DSB):
* Add missing "break;" statement to "case AST__LMSTTOGMST:"
* in Transform.
* 20-MAY-2008 (DSB):
* Add conversions between Local Time and UTC (LTTOUTC and UTCTOLT).
* 18-JUN-2009 (DSB):
* Add OBSALT to argument list for TTTOTDB and TDBTOTT. Change
* CLOCKLAT/LON to OBSLAT/LON for consistency with other classes.
*class--
*/
/* Module Macros. */
/* ============== */
/* Set the name of the class we are implementing. This indicates to
the header files that define class interfaces that they should make
"protected" symbols available. */
#define astCLASS TimeMap
/* Codes to identify time coordinate conversions. */
#define AST__TIME_NULL 0 /* Null value */
#define AST__MJDTOMJD 1 /* MJD to MJD */
#define AST__MJDTOJD 2 /* MJD to JD */
#define AST__JDTOMJD 3 /* JD to MJD */
#define AST__MJDTOBEP 4 /* MJD to Besselian epoch */
#define AST__BEPTOMJD 5 /* Besselian epoch to MJD */
#define AST__MJDTOJEP 6 /* MJD to Julian epoch */
#define AST__JEPTOMJD 7 /* Julian epoch to MJD */
#define AST__TAITOUTC 8 /* TAI to UTC */
#define AST__UTCTOTAI 9 /* UTC to TAI */
#define AST__TTTOTAI 10 /* TT to TAI */
#define AST__TAITOTT 11 /* TAI to TT */
#define AST__TDBTOTT 12 /* TDB to TT */
#define AST__TTTOTDB 13 /* TT to TDB */
#define AST__TCGTOTT 14 /* TCG to TT */
#define AST__TTTOTCG 15 /* TT to TCG */
#define AST__TCBTOTDB 16 /* TCB to TDB */
#define AST__TDBTOTCB 17 /* TDB to TCB */
#define AST__UTTOGMST 18 /* UT to GMST */
#define AST__GMSTTOUT 19 /* GMST to UT1 */
#define AST__GMSTTOLMST 20 /* GMST to LMST */
#define AST__LMSTTOGMST 21 /* LMST to GMST */
#define AST__LASTTOLMST 22 /* LAST to LMST */
#define AST__LMSTTOLAST 23 /* LMST to LAST */
#define AST__UTTOUTC 24 /* UT1 to UTC */
#define AST__UTCTOUT 25 /* UTC to UT1 */
#define AST__LTTOUTC 26 /* Local Time to UTC */
#define AST__UTCTOLT 27 /* UTC to Local Time */
/* Maximum number of arguments required by a conversion. */
#define MAX_ARGS 6
/* The alphabet (used for generating keywords for arguments). */
#define ALPHABET "abcdefghijklmnopqrstuvwxyz"
/* Angle conversion */
#define PI 3.1415926535897932384626433832795028841971693993751
#define D2PI (2*PI)
#define PIBY2 (PI/2.0)
#define D2R (PI/180.0)
#define R2D (180.0/PI)
/* Other constants */
#define SPD 86400
#define LG 6.969290134E-10
#define LB 1.55051976772E-8
#define P0 6.55E-5
#define TTOFF 32.184
/* Macros which return the maximum and minimum of two values. */
#define MAX(aa,bb) ((aa)>(bb)?(aa):(bb))
#define MIN(aa,bb) ((aa)<(bb)?(aa):(bb))
/* Macro to check for equality of floating point values. We cannot
compare bad values directory because of the danger of floating point
exceptions, so bad values are dealt with explicitly. */
#define EQUAL(aa,bb) (((aa)==AST__BAD)?(((bb)==AST__BAD)?1:0):(((bb)==AST__BAD)?0:(fabs((aa)-(bb))<=1.0E5*MAX((fabs(aa)+fabs(bb))*DBL_EPSILON,DBL_MIN))))
/* Include files. */
/* ============== */
/* Interface definitions. */
/* ---------------------- */
#include "pal.h" /* SLALIB interface */
#include "slamap.h" /* Spatial sla mappings */
#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 "pointset.h" /* Sets of points/coordinates */
#include "mapping.h" /* Coordinate Mappings (parent class) */
#include "unitmap.h" /* Unit (null) Mappings */
#include "timemap.h" /* Interface definition for this class */
/* Error code definitions. */
/* ----------------------- */
#include "ast_err.h" /* AST error codes */
/* C header files. */
/* --------------- */
#include
#include
#include
#include
#include
/* 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 int (* parent_getobjsize)( AstObject *, int * );
static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
static double (* parent_rate)( AstMapping *, double *, int, int, int * );
#ifdef THREAD_SAFE
/* Define how to initialise thread-specific globals. */
#define GLOBAL_inits \
globals->Class_Init = 0;
/* Create the function that initialises global data for this module. */
astMAKE_INITGLOBALS(TimeMap)
/* Define macros for accessing each item of thread specific global data. */
#define class_init astGLOBAL(TimeMap,Class_Init)
#define class_vtab astGLOBAL(TimeMap,Class_Vtab)
#include
#else
/* Define the class virtual function table and its initialisation flag
as static variables. */
static AstTimeMapVtab 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. */
AstTimeMap *astTimeMapId_( int, const char *, ... );
/* Prototypes for Private Member Functions. */
/* ======================================== */
static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
static const char *CvtString( int, const char **, int *, int *, const char *[ MAX_ARGS ], int * );
static double Gmsta( double, double, int, int * );
static double Rate( AstMapping *, double *, int, int, int * );
static double Rcc( double, double, double, double, double, int * );
static int Equal( AstObject *, AstObject *, int * );
static int CvtCode( const char *, int * );
static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * );
static void AddArgs( int, double *, int * );
static void AddTimeCvt( AstTimeMap *, int, const double *, int * );
static void Copy( const AstObject *, AstObject *, int * );
static void Delete( AstObject *, int * );
static void Dump( AstObject *, AstChannel *, int * );
static void TimeAdd( AstTimeMap *, const char *, const double[], int * );
static int GetObjSize( AstObject *, int * );
/* Member functions. */
/* ================= */
static int Equal( AstObject *this_object, AstObject *that_object, int *status ) {
/*
* Name:
* Equal
* Purpose:
* Test if two TimeMaps are equivalent.
* Type:
* Private function.
* Synopsis:
* #include "timemap.h"
* int Equal( AstObject *this, AstObject *that, int *status )
* Class Membership:
* TimeMap member function (over-rides the astEqual protected
* method inherited from the astMapping class).
* Description:
* This function returns a boolean result (0 or 1) to indicate whether
* two TimeMaps are equivalent.
* Parameters:
* this
* Pointer to the first Object (a TimeMap).
* that
* Pointer to the second Object.
* status
* Pointer to the inherited status variable.
* Returned Value:
* One if the TimeMaps are equivalent, zero otherwise.
* 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: */
AstTimeMap *that;
AstTimeMap *this;
const char *argdesc[ MAX_ARGS ];
const char *comment;
int i, j;
int nargs;
int nin;
int nout;
int result;
int szargs;
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain pointers to the two TimeMap structures. */
this = (AstTimeMap *) this_object;
that = (AstTimeMap *) that_object;
/* Check the second object is a TimeMap. We know the first is a
TimeMap since we have arrived at this implementation of the virtual
function. */
if( astIsATimeMap( that ) ) {
/* Get the number of inputs and outputs and check they are the same for both. */
nin = astGetNin( this );
nout = astGetNout( this );
if( astGetNin( that ) == nin && astGetNout( that ) == nout ) {
/* If the Invert flags for the two TimeMaps differ, it may still be possible
for them to be equivalent. First compare the TimeMaps if their Invert
flags are the same. In this case all the attributes of the two TimeMaps
must be identical. */
if( astGetInvert( this ) == astGetInvert( that ) ) {
if( this->ncvt == that->ncvt ) {
result = 1;
for( i = 0; i < this->ncvt && result; i++ ) {
if( this->cvttype[ i ] != that->cvttype[ i ] ) {
result = 0;
} else {
CvtString( this->cvttype[ i ], &comment, &nargs, &szargs, argdesc, status );
for( j = 0; j < nargs; j++ ) {
if( !astEQUAL( this->cvtargs[ i ][ j ],
that->cvtargs[ i ][ j ] ) ){
result = 0;
break;
}
}
}
}
}
/* If the Invert flags for the two TimeMaps differ, the attributes of the two
TimeMaps must be inversely related to each other. */
} else {
/* In the specific case of a TimeMap, Invert flags must be equal. */
result = 0;
}
}
}
/* If an error occurred, clear the result value. */
if ( !astOK ) result = 0;
/* Return the result, */
return result;
}
static int GetObjSize( AstObject *this_object, int *status ) {
/*
* Name:
* GetObjSize
* Purpose:
* Return the in-memory size of an Object.
* Type:
* Private function.
* Synopsis:
* #include "timemap.h"
* int GetObjSize( AstObject *this, int *status )
* Class Membership:
* TimeMap member function (over-rides the astGetObjSize protected
* method inherited from the parent class).
* Description:
* This function returns the in-memory size of the supplied TimeMap,
* in bytes.
* Parameters:
* this
* Pointer to the TimeMap.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The Object size, in bytes.
* 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: */
AstTimeMap *this; /* Pointer to TimeMap structure */
int result; /* Result value to return */
int cvt; /* Loop counter for coordinate conversions */
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain a pointers to the TimeMap structure. */
this = (AstTimeMap *) this_object;
/* Invoke the GetObjSize method inherited from the parent class, and then
add on any components of the class structure defined by thsi class
which are stored in dynamically allocated memory. */
result = (*parent_getobjsize)( this_object, status );
for ( cvt = 0; cvt < this->ncvt; cvt++ ) {
result += astTSizeOf( this->cvtargs[ cvt ] );
}
result += astTSizeOf( this->cvtargs );
result += astTSizeOf( this->cvttype );
/* If an error occurred, clear the result value. */
if ( !astOK ) result = 0;
/* Return the result, */
return result;
}
static void AddArgs( int cvttype, double *cvtargs, int *status ) {
/*
* Name:
* AddArgs
* Purpose:
* Set values for addition conversion arguments.
* Type:
* Private function.
* Synopsis:
* #include "timemap.h"
* void AddArgs( int cvttype, double *cvtargs, int *status )
* Class Membership:
* TimeMap member function.
* Description:
* This function stores value for additional conversion arguments,
* based on the values supplied for the user arguments.
* Parameters:
* cvttype
* The conversion type.
* cvtargs
* The arguments for the conversion.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
double r; /* Distance from Earth axis (AU) */
double z; /* Distance from plane of Earth equator (AU) */
/* Check the global error status. */
if ( !astOK ) return;
/* Test for each valid code value in turn and assign the appropriate
extra values. */
switch ( cvttype ) {
case AST__MJDTOMJD:
cvtargs[ 2 ] = cvtargs[ 0 ] - cvtargs[ 1 ];
break;
case AST__MJDTOJD:
cvtargs[ 2 ] = cvtargs[ 0 ] - cvtargs[ 1 ] + 2400000.5;
break;
case AST__JDTOMJD:
cvtargs[ 2 ] = cvtargs[ 0 ] - cvtargs[ 1 ] - 2400000.5;
break;
case AST__MJDTOBEP:
cvtargs[ 2 ] = palEpb( cvtargs[ 0 ] ) - palEpb( 0.0 ) - cvtargs[ 1 ];
cvtargs[ 3 ] = palEpb2d( cvtargs[ 1 ] ) - palEpb2d( 0.0 ) - cvtargs[ 0 ];
break;
case AST__BEPTOMJD:
cvtargs[ 2 ] = palEpb2d( cvtargs[ 0 ] ) - palEpb2d( 0.0 ) - cvtargs[ 1 ];
cvtargs[ 3 ] = palEpb( cvtargs[ 1 ] ) - palEpb( 0.0 ) - cvtargs[ 0 ];
break;
case AST__MJDTOJEP:
cvtargs[ 2 ] = palEpj( cvtargs[ 0 ] ) - palEpj( 0.0 ) - cvtargs[ 1 ];
cvtargs[ 3 ] = palEpj2d( cvtargs[ 1 ] ) - palEpj2d( 0.0 ) - cvtargs[ 0 ];
break;
case AST__JEPTOMJD:
cvtargs[ 2 ] = palEpj2d( cvtargs[ 0 ] ) - palEpj2d( 0.0 ) - cvtargs[ 1 ];
cvtargs[ 3 ] = palEpj( cvtargs[ 1 ] ) - palEpj( 0.0 ) - cvtargs[ 0 ];
break;
case AST__TTTOTDB:
palGeoc( cvtargs[ 2 ], cvtargs[ 3 ], &r, &z );
cvtargs[ 4 ] = 0.001*r*AST__AU;
cvtargs[ 5 ] = 0.001*z*AST__AU;
break;
case AST__TDBTOTT:
palGeoc( cvtargs[ 2 ], cvtargs[ 3 ], &r, &z );
cvtargs[ 4 ] = 0.001*r*AST__AU;
cvtargs[ 5 ] = 0.001*z*AST__AU;
break;
case AST__TDBTOTCB:
cvtargs[ 1 ] = LB*( cvtargs[ 0 ] - (TTOFF/SPD)
- 43144.0 ) + P0/SPD;
break;
case AST__TCBTOTDB:
cvtargs[ 1 ] = LB*( cvtargs[ 0 ] - (TTOFF/SPD)
- 43144.0 ) + P0/SPD;
break;
case AST__TTTOTCG:
cvtargs[ 1 ] = LG*( cvtargs[ 0 ] - (TTOFF/SPD) - 43144.0 );
break;
case AST__TCGTOTT:
cvtargs[ 1 ] = LG*( cvtargs[ 0 ] - (TTOFF/SPD) - 43144.0 );
break;
}
}
static void AddTimeCvt( AstTimeMap *this, int cvttype, const double *args, int *status ) {
/*
* Name:
* AddTimeCvt
* Purpose:
* Add a coordinate conversion step to an TimeMap.
* Type:
* Private function.
* Synopsis:
* #include "timemap.h"
* void AddTimeCvt( AstTimeMap *this, int cvttype, const double *args )
* Class Membership:
* TimeMap member function.
* Description:
* This function allows one of the supported time coordinate
* conversions to be appended to a TimeMap. When a TimeMap is first
* created (using astTimeMap), it simply performs a unit mapping. By
* using AddTimeCvt repeatedly, a series of coordinate conversions may
* then be specified which the TimeMap will subsequently perform in
* sequence. This allows a complex coordinate conversion to be
* assembled out of the basic building blocks. The TimeMap will also
* perform the inverse coordinate conversion (applying the individual
* conversion steps in reverse) if required.
* Parameters:
* this
* Pointer to the TimeMap.
* cvttype
* A code to identify which time coordinate conversion is to be
* appended. See the "Coordinate Conversions" section for details
* of those available.
* args
* Pointer to an array of double containing the argument values
* required to fully specify the required coordinate
* conversion. The number of arguments depends on the conversion
* (see the "Coordinate Conversions" section for details). This
* value is ignored and may be NULL if no arguments are required.
* Returned Value:
* void.
* Coordinate Conversions:
* The following values may be supplied for the "cvttype" parameter
* in order to specify the coordinate conversion to be performed.
* The argument(s) required to fully specify each conversion are
* indicated in parentheses after each value, and described at the end
* of the list. Values for these should be given in the array pointed
* at by "args".
*
* AST__MJDTOMJD( MJDOFF1, MJDOFF2 )
* Convert Modified Julian Date from one offset to another.
* AST__MJDTOJD( MJDOFF, JDOFF )
* Convert Modified Julian Date to Julian Date.
* AST__JDTOMJD( JDOFF, MJDOFF )
* Convert Julian Date to Modified Julian Date.
* AST__MJDTOBEP( MJDOFF, BEPOFF )
* Convert Modified Julian Date to Besselian epoch.
* AST__BEPTOMJD( BEPOFF, MJDOFF )
* Convert Besselian epoch to Modified Julian Date.
* AST__MJDTOJEP( MJDOFF, JEPOFF )
* Convert Modified Julian Date to Julian epoch.
* AST__JEPTOMJD( JEPOFF, MJDOFF )
* Convert Julian epoch to Modified Julian Date.
* AST__TAITOUTC( MJDOFF )
* Convert a TAI MJD to a UTC MJD.
* AST__UTCTOTAI( MJDOFF )
* Convert a UTC MJD to a TAI MJD.
* AST__TAITOTT( MJDOFF )
* Convert a TAI MJD to a TT MJD.
* AST__TTTOTAI( MJDOFF )
* Convert a TT MJD to a TAI MJD.
* AST__TTTOTDB( MJDOFF, OBSLON, OBSLAT, OBSALT )
* Convert a TT MJD to a TDB MJD.
* AST__TDBTOTT( MJDOFF, OBSLON, OBSLAT, OBSALT )
* Convert a TDB MJD to a TT MJD.
* AST__TTTOTCG( MJDOFF )
* Convert a TT MJD to a TCG MJD.
* AST__TCGTOTT( MJDOFF )
* Convert a TCG MJD to a TT MJD.
* AST__TDBTOTCB( MJDOFF)
* Convert a TAI MJD to a TCB MJD.
* AST__TCBTOTDB( MJDOFF)
* Convert a TCB MJD to a TDB MJD.
* AST__UTTOGMST( MJDOFF )
* Convert a UT MJD to a GMST MJD.
* AST__GMSTTOUT( MJDOFF )
* Convert a GMST MJD to a UT MJD.
* AST__GMSTTOLMST( MJDOFF, OBSLON, OBSLAT )
* Convert a GMST MJD to a LMST MJD.
* AST__LMSTTOGMST( MJDOFF, OBSLON, OBSLAT )
* Convert a LMST MJD to a GMST MJD.
* AST__LASTTOLMST( MJDOFF, OBSLON, OBSLAT )
* Convert a LAST MJD to a LMST MJD.
* AST__LMSTTOLAST( MJDOFF, OBSLON, OBSLAT )
* Convert a LMST MJD to a LAST MJD.
* AST__UTTOUTC( DUT1 )
* Convert a UT1 MJD to a UTC MJD.
* AST__UTCTOUT( DUT1 )
* Convert a UTC MJD to a UT1 MJD.
* AST__LTTOUTC( LTOFF )
* Convert a local time MJD to a UTC MJD.
* AST__UTCTOLT( LTOFF )
* Convert a UTC MJD to a local time MJD.
*
* The units for the values processed by the above conversions are as
* follows:
*
* - MJD, MJDOFF, JD, JDOFF: days
* - Julian epochs, BEPOFF: Tropical years
* - Besselian epochs, JEPOFF: Julian years
*
* The arguments used in the above conversions are as follows:
*
* - MJDOFF: Offset to be added to each MJD value
* - JDOFF: Offset to be added to each JD value
* - JEPOFF: Offset to be added to each Julian epoch value
* - BEPOFF: Offset to be added to each Besselian epoch value
* - OBSLON: Observer's longitude in radians (+ve westwards)
* - OBSLAT: Observer's geodetic latitude in radians (+ve northwards)
* - OBSALT: Observer's geodetic altitude in metres.
* - DUT1: The value of UT1-UTC
* - LTOFF: The offset between Local Time and UTC (in hours, positive
* for time zones east of Greenwich).
* Notes:
* - The specified conversion is appended only if the TimeMap's
* Invert attribute is zero. If it is non-zero, this function
* effectively prefixes the inverse of the conversion specified
* instead.
*/
/* Local Variables: */
const char *argdesc[ MAX_ARGS ]; /* Pointers to argument descriptions */
const char *comment; /* Pointer to comment string */
const char *cvt_string; /* Pointer to conversion type string */
int i; /* Argument index */
int nargs; /* Number of user-supplied arguments */
int ncvt; /* Number of coordinate conversions */
int szargs; /* Size of arguments array */
/* Check the global error status. */
if ( !astOK ) return;
/* Validate the coordinate conversion type and obtain the number of
required user-supplied arguments, and the size of the array in which
to put the user-supplied arguments (the array may leave room after
the user-supplied arguments for various useful pre-calculated values). */
cvt_string = CvtString( cvttype, &comment, &nargs, &szargs, argdesc, status );
/* If the coordinate conversion type was not valid, then report an
error. */
if ( astOK && !cvt_string ) {
astError( AST__TIMIN, "AddTimeCvt(%s): Invalid time coordinate "
"conversion type (%d).", status, astGetClass( this ),
(int) cvttype );
}
/* Note the number of coordinate conversions already stored in the TimeMap. */
if ( astOK ) {
ncvt = this->ncvt;
/* Extend the array of conversion types and the array of pointers to
their argument lists to accommodate the new one. */
this->cvttype = (int *) astGrow( this->cvttype, ncvt + 1,
sizeof( int ) );
this->cvtargs = (double **) astGrow( this->cvtargs, ncvt + 1,
sizeof( double * ) );
/* Allocate memory for the argument list, putting a pointer to it into
the TimeMap. */
this->cvtargs[ ncvt ] = astMalloc( sizeof( double ) * (size_t) szargs );
/* Store the conversion type and increment the conversion count. Also
copy the supplied arguments into the memory allocated above and put
suitable values in any elements of the argument array which are beyond
the end of the user-supplied arguments. These are intermediate values
calculated on the basis of the user-supplied arguments. */
if ( astOK ) {
this->cvttype[ ncvt ] = cvttype;
for( i = 0; i < nargs; i++ ) this->cvtargs[ ncvt ][ i ] = args[ i ];
for( i = nargs; i < szargs; i++ ) this->cvtargs[ ncvt ][ i ] = AST__BAD;
this->ncvt++;
/* Test for each valid code value in turn and assign the appropriate extra values. */
AddArgs( cvttype, this->cvtargs[ ncvt ], status );
}
}
}
static int CvtCode( const char *cvt_string, int *status ) {
/*
* Name:
* CvtCode
* Purpose:
* Convert a conversion type from a string representation to a code value.
* Type:
* Private function.
* Synopsis:
* #include "timemap.h"
* int CvtCode( const char *cvt_string, int *status )
* Class Membership:
* TimeMap member function.
* Description:
* This function accepts a string used to repersent one of the
* TimeMap coordinate conversions and converts it into a code
* value for internal use.
* Parameters:
* cvt_string
* Pointer to a constant null-terminated string representing a
* time coordinate conversion. This is case sensitive and should
* contain no unnecessary white space.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The equivalent conversion code. If the string was not
* recognised, the code AST__TIME_NULL is returned, without error.
* Notes:
* - A value of AST__TIME_NULL 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 result; /* Result value to return */
/* Initialise. */
result = AST__TIME_NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Test the string against each recognised value in turn and assign
the result. */
if ( astChrMatch( cvt_string, "MJDTOJD" ) ) {
result = AST__MJDTOJD;
} else if ( astChrMatch( cvt_string, "MJDTOMJD" ) ) {
result = AST__MJDTOMJD;
} else if ( astChrMatch( cvt_string, "JDTOMJD" ) ) {
result = AST__JDTOMJD;
} else if ( astChrMatch( cvt_string, "JDTOMJD" ) ) {
result = AST__JDTOMJD;
} else if ( astChrMatch( cvt_string, "MJDTOBEP" ) ) {
result = AST__MJDTOBEP;
} else if ( astChrMatch( cvt_string, "BEPTOMJD" ) ) {
result = AST__BEPTOMJD;
} else if ( astChrMatch( cvt_string, "MJDTOJEP" ) ) {
result = AST__MJDTOJEP;
} else if ( astChrMatch( cvt_string, "JEPTOMJD" ) ) {
result = AST__JEPTOMJD;
} else if ( astChrMatch( cvt_string, "TAITOUTC" ) ) {
result = AST__TAITOUTC;
} else if ( astChrMatch( cvt_string, "UTCTOTAI" ) ) {
result = AST__UTCTOTAI;
} else if ( astChrMatch( cvt_string, "TAITOTT" ) ) {
result = AST__TAITOTT;
} else if ( astChrMatch( cvt_string, "TTTOTAI" ) ) {
result = AST__TTTOTAI;
} else if ( astChrMatch( cvt_string, "TTTOTDB" ) ) {
result = AST__TTTOTDB;
} else if ( astChrMatch( cvt_string, "TDBTOTT" ) ) {
result = AST__TDBTOTT;
} else if ( astChrMatch( cvt_string, "TTTOTCG" ) ) {
result = AST__TTTOTCG;
} else if ( astChrMatch( cvt_string, "TCGTOTT" ) ) {
result = AST__TCGTOTT;
} else if ( astChrMatch( cvt_string, "TDBTOTCB" ) ) {
result = AST__TDBTOTCB;
} else if ( astChrMatch( cvt_string, "TCBTOTDB" ) ) {
result = AST__TCBTOTDB;
} else if ( astChrMatch( cvt_string, "UTTOGMST" ) ) {
result = AST__UTTOGMST;
} else if ( astChrMatch( cvt_string, "GMSTTOUT" ) ) {
result = AST__GMSTTOUT;
} else if ( astChrMatch( cvt_string, "GMSTTOLMST" ) ) {
result = AST__GMSTTOLMST;
} else if ( astChrMatch( cvt_string, "LMSTTOGMST" ) ) {
result = AST__LMSTTOGMST;
} else if ( astChrMatch( cvt_string, "LASTTOLMST" ) ) {
result = AST__LASTTOLMST;
} else if ( astChrMatch( cvt_string, "LMSTTOLAST" ) ) {
result = AST__LMSTTOLAST;
} else if ( astChrMatch( cvt_string, "UTTOUTC" ) ) {
result = AST__UTTOUTC;
} else if ( astChrMatch( cvt_string, "UTCTOUT" ) ) {
result = AST__UTCTOUT;
} else if ( astChrMatch( cvt_string, "LTTOUTC" ) ) {
result = AST__LTTOUTC;
} else if ( astChrMatch( cvt_string, "UTCTOLT" ) ) {
result = AST__UTCTOLT;
}
/* Return the result. */
return result;
}
static const char *CvtString( int cvt_code, const char **comment,
int *nargs, int *szargs,
const char *arg[ MAX_ARGS ], int *status ) {
/*
* Name:
* CvtString
* Purpose:
* Convert a conversion type from a code value to a string representation.
* Type:
* Private function.
* Synopsis:
* #include "timemap.h"
* const char *CvtString( int cvt_code, const char **comment, int *nargs,
* int *szargs, const char *arg[ MAX_ARGS ], int *status )
* Class Membership:
* TimeMap member function.
* Description:
* This function accepts a code value used to represent one of the
* TimeMap coordinate conversions and converts it into an
* equivalent string representation. It also returns a descriptive
* comment and information about the arguments required in order to
* perform the conversion.
* Parameters:
* cvt_code
* The conversion code.
* comment
* Address of a location to return a pointer to a constant
* null-terminated string containing a description of the
* conversion.
* nargs
* Address of an int in which to return the number of arguments
* required from the user in order to perform the conversion (may
* be zero).
* szargs
* Address of an int in which to return the number of arguments
* associated with the conversion. This may be bigger than "nargs"
* if the conversion can pre-calculate useful values on the basis
* of the user-supplied values. Such precalculated values are
* stored after the last user-supplied argument.
* arg
* An array in which to return a pointer to a constant
* null-terminated string for each argument (above) containing a
* description of what each argument represents. This includes both
* user-supplied arguments and pre-calculated values.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to a constant null-terminated string representation of
* the conversion code value supplied. If the code supplied is not
* valid, a NULL pointer will be returned, without error.
* 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: */
const char *result; /* Result pointer to return */
/* Initialise the returned values. */
*comment = NULL;
*nargs = 0;
result = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Test for each valid code value in turn and assign the appropriate
return values. */
switch ( cvt_code ) {
case AST__MJDTOMJD:
*comment = "Convert MJD between offsets";
result = "MJDTOMJD";
*nargs = 2;
*szargs = 3;
arg[ 0 ] = "Input MJD offset";
arg[ 1 ] = "Output MJD offset";
arg[ 2 ] = "Combined offset";
break;
case AST__MJDTOJD:
*comment = "Convert MJD to JD";
result = "MJDTOJD";
*nargs = 2;
*szargs = 3;
arg[ 0 ] = "MJD offset";
arg[ 1 ] = "JD offset";
arg[ 2 ] = "Combined offset";
break;
case AST__JDTOMJD:
*comment = "Convert JD to MJD";
result = "JDTOMJD";
*nargs = 2;
*szargs = 3;
arg[ 0 ] = "JD offset";
arg[ 1 ] = "MJD offset";
arg[ 2 ] = "Combined offset";
break;
case AST__MJDTOBEP:
*comment = "Convert MJD to Besselian epoch";
result = "MJDTOBEP";
*nargs = 2;
*szargs = 4;
arg[ 0 ] = "MJD offset";
arg[ 1 ] = "Besselian epoch offset";
arg[ 2 ] = "Combined forward offset";
arg[ 3 ] = "Combined inverse offset";
break;
case AST__BEPTOMJD:
*comment = "Convert Besselian epoch to MJD";
result = "BEPTOMJD";
*nargs = 2;
*szargs = 4;
arg[ 0 ] = "Besselian epoch offset";
arg[ 1 ] = "MJD offset";
arg[ 2 ] = "Combined forward offset";
arg[ 3 ] = "Combined inverse offset";
break;
case AST__MJDTOJEP:
*comment = "Convert MJD to Julian epoch";
result = "MJDTOJEP";
*nargs = 2;
*szargs = 4;
arg[ 0 ] = "MJD offset";
arg[ 1 ] = "Julian epoch offset";
arg[ 2 ] = "Combined forward offset";
arg[ 3 ] = "Combined inverse offset";
break;
case AST__JEPTOMJD:
*comment = "Convert Julian epoch to MJD";
result = "JEPTOMJD";
*nargs = 2;
*szargs = 4;
arg[ 0 ] = "Julian epoch offset";
arg[ 1 ] = "MJD offset";
arg[ 2 ] = "Combined forward offset";
arg[ 3 ] = "Combined inverse offset";
break;
case AST__TAITOUTC:
*comment = "Convert TAI to UTC";
result = "TAITOUTC";
*nargs = 1;
*szargs = 1;
arg[ 0 ] = "MJD offset";
break;
case AST__UTCTOTAI:
*comment = "Convert UTC to TAI";
result = "UTCTOTAI";
*nargs = 1;
*szargs = 1;
arg[ 0 ] = "MJD offset";
break;
case AST__TAITOTT:
*comment = "Convert TAI to TT";
result = "TAITOTT";
*nargs = 1;
*szargs = 1;
arg[ 0 ] = "MJD offset";
break;
case AST__TTTOTAI:
*comment = "Convert TT to TAI";
result = "TTTOTAI";
*nargs = 1;
*szargs = 1;
arg[ 0 ] = "MJD offset";
break;
case AST__TTTOTDB:
*comment = "Convert TT to TDB";
result = "TTTOTDB";
*nargs = 4;
*szargs = 6;
arg[ 0 ] = "MJD offset";
arg[ 1 ] = "Observer longitude";
arg[ 2 ] = "Observer latitude";
arg[ 3 ] = "Observer altitude";
arg[ 4 ] = "Distance from earth spin axis";
arg[ 5 ] = "Distance north of equatorial plane";
break;
case AST__TDBTOTT:
*comment = "Convert TDB to TT";
result = "TDBTOTT";
*nargs = 4;
*szargs = 6;
arg[ 0 ] = "MJD offset";
arg[ 1 ] = "Observer longitude";
arg[ 2 ] = "Observer latitude";
arg[ 3 ] = "Observer altitude";
arg[ 4 ] = "Distance from earth spin axis";
arg[ 5 ] = "Distance north of equatorial plane";
break;
case AST__TTTOTCG:
*comment = "Convert TT to TCG";
result = "TTTOTCG";
*nargs = 1;
*szargs = 2;
arg[ 0 ] = "MJD offset";
arg[ 1 ] = "TCG offset";
break;
case AST__TCGTOTT:
*comment = "Convert TCG to TT";
result = "TCGTOTT";
*nargs = 1;
*szargs = 2;
arg[ 0 ] = "MJD offset";
arg[ 1 ] = "TCG offset";
break;
case AST__TDBTOTCB:
*comment = "Convert TDB to TCB";
result = "TDBTOTCB";
*nargs = 1;
*szargs = 2;
arg[ 0 ] = "MJD offset";
arg[ 1 ] = "TCB offset";
break;
case AST__TCBTOTDB:
*comment = "Convert TCB to TDB";
result = "TCBTOTDB";
*nargs = 1;
*szargs = 2;
arg[ 0 ] = "MJD offset";
arg[ 1 ] = "TCB offset";
break;
case AST__UTTOGMST:
*comment = "Convert UT to GMST";
result = "UTTOGMST";
*nargs = 1;
*szargs = 1;
arg[ 0 ] = "MJD offset";
break;
case AST__GMSTTOUT:
*comment = "Convert GMST to UT";
result = "GMSTTOUT";
*nargs = 1;
*szargs = 1;
arg[ 0 ] = "MJD offset";
break;
case AST__GMSTTOLMST:
*comment = "Convert GMST to LMST";
result = "GMSTTOLMST";
*nargs = 3;
*szargs = 3;
arg[ 0 ] = "MJD offset";
arg[ 1 ] = "Observer longitude";
arg[ 2 ] = "Observer latitude";
break;
case AST__LMSTTOGMST:
*comment = "Convert LMST to GMST";
result = "LMSTTOGMST";
*nargs = 3;
*szargs = 3;
arg[ 0 ] = "MJD offset";
arg[ 1 ] = "Observer longitude";
arg[ 2 ] = "Observer latitude";
break;
case AST__LASTTOLMST:
*comment = "Convert LAST to LMST";
result = "LASTTOLMST";
*nargs = 3;
*szargs = 3;
arg[ 0 ] = "MJD offset";
arg[ 1 ] = "Observer longitude";
arg[ 2 ] = "Observer latitude";
break;
case AST__LMSTTOLAST:
*comment = "Convert LMST to LAST";
result = "LMSTTOLAST";
*nargs = 3;
*szargs = 3;
arg[ 0 ] = "MJD offset";
arg[ 1 ] = "Observer longitude";
arg[ 2 ] = "Observer latitude";
break;
case AST__UTTOUTC:
*comment = "Convert UT1 to UTC";
result = "UTTOUTC";
*nargs = 1;
*szargs = 1;
arg[ 0 ] = "DUT1";
break;
case AST__UTCTOUT:
*comment = "Convert UTC to UT1";
result = "UTCTOUT";
*nargs = 1;
*szargs = 1;
arg[ 0 ] = "DUT1";
break;
case AST__LTTOUTC:
*comment = "Convert Local Time to UTC";
result = "LTTOUTC";
*nargs = 1;
*szargs = 1;
arg[ 0 ] = "LTOFF";
break;
case AST__UTCTOLT:
*comment = "Convert UTC to Local Time";
result = "UTCTOLT";
*nargs = 1;
*szargs = 1;
arg[ 0 ] = "LTOFF";
break;
}
/* Return the result. */
return result;
}
double astDat_( double in, int forward, int *status ){
/*
*+
* Name:
* Dat
* Purpose:
* Convert between UTC and TAI.
* Type:
* Protected function.
* Synopsis:
* #include "timemap.h"
* double astDat( double in, int forward )
* Class Membership:
* TimeMap member function
* Description:
* This function returns the difference between Coordinated Universal Time
* (UTC) and International Atomic Time (TAI), at a given epoch.
* Parameters:
* in
* UTC date or TAI time (as selected by "forward"), as an absolute
* MJD.
* forward
* If non-zero, "in" should be a UTC value, and the returned value
* is TAI-UTC. If zero, "in" should be a TAI value, and the returned
* value is UTC-TAI.
* Returned Value:
* Either UTC-TAI or TAI-UTC (as indicated by "forward") in units of
* seconds.
* Notes:
* - The UTC is specified to be a date rather than a time to indicate
* that care needs to be taken not to specify an instant which lies
* within a leap second. Though in most cases UTC can include the
* fractional part, correct behaviour on the day of a leap second
* can only be guaranteed up to the end of the second 23:59:59.
* - For epochs from 1961 January 1 onwards, the expressions from the
* file ftp://maia.usno.navy.mil/ser7/tai-utc.dat are used.
* - The 5ms time step at 1961 January 1 is taken from 2.58.1 (p87) of
* the 1992 Explanatory Supplement.
* - UTC began at 1960 January 1.0 (JD 2436934.5) and it is improper
* to call the routine with an earlier epoch. However, if this
* is attempted, the TAI-UTC expression for the year 1960 is used.
* Implementation Details:
* - This function is based on SLA_DAT by P.T.Wallace.
* - This routine must be updated on each occasion that a leap second is
* announced
* - Latest leap second: 2015 July 1
*-
*/
/* Local Variables: */
double result;
/* Initialise the returned value. */
if( in == AST__BAD ) return AST__BAD;
/* First do TAI-UTC at a given UTC
------------------------------- */
if( forward ) {
/* 2015 July 1 */
if ( in >= 57204.0 ) {
result = 36.0;
/* 2012 July 1 */
} else if ( in >= 56109.0 ) {
result = 35.0;
/* 2009 January 1 */
} else if ( in >= 54832.0 ) {
result = 34.0;
/* 2006 January 1 */
} else if( in >= 53736.0 ) {
result = 33.0;
/* 1999 January 1 */
} else if( in >= 51179.0 ){
result = 32.0;
/* 1997 July 1 */
} else if( in >= 50630.0 ){
result = 31.0;
/* 1996 January 1 */
} else if( in >= 50083.0 ){
result = 30.0;
/* 1994 July 1 */
} else if( in >= 49534.0 ){
result = 29.0;
/* 1993 July 1 */
} else if( in >= 49169.0 ){
result = 28.0;
/* 1992 July 1 */
} else if( in >= 48804.0 ){
result = 27.0;
/* 1991 January 1 */
} else if( in >= 48257.0 ){
result = 26.0;
/* 1990 January 1 */
} else if( in >= 47892.0 ){
result = 25.0;
/* 1988 January 1 */
} else if( in >= 47161.0 ){
result = 24.0;
/* 1985 July 1 */
} else if( in >= 46247.0 ){
result = 23.0;
/* 1983 July 1 */
} else if( in >= 45516.0 ){
result = 22.0;
/* 1982 July 1 */
} else if( in >= 45151.0 ){
result = 21.0;
/* 1981 July 1 */
} else if( in >= 44786.0 ){
result = 20.0;
/* 1980 January 1 */
} else if( in >= 44239.0 ){
result = 19.0;
/* 1979 January 1 */
} else if( in >= 43874.0 ){
result = 18.0;
/* 1978 January 1 */
} else if( in >= 43509.0 ){
result = 17.0;
/* 1977 January 1 */
} else if( in >= 43144.0 ){
result = 16.0;
/* 1976 January 1 */
} else if( in >= 42778.0 ){
result = 15.0;
/* 1975 January 1 */
} else if( in >= 42413.0 ){
result = 14.0;
/* 1974 January 1 */
} else if( in >= 42048.0 ){
result = 13.0;
/* 1973 January 1 */
} else if( in >= 41683.0 ){
result = 12.0;
/* 1972 July 1 */
} else if( in >= 41499.0 ){
result = 11.0;
/* 1972 January 1 */
} else if( in >= 41317.0 ){
result = 10.0;
/* 1968 February 1 */
} else if( in >= 39887.0 ){
result = 4.2131700 + ( in - 39126.0 )*0.002592;
/* 1966 January 1 */
} else if( in >= 39126.0 ){
result = 4.3131700 + ( in - 39126.0 )*0.002592;
/* 1965 September 1 */
} else if( in >= 39004.0 ){
result = 3.8401300 + ( in - 38761.0 )*0.001296;
/* 1965 July 1 */
} else if( in >= 38942.0 ){
result = 3.7401300 + ( in - 38761.0 )*0.001296;
/* 1965 March 1 */
} else if( in >= 38820.0 ){
result = 3.6401300 + ( in - 38761.0 )*0.001296;
/* 1965 January 1 */
} else if( in >= 38761.0 ){
result = 3.5401300 + ( in - 38761.0 )*0.001296;
/* 1964 September 1 */
} else if( in >= 38639.0 ){
result = 3.4401300 + ( in - 38761.0 )*0.001296;
/* 1964 April 1 */
} else if( in >= 38486.0 ){
result = 3.3401300 + ( in - 38761.0 )*0.001296;
/* 1964 January 1 */
} else if( in >= 38395.0 ){
result = 3.2401300 + ( in - 38761.0 )*0.001296;
/* 1963 November 1 */
} else if( in >= 38334.0 ){
result = 1.9458580 + ( in - 37665.0 )*0.0011232;
/* 1962 January 1 */
} else if( in >= 37665.0 ){
result = 1.8458580 + ( in - 37665.0 )*0.0011232;
/* 1961 August 1 */
} else if( in >= 37512.0 ){
result = 1.3728180 + ( in - 37300.0 )*0.001296;
/* 1961 January 1 */
} else if( in >= 37300.0 ){
result = 1.4228180 + ( in - 37300.0 )*0.001296;
/* Before that */
} else {
result = 1.4178180 + ( in - 37300.0 )*0.001296;
}
/* Now do UTC-TAI at a given TAI.
------------------------------ */
} else {
/* 2015 July 1 */
if ( in >= 57204.0 + 36.0/SPD ) {
result = -36.0;
/* 2012 July 1 */
} else if( in >= 56109.0 + 35.0/SPD ) {
result = -35.0;
/* 2009 January 1 */
} else if( in >= 54832.0 + 34.0/SPD ) {
result = -34.0;
/* 2006 January 1 */
} else if( in >= 53736.0 + 33.0/SPD ){
result = -33.0;
/* 1999 January 1 */
} else if( in >= 51179.0 + 32.0/SPD ){
result = -32.0;
/* 1997 July 1 */
} else if( in >= 50630.0 + 31.0/SPD ){
result = -31.0;
/* 1996 January 1 */
} else if( in >= 50083.0 + 30.0/SPD ){
result = -30.0;
/* 1994 July 1 */
} else if( in >= 49534.0 + 29.0/SPD ){
result = -29.0;
/* 1993 July 1 */
} else if( in >= 49169.0 + 28.0/SPD ){
result = -28.0;
/* 1992 July 1 */
} else if( in >= 48804.0 + 27.0/SPD ){
result = -27.0;
/* 1991 January 1 */
} else if( in >= 48257.0 + 26.0/SPD ){
result = -26.0;
/* 1990 January 1 */
} else if( in >= 47892.0 + 25.0/SPD ){
result = -25.0;
/* 1988 January 1 */
} else if( in >= 47161.0 + 24.0/SPD ){
result = -24.0;
/* 1985 July 1 */
} else if( in >= 46247.0 + 23.0/SPD ){
result = -23.0;
/* 1983 July 1 */
} else if( in >= 45516.0 + 22.0/SPD ){
result = -22.0;
/* 1982 July 1 */
} else if( in >= 45151.0 + 21.0/SPD ){
result = -21.0;
/* 1981 July 1 */
} else if( in >= 44786.0 + 20.0/SPD ){
result = -20.0;
/* 1980 January 1 */
} else if( in >= 44239.0 + 19.0/SPD ){
result = -19.0;
/* 1979 January 1 */
} else if( in >= 43874.0 + 18.0/SPD ){
result = -18.0;
/* 1978 January 1 */
} else if( in >= 43509.0 + 17.0/SPD ){
result = -17.0;
/* 1977 January 1 */
} else if( in >= 43144.0 + 16.0/SPD ){
result = -16.0;
/* 1976 January 1 */
} else if( in >= 42778.0 + 15.0/SPD ){
result = -15.0;
/* 1975 January 1 */
} else if( in >= 42413.0 + 14.0/SPD ){
result = -14.0;
/* 1974 January 1 */
} else if( in >= 42048.0 + 13.0/SPD ){
result = -13.0;
/* 1973 January 1 */
} else if( in >= 41683.0 + 12.0/SPD ){
result = -12.0;
/* 1972 July 1 */
} else if( in >= 41499.0 + 11.0/SPD ){
result = -11.0;
/* 1972 January 1 */
} else if( in >= 41317.0 + 10.0/SPD ){
result = -10.0;
/* 1968 February 1 */
} else if( in >= 39887.0 + ( 4.2131700
+ ( 39887.0 - 39126.0 )*0.002592 )/SPD ){
result = -( 4.2131700 + ( in - 39126.0 )*0.002592 )/1.02592;
/* 1966 January 1 */
} else if( in >= 39126.0 + ( 4.3131700
+ ( 39126.0 - 39126.0 )*0.002592 )/SPD ){
result = -( 4.2131700 + ( in - 39126.0 )*0.002592 )/1.02592;
/* 1965 September 1 */
} else if( in >= 39004.0 + ( 3.8401300
+ ( 39004.0 - 38761.0 )*0.001296 )/SPD ){
result = -( 3.8401300 + ( in - 38761.0 )*0.001296 )/1.001296;
/* 1965 July 1 */
} else if( in >= 38942.0 + ( 3.7401300
+ ( 38942.0 - 38761.0 )*0.001296 )/SPD ){
result = -( 3.7401300 + ( in - 38761.0 )*0.001296 )/1.01296;
/* 1965 March 1 */
} else if( in >= 38820.0 + ( 3.6401300
+ ( 38820.0 - 38761.0 )*0.001296 )/SPD ){
result = -( 3.6401300 + ( in - 38761.0 )*0.001296 )/1.001296;
/* 1965 January 1 */
} else if( in >= 38761.0 + ( 3.5401300
+ ( 38761.0 - 38761.0 )*0.001296 )/SPD ){
result = -( 3.5401300 + ( in - 38761.0 )*0.001296 )/1.001296;
/* 1964 September 1 */
} else if( in >= 38639.0 + ( 3.4401300
+ ( 38639.0 - 38761.0 )*0.001296 )/SPD ){
result = -( 3.4401300 + ( in - 38761.0 )*0.001296 )/1.001296;
/* 1964 April 1 */
} else if( in >= 38486.0 + ( 3.3401300
+ ( 38486.0 - 38761.0 )*0.001296 )/SPD ){
result = -( 3.3401300 + ( in - 38761.0 )*0.001296 )/1.001296;
/* 1964 January 1 */
} else if( in >= 38395.0 + ( 3.2401300
+ ( 38395.0 - 38761.0 )*0.001296 )/SPD ){
result = -( 3.2401300 + ( in - 38761.0 )*0.001296 )/1.001296;
/* 1963 November 1 */
} else if( in >= 38334.0 + ( 1.9458580
+ ( 38334.0 - 37665.0 )*0.0011232 )/SPD ){
result = -( 1.9458580 + ( in - 37665.0 )*0.0011232 )/1.0011232;
/* 1962 January 1 */
} else if( in >= 37665.0 + ( 1.8458580
+ ( 37665.0 - 37665.0 )*0.0011232 )/SPD ){
result = -( 1.8458580 + ( in - 37665.0 )*0.0011232 )/1.0011232;
/* 1961 August 1 */
} else if( in >= 37512.0 + ( 1.3728180
+ ( 37512.0 - 37300.0 )*0.001296 )/SPD ){
result = -( 1.3728180 + ( in - 37300.0 )*0.001296 )/1.001296;
/* 1961 January 1 */
} else if( in >= 37300.0 + ( 1.4228180
+ ( 37300.0 - 37300.0 )*0.001296 )/SPD ){
result = -( 1.4228180 + ( in - 37300.0 )*0.001296 )/1.001296;
/* Before that */
} else {
result = -( 1.4178180 + ( in - 37300.0 )*0.001296 )/1.001296;
}
}
/* Return the result */
return result;
}
static double Gmsta( double in, double off, int forward, int *status ){
/*
* Name:
* Gmsta
* Purpose:
* Convert between Universal Time (UT) and Greenwich Mean Sidereal Time (GMST).
* Type:
* Private function.
* Synopsis:
* #include "timemap.h"
* double Gmsta( double in, double off, int forward, int *status ){
* Class Membership:
* TimeMap member function
* Description:
* This functions converts between UT and GMST. Both timescales are
* represented by an MJD, rather than as an angle (as is done by SLALIB)
* in order to facilitate conversions from GMST to UT1. This means
* that whole days are retained.
* Parameters:
* in
* The time to convert, represented as an offset in days from the MJD
* zero-point specified by "off". The time is either UT1 or GMST, as
* selected by "forward").
* off
* The MJD value corresponding to a value of 0.0 for "in".
* forward
* If non-zero, "in" should be a UT1 value, and the returned value
* is GMST. If zero, "in" should be a GMST value, and the returned
* value is UT1.
* status
* Pointer to the inherited status variable.
* Returned Value:
* An offset in days from the MJD given by "off". When the returned
* value is added to "off" the sum is a GMST MJD (if "forward" is
* non-zero), or a UT1 MJD (if "forward" is zero),
* Notes:
* - This function is based on SLA_GMST by P.T.Wallace.
*/
/* Local Variables: */
double dgdu;
double g;
double result;
double t;
double utl;
int nit;
/* Initialise the returned value. */
if( in == AST__BAD || off == AST__BAD ) return AST__BAD;
/* First deal with UT1 -> GMST
--------------------------- */
if( forward ) {
/* Julian centuries since J2000. */
t = ( off + in - 51544.5 )/36525.0;
/* GMST at this UT1. */
result = in + ( 24110.54841 + ( 8640184.812866 + ( 0.093104 -
6.2E-6*t )*t )*t )/86400.0;
/* Now deal with GMST -> UT1
----------------------- */
} else {
/* Form an initial guess at the UT1 value using the inverse of a linear
approximation to the UT1->GMST equation. */
result = 0.996997348638869*in + 154.49194372222 - 0.00300265136113098*off;
/* Loop round improving the guess, until the guess stops changing, or 10
iterations have been performed. */
utl = AST__BAD;
nit = 0;
while( result != utl && nit++ < 10 ){
/* Calculate the GMST at the current UT1 guess. */
t = ( off + result - 51544.5 )/36525.0;
g = result + ( 24110.54841 + ( 8640184.812866 + ( 0.093104 -
6.2E-6*t )*t )*t )/86400.0;
/* Calculate the rate of change of GMST with respect to UT1 at the current
UT1 guess. */
dgdu = 1.0 + ( 8640184.812866 +
( 0.186208 - 12.4E-6*t )*t)/(36525.0*86400.0);
/* Improve the UT1 guess. */
utl = result;
result = result - ( g - in )/dgdu;
}
}
/* Return the result */
return result;
}
void astInitTimeMapVtab_( AstTimeMapVtab *vtab, const char *name, int *status ) {
/*
*+
* Name:
* astInitTimeMapVtab
* Purpose:
* Initialise a virtual function table for a TimeMap.
* Type:
* Protected function.
* Synopsis:
* #include "timemap.h"
* void astInitTimeMapVtab( AstTimeMapVtab *vtab, const char *name )
* Class Membership:
* TimeMap vtab initialiser.
* Description:
* This function initialises the component of a virtual function
* table which is used by the TimeMap 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 */
AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */
AstObjectVtab *object; /* Pointer to Object 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. */
astInitMappingVtab( (AstMappingVtab *) vtab, name );
/* Store a unique "magic" value in the virtual function table. This
will be used (by astIsATimeMap) 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 = &(((AstMappingVtab *) vtab)->id);
/* Initialise member function pointers. */
/* ------------------------------------ */
/* Store pointers to the member functions (implemented here) that
provide virtual methods for this class. */
vtab->TimeAdd = TimeAdd;
/* Save the inherited pointers to methods that will be extended, and
replace them with pointers to the new member functions. */
object = (AstObjectVtab *) vtab;
mapping = (AstMappingVtab *) vtab;
parent_getobjsize = object->GetObjSize;
object->GetObjSize = GetObjSize;
parent_transform = mapping->Transform;
mapping->Transform = Transform;
parent_rate = mapping->Rate;
mapping->Rate = Rate;
/* Store replacement pointers for methods which will be over-ridden by
new member functions implemented here. */
object->Equal = Equal;
mapping->MapMerge = MapMerge;
/* Declare the copy constructor, destructor and class dump
function. */
astSetCopy( vtab, Copy );
astSetDelete( vtab, Delete );
astSetDump( vtab, Dump, "TimeMap",
"Conversion between time coordinate systems" );
/* 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 int MapMerge( AstMapping *this, int where, int series, int *nmap,
AstMapping ***map_list, int **invert_list, int *status ) {
/*
* Name:
* MapMerge
* Purpose:
* Simplify a sequence of Mappings containing a TimeMap.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h
* int MapMerge( AstMapping *this, int where, int series, int *nmap,
* AstMapping ***map_list, int **invert_list, int *status )
* Class Membership:
* TimeMap method (over-rides the protected astMapMerge method
* inherited from the Mapping class).
* Description:
* This function attempts to simplify a sequence of Mappings by
* merging a nominated TimeMap in the sequence with its neighbours,
* so as to shorten the sequence if possible.
*
* In many cases, simplification will not be possible and the
* function will return -1 to indicate this, without further
* action.
*
* In most cases of interest, however, this function will either
* attempt to replace the nominated TimeMap with one which it
* considers simpler, or to merge it with the Mappings which
* immediately precede it or follow it in the sequence (both will
* normally be considered). This is sufficient to ensure the
* eventual simplification of most Mapping sequences by repeated
* application of this function.
*
* In some cases, the function may attempt more elaborate
* simplification, involving any number of other Mappings in the
* sequence. It is not restricted in the type or scope of
* simplification it may perform, but will normally only attempt
* elaborate simplification in cases where a more straightforward
* approach is not adequate.
* Parameters:
* this
* Pointer to the nominated TimeMap which is to be merged with
* its neighbours. This should be a cloned copy of the TimeMap
* pointer contained in the array element "(*map_list)[where]"
* (see below). This pointer will not be annulled, and the
* TimeMap it identifies will not be modified by this function.
* where
* Index in the "*map_list" array (below) at which the pointer
* to the nominated TimeMap resides.
* series
* A non-zero value indicates that the sequence of Mappings to
* be simplified will be applied in series (i.e. one after the
* other), whereas a zero value indicates that they will be
* applied in parallel (i.e. on successive sub-sets of the
* input/output coordinates).
* nmap
* Address of an int which counts the number of Mappings in the
* sequence. On entry this should be set to the initial number
* of Mappings. On exit it will be updated to record the number
* of Mappings remaining after simplification.
* map_list
* Address of a pointer to a dynamically allocated array of
* Mapping pointers (produced, for example, by the astMapList
* method) which identifies the sequence of Mappings. On entry,
* the initial sequence of Mappings to be simplified should be
* supplied.
*
* On exit, the contents of this array will be modified to
* reflect any simplification carried out. Any form of
* simplification may be performed. This may involve any of: (a)
* removing Mappings by annulling any of the pointers supplied,
* (b) replacing them with pointers to new Mappings, (c)
* inserting additional Mappings and (d) changing their order.
*
* The intention is to reduce the number of Mappings in the
* sequence, if possible, and any reduction will be reflected in
* the value of "*nmap" returned. However, simplifications which
* do not reduce the length of the sequence (but improve its
* execution time, for example) may also be performed, and the
* sequence might conceivably increase in length (but normally
* only in order to split up a Mapping into pieces that can be
* more easily merged with their neighbours on subsequent
* invocations of this function).
*
* If Mappings are removed from the sequence, any gaps that
* remain will be closed up, by moving subsequent Mapping
* pointers along in the array, so that vacated elements occur
* at the end. If the sequence increases in length, the array
* will be extended (and its pointer updated) if necessary to
* accommodate any new elements.
*
* Note that any (or all) of the Mapping pointers supplied in
* this array may be annulled by this function, but the Mappings
* to which they refer are not modified in any way (although
* they may, of course, be deleted if the annulled pointer is
* the final one).
* invert_list
* Address of a pointer to a dynamically allocated array which,
* on entry, should contain values to be assigned to the Invert
* attributes of the Mappings identified in the "*map_list"
* array before they are applied (this array might have been
* produced, for example, by the astMapList method). These
* values will be used by this function instead of the actual
* Invert attributes of the Mappings supplied, which are
* ignored.
*
* On exit, the contents of this array will be updated to
* correspond with the possibly modified contents of the
* "*map_list" array. If the Mapping sequence increases in
* length, the "*invert_list" array will be extended (and its
* pointer updated) if necessary to accommodate any new
* elements.
* status
* Pointer to the inherited status variable.
* Returned Value:
* If simplification was possible, the function returns the index
* in the "map_list" array of the first element which was
* modified. Otherwise, it returns -1 (and makes no changes to the
* arrays supplied).
* Notes:
* - A value of -1 will be returned if this function is invoked
* with the global error status set, or if it should fail for any
* reason.
*/
/* Local Variables: */
AstMapping *new; /* Pointer to replacement Mapping */
AstTimeMap *timemap; /* Pointer to TimeMap */
const char *argdesc[ MAX_ARGS ]; /* Argument descriptions (junk) */
const char *class; /* Pointer to Mapping class string */
const char *comment; /* Pointer to comment string (junk) */
double (*cvtargs)[ MAX_ARGS ]; /* Pointer to argument arrays */
double tmp; /* Temporary storage */
int *cvttype; /* Pointer to transformation type codes */
int *szarg; /* Pointer to argument count array */
int done; /* Finished (no further simplification)? */
int iarg; /* Loop counter for arguments */
int icvt1; /* Loop initial value */
int icvt2; /* Loop final value */
int icvt; /* Loop counter for transformation steps */
int ikeep; /* Index to store step being kept */
int imap1; /* Index of first TimeMap to merge */
int imap2; /* Index of last TimeMap to merge */
int imap; /* Loop counter for Mappings */
int inc; /* Increment for transformation step loop */
int invert; /* TimeMap applied in inverse direction? */
int istep; /* Loop counter for transformation steps */
int keep; /* Keep transformation step? */
int narg; /* Number of user-supplied arguments */
int ngone; /* Number of Mappings eliminated */
int nstep0; /* Original number of transformation steps */
int nstep; /* Total number of transformation steps */
int result; /* Result value to return */
int simpler; /* Simplification possible? */
int unit; /* Replacement Mapping is a UnitMap? */
/* Initialise. */
result = -1;
/* Check the global error status. */
if ( !astOK ) return result;
/* TimeMaps can only be merged if they are in series (or if there is
only one Mapping present, in which case it makes no difference), so
do nothing if they are not. */
if ( series || ( *nmap == 1 ) ) {
/* Initialise the number of transformation steps to be merged to equal
the number in the nominated TimeMap. */
nstep = ( (AstTimeMap *) ( *map_list )[ where ] )->ncvt;
/* Search adjacent lower-numbered Mappings until one is found which is
not a TimeMap. Accumulate the number of transformation steps involved in
any TimeMaps found. */
imap1 = where;
while ( ( imap1 - 1 >= 0 ) && astOK ) {
class = astGetClass( ( *map_list )[ imap1 - 1 ] );
if ( !astOK || strcmp( class, "TimeMap" ) ) break;
nstep += ( (AstTimeMap *) ( *map_list )[ imap1 - 1 ] )->ncvt;
imap1--;
}
/* Similarly search adjacent higher-numbered Mappings. */
imap2 = where;
while ( ( imap2 + 1 < *nmap ) && astOK ) {
class = astGetClass( ( *map_list )[ imap2 + 1 ] );
if ( !astOK || strcmp( class, "TimeMap" ) ) break;
nstep += ( (AstTimeMap *) ( *map_list )[ imap2 + 1 ] )->ncvt;
imap2++;
}
/* Remember the initial number of transformation steps. */
nstep0 = nstep;
/* Allocate memory for accumulating a list of all the transformation
steps involved in all the TimeMaps found. */
cvttype = astMalloc( sizeof( int ) * (size_t) nstep );
cvtargs = astMalloc( sizeof( double[ MAX_ARGS ] ) * (size_t) nstep );
szarg = astMalloc( sizeof( int ) * (size_t) nstep );
/* Loop to obtain the transformation data for each TimeMap being merged. */
nstep = 0;
for ( imap = imap1; astOK && ( imap <= imap2 ); imap++ ) {
/* Obtain a pointer to the TimeMap and note if it is being applied in
its inverse direction. */
timemap = (AstTimeMap *) ( *map_list )[ imap ];
invert = ( *invert_list )[ imap ];
/* Set up loop limits and an increment to scan the transformation
steps in each TimeMap in either the forward or reverse direction, as
dictated by the associated "invert" value. */
icvt1 = invert ? timemap->ncvt - 1 : 0;
icvt2 = invert ? -1 : timemap->ncvt;
inc = invert ? -1 : 1;
/* Loop through each transformation step in the TimeMap. */
for ( icvt = icvt1; icvt != icvt2; icvt += inc ) {
/* Store the transformation type code and use "CvtString" to determine
the associated number of arguments. Then store these arguments. */
cvttype[ nstep ] = timemap->cvttype[ icvt ];
(void) CvtString( cvttype[ nstep ], &comment,
&narg, szarg + nstep, argdesc, status );
if ( !astOK ) break;
for ( iarg = 0; iarg < szarg[ nstep ]; iarg++ ) {
cvtargs[ nstep ][ iarg ] = timemap->cvtargs[ icvt ][ iarg ];
}
/* If the TimeMap is inverted, we must not only accumulate its
transformation steps in reverse, but also apply them in
reverse. For some steps this means changing arguments, for some it
means changing the transformation type code to a complementary
value, and for others it means both. Define macros to perform each
of the required changes. */
/* Macro to exchange a transformation type code for its inverse (and
vice versa). */
#define SWAP_CODES( code1, code2 ) \
if ( cvttype[ nstep ] == code1 ) { \
cvttype[ nstep ] = code2; \
AddArgs( code2, cvtargs[ nstep ], status ); \
} else if ( cvttype[ nstep ] == code2 ) { \
cvttype[ nstep ] = code1; \
AddArgs( code1, cvtargs[ nstep ], status ); \
}
/* Macro to exchange a transformation type code for its inverse (and
vice versa), and swap the order of its 2 arguments. */
#define SWAP_CODES2( code1, code2 ) \
if ( cvttype[ nstep ] == code1 ) { \
cvttype[ nstep ] = code2; \
tmp = cvtargs[ nstep ][ 0 ]; \
cvtargs[ nstep ][ 0 ] = cvtargs[ nstep ][ 1 ]; \
cvtargs[ nstep ][ 1 ] = tmp; \
AddArgs( cvttype[ nstep ], cvtargs[ nstep ], status ); \
} else if ( cvttype[ nstep ] == code2 ) { \
cvttype[ nstep ] = code1; \
tmp = cvtargs[ nstep ][ 0 ]; \
cvtargs[ nstep ][ 0 ] = cvtargs[ nstep ][ 1 ]; \
cvtargs[ nstep ][ 1 ] = tmp; \
AddArgs( cvttype[ nstep ], cvtargs[ nstep ], status ); \
}
/* Use these macros to apply the changes where needed. */
if ( invert ) {
/* Exchange transformation codes for their inverses. */
SWAP_CODES( AST__TAITOUTC, AST__UTCTOTAI )
SWAP_CODES( AST__TAITOTT, AST__TTTOTAI )
SWAP_CODES( AST__TTTOTDB, AST__TDBTOTT )
SWAP_CODES( AST__TDBTOTCB, AST__TCBTOTDB )
SWAP_CODES( AST__TTTOTCG, AST__TCGTOTT )
SWAP_CODES( AST__UTTOGMST, AST__GMSTTOUT )
SWAP_CODES( AST__GMSTTOLMST, AST__LMSTTOGMST )
SWAP_CODES( AST__LASTTOLMST, AST__LMSTTOLAST )
SWAP_CODES( AST__UTTOUTC, AST__UTCTOUT )
SWAP_CODES( AST__LTTOUTC, AST__UTCTOLT )
/* Exchange transformation codes for their inverses, and swap the offset
values. */
SWAP_CODES2( AST__MJDTOMJD, AST__MJDTOMJD )
SWAP_CODES2( AST__MJDTOJD, AST__JDTOMJD )
SWAP_CODES2( AST__MJDTOBEP, AST__BEPTOMJD )
SWAP_CODES2( AST__MJDTOJEP, AST__JEPTOMJD )
}
/* Undefine the local macros. */
#undef SWAP_CODES
#undef SWAP_CODES2
/* Count the transformation steps. */
nstep++;
}
}
/* Loop to simplify the sequence of transformation steps until no
further improvement is possible. */
done = 0;
while ( astOK && !done ) {
/* Examine each remaining transformation step in turn. */
ikeep = -1;
for ( istep = 0; istep < nstep; istep++ ) {
/* Initially assume we will retain the current step. */
keep = 1;
/* We can eliminate changes of system which have no effect. */
if( ( cvttype[ istep ] == AST__MJDTOMJD ||
cvttype[ istep ] == AST__MJDTOJD ||
cvttype[ istep ] == AST__JDTOMJD ) &&
cvtargs[ istep ][ 2 ] == 0.0 ) {
keep = 0;
/* The only simplifications for the conversions currently in this class act
to combine adjacent transformation steps, so only apply them while there
are at least 2 steps left. */
} else if ( istep < ( nstep - 1 ) ) {
/* Define a macro to test if two adjacent transformation type codes
have specified values. */
#define PAIR_CVT( code1, code2 ) \
( ( cvttype[ istep ] == code1 ) && \
( cvttype[ istep + 1 ] == code2 ) )
/* Define a macro to test if two adjacent transformation type codes
have specified values, either way round. */
#define PAIR_CVT2( code1, code2 ) \
( ( PAIR_CVT( code1, code2 ) ) || \
( PAIR_CVT( code2, code1 ) ) )
/* If a correction is followed by its inverse, and the user-supplied argument
values are unchanged (we do not need to test values stored in the
argument array which were not supplied by the user), we can eliminate them.
First check for conversions which have a single user-supplied argument. */
if( ( PAIR_CVT2( AST__TAITOUTC, AST__UTCTOTAI ) ||
PAIR_CVT2( AST__TAITOTT, AST__TTTOTAI ) ||
PAIR_CVT2( AST__UTTOGMST, AST__GMSTTOUT ) ||
PAIR_CVT2( AST__TTTOTCG, AST__TCGTOTT ) ||
PAIR_CVT2( AST__TTTOTCG, AST__TCGTOTT ) ||
PAIR_CVT2( AST__UTTOUTC, AST__UTCTOUT ) ||
PAIR_CVT2( AST__LTTOUTC, AST__UTCTOLT ) ) &&
EQUAL( cvtargs[ istep ][ 0 ],
cvtargs[ istep + 1 ][ 0 ] ) ) {
istep++;
keep = 0;
/* Now check for conversions which have two user-supplied arguments
(test they are swapped). */
} else if( ( PAIR_CVT2( AST__MJDTOJD, AST__JDTOMJD ) ||
PAIR_CVT2( AST__MJDTOMJD, AST__MJDTOMJD ) ||
PAIR_CVT2( AST__MJDTOBEP, AST__BEPTOMJD ) ||
PAIR_CVT2( AST__MJDTOJEP, AST__JEPTOMJD ) ) &&
EQUAL( cvtargs[ istep ][ 0 ],
cvtargs[ istep + 1 ][ 1 ] ) &&
EQUAL( cvtargs[ istep ][ 1 ],
cvtargs[ istep + 1 ][ 0 ] ) ) {
istep++;
keep = 0;
/* Now check for conversions which have three user-supplied arguments. */
} else if( ( PAIR_CVT2( AST__TDBTOTCB, AST__TCBTOTDB ) ||
PAIR_CVT2( AST__GMSTTOLMST, AST__LMSTTOGMST ) ||
PAIR_CVT2( AST__LASTTOLMST, AST__LMSTTOLAST ) ) &&
EQUAL( cvtargs[ istep ][ 0 ],
cvtargs[ istep + 1 ][ 0 ] ) &&
EQUAL( cvtargs[ istep ][ 1 ],
cvtargs[ istep + 1 ][ 1 ] ) &&
EQUAL( cvtargs[ istep ][ 2 ],
cvtargs[ istep + 1 ][ 2 ] ) ) {
istep++;
keep = 0;
/* Now check for conversions which have four user-supplied arguments. */
} else if( ( PAIR_CVT2( AST__TTTOTDB, AST__TDBTOTT ) ) &&
EQUAL( cvtargs[ istep ][ 0 ],
cvtargs[ istep + 1 ][ 0 ] ) &&
EQUAL( cvtargs[ istep ][ 1 ],
cvtargs[ istep + 1 ][ 1 ] ) &&
EQUAL( cvtargs[ istep ][ 2 ],
cvtargs[ istep + 1 ][ 2 ] ) &&
EQUAL( cvtargs[ istep ][ 3 ],
cvtargs[ istep + 1 ][ 3 ] ) ) {
istep++;
keep = 0;
}
/* Undefine the local macros. */
#undef PAIR_CVT
#undef PAIR_CVT2
}
/* If the current transformation (possibly modified above) is being
kept, then increment the index that identifies its new location in
the list of transformation steps. */
if ( keep ) {
ikeep++;
/* If the new location is different to its current location, copy the
transformation data into the new location. */
if ( ikeep != istep ) {
cvttype[ ikeep ] = cvttype[ istep ];
for ( iarg = 0; iarg < szarg[ istep ]; iarg++ ) {
cvtargs[ ikeep ][ iarg ] = cvtargs[ istep ][ iarg ];
}
szarg[ ikeep ] = szarg[ istep ];
}
}
}
/* Note if no simplification was achieved on this iteration (i.e. the
number of transformation steps was not reduced). This is the signal
to quit. */
done = ( ( ikeep + 1 ) >= nstep );
/* Note how many transformation steps now remain. */
nstep = ikeep + 1;
}
/* Determine how many Mappings can be eliminated by condensing all
those considered above into a single Mapping. */
if ( astOK ) {
ngone = imap2 - imap1;
/* Determine if the replacement Mapping can be a UnitMap (a null
Mapping). This will only be the case if all the transformation
steps were eliminated above. */
unit = ( nstep == 0 );
/* Determine if simplification is possible. This will be the case if
(a) Mappings were eliminated ("ngone" is non-zero), or (b) the
number of transformation steps was reduced, or (c) the TimeMap(s)
can be replaced by a UnitMap, or (d) if there was initially only
one TimeMap present, its invert flag was set (this flag will always
be cleared in the replacement Mapping). */
simpler = ngone || ( nstep < nstep0 ) || unit ||
( *invert_list )[ where ];
/* Do nothing more unless simplification is possible. */
if ( simpler ) {
/* If the replacement Mapping is a UnitMap, then create it. */
if ( unit ) {
new = (AstMapping *)
astUnitMap( astGetNin( ( *map_list )[ where ] ), "", status );
/* Otherwise, create a replacement TimeMap and add each of the
remaining transformation steps to it. */
} else {
new = (AstMapping *) astTimeMap( 0, "", status );
for ( istep = 0; istep < nstep; istep++ ) {
AddTimeCvt( (AstTimeMap *) new, cvttype[ istep ],
cvtargs[ istep ], status );
}
}
/* Annul the pointers to the Mappings being eliminated. */
if ( astOK ) {
for ( imap = imap1; imap <= imap2; imap++ ) {
( *map_list )[ imap ] = astAnnul( ( *map_list )[ imap ] );
}
/* Insert the pointer and invert value for the new Mapping. */
( *map_list )[ imap1 ] = new;
( *invert_list )[ imap1 ] = 0;
/* Move any subsequent Mapping information down to close the gap. */
for ( imap = imap2 + 1; imap < *nmap; imap++ ) {
( *map_list )[ imap - ngone ] = ( *map_list )[ imap ];
( *invert_list )[ imap - ngone ] = ( *invert_list )[ imap ];
}
/* Blank out any information remaining at the end of the arrays. */
for ( imap = ( *nmap - ngone ); imap < *nmap; imap++ ) {
( *map_list )[ imap ] = NULL;
( *invert_list )[ imap ] = 0;
}
/* Decrement the Mapping count and return the index of the first
Mapping which was eliminated. */
( *nmap ) -= ngone;
result = imap1;
/* If an error occurred, annul the new Mapping pointer. */
} else {
new = astAnnul( new );
}
}
}
/* Free the memory used for the transformation steps. */
cvttype = astFree( cvttype );
cvtargs = astFree( cvtargs );
szarg = astFree( szarg );
}
/* If an error occurred, clear the returned value. */
if ( !astOK ) result = -1;
/* Return the result. */
return result;
}
static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){
/*
* Name:
* Rate
* Purpose:
* Calculate the rate of change of a Mapping output.
* Type:
* Private function.
* Synopsis:
* #include "timemap.h"
* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status )
* Class Membership:
* TimeMap member function (overrides the astRate method inherited
* from the Mapping class ).
* Description:
* This function returns the rate of change of a specified output of
* the supplied Mapping with respect to a specified input, at a
* specified input position.
* Parameters:
* this
* Pointer to the Mapping to be applied.
* at
* The address of an array holding the axis values at the position
* at which the rate of change is to be evaluated. The number of
* elements in this array should equal the number of inputs to the
* Mapping.
* ax1
* The index of the Mapping output for which the rate of change is to
* be found (output numbering starts at 0 for the first output).
* ax2
* The index of the Mapping input which is to be varied in order to
* find the rate of change (input numbering starts at 0 for the first
* input).
* status
* Pointer to the inherited status variable.
* Returned Value:
* The rate of change of Mapping output "ax1" with respect to input
* "ax2", evaluated at "at", or AST__BAD if the value cannot be
* calculated.
* Implementation Deficiencies:
* The initial version of this implementation only deals with
* frequency->wavelength conversions. This is because the slowness of
* the numerical differentiation implemented by the astRate method in
* the parent Mapping class is cripples conversion between SpecFluxFrames.
* Such conversions only rely on rate of change of wavelength with
* respect to frequency. This implementation should be extended when
* needed.
*/
/* Local Variables: */
AstTimeMap *map;
double result;
int cvt;
int i;
/* Check inherited status */
if( !astOK ) return AST__BAD;
/* Get a pointer to the TimeMap structure. */
map = (AstTimeMap *) this;
/* Initialise the returned value. */
result = 1.0;
/* Loop round each conversion. */
for( i = 0; i < map->ncvt; i++ ) {
/* Store the type of the current conversion.*/
cvt = map->cvttype[ i ];
/* Many of the time conversions are linear. If this is the case, multiply
the total rate of change by the rate of change for this step. */
if( cvt == AST__MJDTOBEP ) {
result *= 1.0/365.242198781;
} else if( cvt == AST__BEPTOMJD ) {
result *= 365.242198781;
} else if( cvt == AST__MJDTOJEP ) {
result *= 1.0/365.25;
} else if( cvt == AST__JEPTOMJD ) {
result *= 365.25;
/* The GMST scales is not linear, so break if we encounter it, and use the
(numerical) parent astRate method. The other time scale conversions are
assumed to have a slope of unity. In fact the slope will be ever so
slightly different to unity. */
} else if( cvt == AST__UTTOGMST || cvt == AST__GMSTTOUT ) {
result = AST__BAD;
break;
}
}
/* If this is non-linear TimeMap, use the astRate method inherited from the
parent Mapping class. */
if( result == AST__BAD ) result = (*parent_rate)( this, at, ax1, ax2, status );
/* Return the result. */
return result;
}
static double Rcc( double tdb, double ut1, double wl, double u, double v, int *status ){
/*
* Name:
* Rcc
* Purpose:
* Find difference between TDB and TT.
* Type:
* Private function.
* Synopsis:
* #include "timemap.h"
* double Rcc( double tdb, double ut1, double wl, double u, double v, int *status )
* Class Membership:
* TimeMap member function
* Description:
* Relativistic clock correction: the difference between proper time at
* a point on the surface of the Earth and coordinate time in the Solar
* System barycentric space-time frame of reference.
*
* The proper time is terrestrial time, TT; the coordinate time is an
* implementation of barycentric dynamical time, TDB.
* Parameters:
* tdb
* TDB as an MJD.
* ut1
* Universal time (only the fraction of the day is relevant)
* wl
* Observer longitude (radians west)
* u
* Observer distance from Earth spin axis (km)
* v
* Observer distance north of Earth equatorial plane (km)
* status
* Pointer to the inherited status variable.
* Returned Value:
* The clock correction, TDB-TT, in seconds. TDB is coordinate time in the
* solar system barycentre frame of reference, in units chosen to eliminate
* the scale difference with respect to terrestrial time. TT is the proper
* time for clocks at mean sea level on the Earth.
* Notes:
* - This function is a translation of the fortran routine SLA_RCC
* written by by P.T.Wallace.
*
* - The argument TDB is, strictly, the barycentric coordinate time;
* however, the terrestrial time TT can in practice be used without
* any significant loss of accuracy.
*
* - The result returned by Rcc comprises a main (annual)
* sinusoidal term of amplitude approximately 0.00166 seconds, plus
* planetary and lunar terms up to about 20 microseconds, and diurnal
* terms up to 2 microseconds. The variation arises from the
* transverse Doppler effect and the gravitational red-shift as the
* observer varies in speed and moves through different gravitational
* potentials.
*
* - The geocentric model is that of Fairhead & Bretagnon (1990), in
* its full form. It was supplied by Fairhead (private
* communication) as a FORTRAN subroutine. The original Fairhead
* routine used explicit formulae, in such large numbers that
* problems were experienced with certain compilers (Microsoft
* Fortran on PC aborted with stack overflow, Convex compiled
* successfully but extremely slowly). The present implementation is
* a complete recoding, with the original Fairhead coefficients held
* in a table. To optimise arithmetic precision, the terms are
* accumulated in reverse order, smallest first. A number of other
* coding changes were made, in order to match the calling sequence
* of previous versions of the present routine, and to comply with
* Starlink programming standards. The numerical results compared
* with those from the Fairhead form are essentially unaffected by
* the changes, the differences being at the 10^-20 sec level.
*
* - The topocentric part of the model is from Moyer (1981) and
* Murray (1983). It is an approximation to the expression
* ( v / c ) . ( r / c ), where v is the barycentric velocity of
* the Earth, r is the geocentric position of the observer and
* c is the speed of light.
*
* - During the interval 1950-2050, the absolute accuracy of is better
* than +/- 3 nanoseconds relative to direct numerical integrations
* using the JPL DE200/LE200 solar system ephemeris.
*
* - The IAU definition of TDB was that it must differ from TT only by
* periodic terms. Though practical, this is an imprecise definition
* which ignores the existence of very long-period and secular
* effects in the dynamics of the solar system. As a consequence,
* different implementations of TDB will, in general, differ in zero-
* point and will drift linearly relative to one other.
*
* - TDB was, in principle, superseded by new coordinate timescales
* which the IAU introduced in 1991: geocentric coordinate time,
* TCG, and barycentric coordinate time, TCB. However, Rcc
* can be used to implement the periodic part of TCB-TCG.
* References:
* - Fairhead, L., & Bretagnon, P., Astron.Astrophys., 229, 240-247
* (1990).
*
* - Moyer, T.D., Cel.Mech., 23, 33 (1981).
*
* - Murray, C.A., Vectorial Astrometry, Adam Hilger (1983).
*
* - Seidelmann, P.K. et al, Explanatory Supplement to the
* Astronomical Almanac, Chapter 2, University Science Books
* (1992).
*
* - Simon J.L., Bretagnon P., Chapront J., Chapront-Touze M.,
* Francou G. & Laskar J., Astron.Astrophys., 282, 663-683 (1994).
*/
/* -----------------------------------------------------------------------
*
* Fairhead and Bretagnon canonical coefficients
*
* 787 sets of three coefficients.
*
* Each set is amplitude (microseconds)
* frequency (radians per Julian millennium since J2000),
* phase (radians).
*
* Sets 0-473 are the T**0 terms,
* " 474-678 " " T**1 "
* " 679-763 " " T**2 "
* " 764-783 " " T**3 "
* " 784-786 " " T**4 " .
*/
static double fairhd[ 787 ][ 3 ] = {
{ 1656.674564E-6, 6283.075849991, 6.240054195},
{ 22.417471E-6, 5753.384884897, 4.296977442},
{ 13.839792E-6, 12566.151699983, 6.196904410},
{ 4.770086E-6, 529.690965095, 0.444401603},
{ 4.676740E-6, 6069.776754553, 4.021195093},
{ 2.256707E-6, 213.299095438, 5.543113262},
{ 1.694205E-6, -3.523118349, 5.025132748},
{ 1.554905E-6, 77713.771467920, 5.198467090},
{ 1.276839E-6, 7860.419392439, 5.988822341},
{ 1.193379E-6, 5223.693919802, 3.649823730},
{ 1.115322E-6, 3930.209696220, 1.422745069},
{ 0.794185E-6, 11506.769769794, 2.322313077},
{ 0.447061E-6, 26.298319800, 3.615796498},
{ 0.435206E-6, -398.149003408, 4.349338347},
{ 0.600309E-6, 1577.343542448, 2.678271909},
{ 0.496817E-6, 6208.294251424, 5.696701824},
{ 0.486306E-6, 5884.926846583, 0.520007179},
{ 0.432392E-6, 74.781598567, 2.435898309},
{ 0.468597E-6, 6244.942814354, 5.866398759},
{ 0.375510E-6, 5507.553238667, 4.103476804},
{ 0.243085E-6, -775.522611324, 3.651837925},
{ 0.173435E-6, 18849.227549974, 6.153743485},
{ 0.230685E-6, 5856.477659115, 4.773852582},
{ 0.203747E-6, 12036.460734888, 4.333987818},
{ 0.143935E-6, -796.298006816, 5.957517795},
{ 0.159080E-6, 10977.078804699, 1.890075226},
{ 0.119979E-6, 38.133035638, 4.551585768},
{ 0.118971E-6, 5486.777843175, 1.914547226},
{ 0.116120E-6, 1059.381930189, 0.873504123},
{ 0.137927E-6, 11790.629088659, 1.135934669},
{ 0.098358E-6, 2544.314419883, 0.092793886},
{ 0.101868E-6, -5573.142801634, 5.984503847},
{ 0.080164E-6, 206.185548437, 2.095377709},
{ 0.079645E-6, 4694.002954708, 2.949233637},
{ 0.062617E-6, 20.775395492, 2.654394814},
{ 0.075019E-6, 2942.463423292, 4.980931759},
{ 0.064397E-6, 5746.271337896, 1.280308748},
{ 0.063814E-6, 5760.498431898, 4.167901731},
{ 0.048042E-6, 2146.165416475, 1.495846011},
{ 0.048373E-6, 155.420399434, 2.251573730},
{ 0.058844E-6, 426.598190876, 4.839650148},
{ 0.046551E-6, -0.980321068, 0.921573539},
{ 0.054139E-6, 17260.154654690, 3.411091093},
{ 0.042411E-6, 6275.962302991, 2.869567043},
{ 0.040184E-6, -7.113547001, 3.565975565},
{ 0.036564E-6, 5088.628839767, 3.324679049},
{ 0.040759E-6, 12352.852604545, 3.981496998},
{ 0.036507E-6, 801.820931124, 6.248866009},
{ 0.036955E-6, 3154.687084896, 5.071801441},
{ 0.042732E-6, 632.783739313, 5.720622217},
{ 0.042560E-6, 161000.685737473, 1.270837679},
{ 0.040480E-6, 15720.838784878, 2.546610123},
{ 0.028244E-6, -6286.598968340, 5.069663519},
{ 0.033477E-6, 6062.663207553, 4.144987272},
{ 0.034867E-6, 522.577418094, 5.210064075},
{ 0.032438E-6, 6076.890301554, 0.749317412},
{ 0.030215E-6, 7084.896781115, 3.389610345},
{ 0.029247E-6, -71430.695617928, 4.183178762},
{ 0.033529E-6, 9437.762934887, 2.404714239},
{ 0.032423E-6, 8827.390269875, 5.541473556},
{ 0.027567E-6, 6279.552731642, 5.040846034},
{ 0.029862E-6, 12139.553509107, 1.770181024},
{ 0.022509E-6, 10447.387839604, 1.460726241},
{ 0.020937E-6, 8429.241266467, 0.652303414},
{ 0.020322E-6, 419.484643875, 3.735430632},
{ 0.024816E-6, -1194.447010225, 1.087136918},
{ 0.025196E-6, 1748.016413067, 2.901883301},
{ 0.021691E-6, 14143.495242431, 5.952658009},
{ 0.017673E-6, 6812.766815086, 3.186129845},
{ 0.022567E-6, 6133.512652857, 3.307984806},
{ 0.016155E-6, 10213.285546211, 1.331103168},
{ 0.014751E-6, 1349.867409659, 4.308933301},
{ 0.015949E-6, -220.412642439, 4.005298270},
{ 0.015974E-6, -2352.866153772, 6.145309371},
{ 0.014223E-6, 17789.845619785, 2.104551349},
{ 0.017806E-6, 73.297125859, 3.475975097},
{ 0.013671E-6, -536.804512095, 5.971672571},
{ 0.011942E-6, 8031.092263058, 2.053414715},
{ 0.014318E-6, 16730.463689596, 3.016058075},
{ 0.012462E-6, 103.092774219, 1.737438797},
{ 0.010962E-6, 3.590428652, 2.196567739},
{ 0.015078E-6, 19651.048481098, 3.969480770},
{ 0.010396E-6, 951.718406251, 5.717799605},
{ 0.011707E-6, -4705.732307544, 2.654125618},
{ 0.010453E-6, 5863.591206116, 1.913704550},
{ 0.012420E-6, 4690.479836359, 4.734090399},
{ 0.011847E-6, 5643.178563677, 5.489005403},
{ 0.008610E-6, 3340.612426700, 3.661698944},
{ 0.011622E-6, 5120.601145584, 4.863931876},
{ 0.010825E-6, 553.569402842, 0.842715011},
{ 0.008666E-6, -135.065080035, 3.293406547},
{ 0.009963E-6, 149.563197135, 4.870690598},
{ 0.009858E-6, 6309.374169791, 1.061816410},
{ 0.007959E-6, 316.391869657, 2.465042647},
{ 0.010099E-6, 283.859318865, 1.942176992},
{ 0.007147E-6, -242.728603974, 3.661486981},
{ 0.007505E-6, 5230.807466803, 4.920937029},
{ 0.008323E-6, 11769.853693166, 1.229392026},
{ 0.007490E-6, -6256.777530192, 3.658444681},
{ 0.009370E-6, 149854.400134205, 0.673880395},
{ 0.007117E-6, 38.027672636, 5.294249518},
{ 0.007857E-6, 12168.002696575, 0.525733528},
{ 0.007019E-6, 6206.809778716, 0.837688810},
{ 0.006056E-6, 955.599741609, 4.194535082},
{ 0.008107E-6, 13367.972631107, 3.793235253},
{ 0.006731E-6, 5650.292110678, 5.639906583},
{ 0.007332E-6, 36.648562930, 0.114858677},
{ 0.006366E-6, 4164.311989613, 2.262081818},
{ 0.006858E-6, 5216.580372801, 0.642063318},
{ 0.006919E-6, 6681.224853400, 6.018501522},
{ 0.006826E-6, 7632.943259650, 3.458654112},
{ 0.005308E-6, -1592.596013633, 2.500382359},
{ 0.005096E-6, 11371.704689758, 2.547107806},
{ 0.004841E-6, 5333.900241022, 0.437078094},
{ 0.005582E-6, 5966.683980335, 2.246174308},
{ 0.006304E-6, 11926.254413669, 2.512929171},
{ 0.006603E-6, 23581.258177318, 5.393136889},
{ 0.005123E-6, -1.484472708, 2.999641028},
{ 0.004648E-6, 1589.072895284, 1.275847090},
{ 0.005119E-6, 6438.496249426, 1.486539246},
{ 0.004521E-6, 4292.330832950, 6.140635794},
{ 0.005680E-6, 23013.539539587, 4.557814849},
{ 0.005488E-6, -3.455808046, 0.090675389},
{ 0.004193E-6, 7234.794256242, 4.869091389},
{ 0.003742E-6, 7238.675591600, 4.691976180},
{ 0.004148E-6, -110.206321219, 3.016173439},
{ 0.004553E-6, 11499.656222793, 5.554998314},
{ 0.004892E-6, 5436.993015240, 1.475415597},
{ 0.004044E-6, 4732.030627343, 1.398784824},
{ 0.004164E-6, 12491.370101415, 5.650931916},
{ 0.004349E-6, 11513.883316794, 2.181745369},
{ 0.003919E-6, 12528.018664345, 5.823319737},
{ 0.003129E-6, 6836.645252834, 0.003844094},
{ 0.004080E-6, -7058.598461315, 3.690360123},
{ 0.003270E-6, 76.266071276, 1.517189902},
{ 0.002954E-6, 6283.143160294, 4.447203799},
{ 0.002872E-6, 28.449187468, 1.158692983},
{ 0.002881E-6, 735.876513532, 0.349250250},
{ 0.003279E-6, 5849.364112115, 4.893384368},
{ 0.003625E-6, 6209.778724132, 1.473760578},
{ 0.003074E-6, 949.175608970, 5.185878737},
{ 0.002775E-6, 9917.696874510, 1.030026325},
{ 0.002646E-6, 10973.555686350, 3.918259169},
{ 0.002575E-6, 25132.303399966, 6.109659023},
{ 0.003500E-6, 263.083923373, 1.892100742},
{ 0.002740E-6, 18319.536584880, 4.320519510},
{ 0.002464E-6, 202.253395174, 4.698203059},
{ 0.002409E-6, 2.542797281, 5.325009315},
{ 0.003354E-6, -90955.551694697, 1.942656623},
{ 0.002296E-6, 6496.374945429, 5.061810696},
{ 0.003002E-6, 6172.869528772, 2.797822767},
{ 0.003202E-6, 27511.467873537, 0.531673101},
{ 0.002954E-6, -6283.008539689, 4.533471191},
{ 0.002353E-6, 639.897286314, 3.734548088},
{ 0.002401E-6, 16200.772724501, 2.605547070},
{ 0.003053E-6, 233141.314403759, 3.029030662},
{ 0.003024E-6, 83286.914269554, 2.355556099},
{ 0.002863E-6, 17298.182327326, 5.240963796},
{ 0.002103E-6, -7079.373856808, 5.756641637},
{ 0.002303E-6, 83996.847317911, 2.013686814},
{ 0.002303E-6, 18073.704938650, 1.089100410},
{ 0.002381E-6, 63.735898303, 0.759188178},
{ 0.002493E-6, 6386.168624210, 0.645026535},
{ 0.002366E-6, 3.932153263, 6.215885448},
{ 0.002169E-6, 11015.106477335, 4.845297676},
{ 0.002397E-6, 6243.458341645, 3.809290043},
{ 0.002183E-6, 1162.474704408, 6.179611691},
{ 0.002353E-6, 6246.427287062, 4.781719760},
{ 0.002199E-6, -245.831646229, 5.956152284},
{ 0.001729E-6, 3894.181829542, 1.264976635},
{ 0.001896E-6, -3128.388765096, 4.914231596},
{ 0.002085E-6, 35.164090221, 1.405158503},
{ 0.002024E-6, 14712.317116458, 2.752035928},
{ 0.001737E-6, 6290.189396992, 5.280820144},
{ 0.002229E-6, 491.557929457, 1.571007057},
{ 0.001602E-6, 14314.168113050, 4.203664806},
{ 0.002186E-6, 454.909366527, 1.402101526},
{ 0.001897E-6, 22483.848574493, 4.167932508},
{ 0.001825E-6, -3738.761430108, 0.545828785},
{ 0.001894E-6, 1052.268383188, 5.817167450},
{ 0.001421E-6, 20.355319399, 2.419886601},
{ 0.001408E-6, 10984.192351700, 2.732084787},
{ 0.001847E-6, 10873.986030480, 2.903477885},
{ 0.001391E-6, -8635.942003763, 0.593891500},
{ 0.001388E-6, -7.046236698, 1.166145902},
{ 0.001810E-6, -88860.057071188, 0.487355242},
{ 0.001288E-6, -1990.745017041, 3.913022880},
{ 0.001297E-6, 23543.230504682, 3.063805171},
{ 0.001335E-6, -266.607041722, 3.995764039},
{ 0.001376E-6, 10969.965257698, 5.152914309},
{ 0.001745E-6, 244287.600007027, 3.626395673},
{ 0.001649E-6, 31441.677569757, 1.952049260},
{ 0.001416E-6, 9225.539273283, 4.996408389},
{ 0.001238E-6, 4804.209275927, 5.503379738},
{ 0.001472E-6, 4590.910180489, 4.164913291},
{ 0.001169E-6, 6040.347246017, 5.841719038},
{ 0.001039E-6, 5540.085789459, 2.769753519},
{ 0.001004E-6, -170.672870619, 0.755008103},
{ 0.001284E-6, 10575.406682942, 5.306538209},
{ 0.001278E-6, 71.812653151, 4.713486491},
{ 0.001321E-6, 18209.330263660, 2.624866359},
{ 0.001297E-6, 21228.392023546, 0.382603541},
{ 0.000954E-6, 6282.095528923, 0.882213514},
{ 0.001145E-6, 6058.731054289, 1.169483931},
{ 0.000979E-6, 5547.199336460, 5.448375984},
{ 0.000987E-6, -6262.300454499, 2.656486959},
{ 0.001070E-6, -154717.609887482, 1.827624012},
{ 0.000991E-6, 4701.116501708, 4.387001801},
{ 0.001155E-6, -14.227094002, 3.042700750},
{ 0.001176E-6, 277.034993741, 3.335519004},
{ 0.000890E-6, 13916.019109642, 5.601498297},
{ 0.000884E-6, -1551.045222648, 1.088831705},
{ 0.000876E-6, 5017.508371365, 3.969902609},
{ 0.000806E-6, 15110.466119866, 5.142876744},
{ 0.000773E-6, -4136.910433516, 0.022067765},
{ 0.001077E-6, 175.166059800, 1.844913056},
{ 0.000954E-6, -6284.056171060, 0.968480906},
{ 0.000737E-6, 5326.786694021, 4.923831588},
{ 0.000845E-6, -433.711737877, 4.749245231},
{ 0.000819E-6, 8662.240323563, 5.991247817},
{ 0.000852E-6, 199.072001436, 2.189604979},
{ 0.000723E-6, 17256.631536341, 6.068719637},
{ 0.000940E-6, 6037.244203762, 6.197428148},
{ 0.000885E-6, 11712.955318231, 3.280414875},
{ 0.000706E-6, 12559.038152982, 2.824848947},
{ 0.000732E-6, 2379.164473572, 2.501813417},
{ 0.000764E-6, -6127.655450557, 2.236346329},
{ 0.000908E-6, 131.541961686, 2.521257490},
{ 0.000907E-6, 35371.887265976, 3.370195967},
{ 0.000673E-6, 1066.495477190, 3.876512374},
{ 0.000814E-6, 17654.780539750, 4.627122566},
{ 0.000630E-6, 36.027866677, 0.156368499},
{ 0.000798E-6, 515.463871093, 5.151962502},
{ 0.000798E-6, 148.078724426, 5.909225055},
{ 0.000806E-6, 309.278322656, 6.054064447},
{ 0.000607E-6, -39.617508346, 2.839021623},
{ 0.000601E-6, 412.371096874, 3.984225404},
{ 0.000646E-6, 11403.676995575, 3.852959484},
{ 0.000704E-6, 13521.751441591, 2.300991267},
{ 0.000603E-6, -65147.619767937, 4.140083146},
{ 0.000609E-6, 10177.257679534, 0.437122327},
{ 0.000631E-6, 5767.611978898, 4.026532329},
{ 0.000576E-6, 11087.285125918, 4.760293101},
{ 0.000674E-6, 14945.316173554, 6.270510511},
{ 0.000726E-6, 5429.879468239, 6.039606892},
{ 0.000710E-6, 28766.924424484, 5.672617711},
{ 0.000647E-6, 11856.218651625, 3.397132627},
{ 0.000678E-6, -5481.254918868, 6.249666675},
{ 0.000618E-6, 22003.914634870, 2.466427018},
{ 0.000738E-6, 6134.997125565, 2.242668890},
{ 0.000660E-6, 625.670192312, 5.864091907},
{ 0.000694E-6, 3496.032826134, 2.668309141},
{ 0.000531E-6, 6489.261398429, 1.681888780},
{ 0.000611E-6, -143571.324284214, 2.424978312},
{ 0.000575E-6, 12043.574281889, 4.216492400},
{ 0.000553E-6, 12416.588502848, 4.772158039},
{ 0.000689E-6, 4686.889407707, 6.224271088},
{ 0.000495E-6, 7342.457780181, 3.817285811},
{ 0.000567E-6, 3634.621024518, 1.649264690},
{ 0.000515E-6, 18635.928454536, 3.945345892},
{ 0.000486E-6, -323.505416657, 4.061673868},
{ 0.000662E-6, 25158.601719765, 1.794058369},
{ 0.000509E-6, 846.082834751, 3.053874588},
{ 0.000472E-6, -12569.674818332, 5.112133338},
{ 0.000461E-6, 6179.983075773, 0.513669325},
{ 0.000641E-6, 83467.156352816, 3.210727723},
{ 0.000520E-6, 10344.295065386, 2.445597761},
{ 0.000493E-6, 18422.629359098, 1.676939306},
{ 0.000478E-6, 1265.567478626, 5.487314569},
{ 0.000472E-6, -18.159247265, 1.999707589},
{ 0.000559E-6, 11190.377900137, 5.783236356},
{ 0.000494E-6, 9623.688276691, 3.022645053},
{ 0.000463E-6, 5739.157790895, 1.411223013},
{ 0.000432E-6, 16858.482532933, 1.179256434},
{ 0.000574E-6, 72140.628666286, 1.758191830},
{ 0.000484E-6, 17267.268201691, 3.290589143},
{ 0.000550E-6, 4907.302050146, 0.864024298},
{ 0.000399E-6, 14.977853527, 2.094441910},
{ 0.000491E-6, 224.344795702, 0.878372791},
{ 0.000432E-6, 20426.571092422, 6.003829241},
{ 0.000481E-6, 5749.452731634, 4.309591964},
{ 0.000480E-6, 5757.317038160, 1.142348571},
{ 0.000485E-6, 6702.560493867, 0.210580917},
{ 0.000426E-6, 6055.549660552, 4.274476529},
{ 0.000480E-6, 5959.570433334, 5.031351030},
{ 0.000466E-6, 12562.628581634, 4.959581597},
{ 0.000520E-6, 39302.096962196, 4.788002889},
{ 0.000458E-6, 12132.439962106, 1.880103788},
{ 0.000470E-6, 12029.347187887, 1.405611197},
{ 0.000416E-6, -7477.522860216, 1.082356330},
{ 0.000449E-6, 11609.862544012, 4.179989585},
{ 0.000465E-6, 17253.041107690, 0.353496295},
{ 0.000362E-6, -4535.059436924, 1.583849576},
{ 0.000383E-6, 21954.157609398, 3.747376371},
{ 0.000389E-6, 17.252277143, 1.395753179},
{ 0.000331E-6, 18052.929543158, 0.566790582},
{ 0.000430E-6, 13517.870106233, 0.685827538},
{ 0.000368E-6, -5756.908003246, 0.731374317},
{ 0.000330E-6, 10557.594160824, 3.710043680},
{ 0.000332E-6, 20199.094959633, 1.652901407},
{ 0.000384E-6, 11933.367960670, 5.827781531},
{ 0.000387E-6, 10454.501386605, 2.541182564},
{ 0.000325E-6, 15671.081759407, 2.178850542},
{ 0.000318E-6, 138.517496871, 2.253253037},
{ 0.000305E-6, 9388.005909415, 0.578340206},
{ 0.000352E-6, 5749.861766548, 3.000297967},
{ 0.000311E-6, 6915.859589305, 1.693574249},
{ 0.000297E-6, 24072.921469776, 1.997249392},
{ 0.000363E-6, -640.877607382, 5.071820966},
{ 0.000323E-6, 12592.450019783, 1.072262823},
{ 0.000341E-6, 12146.667056108, 4.700657997},
{ 0.000290E-6, 9779.108676125, 1.812320441},
{ 0.000342E-6, 6132.028180148, 4.322238614},
{ 0.000329E-6, 6268.848755990, 3.033827743},
{ 0.000374E-6, 17996.031168222, 3.388716544},
{ 0.000285E-6, -533.214083444, 4.687313233},
{ 0.000338E-6, 6065.844601290, 0.877776108},
{ 0.000276E-6, 24.298513841, 0.770299429},
{ 0.000336E-6, -2388.894020449, 5.353796034},
{ 0.000290E-6, 3097.883822726, 4.075291557},
{ 0.000318E-6, 709.933048357, 5.941207518},
{ 0.000271E-6, 13095.842665077, 3.208912203},
{ 0.000331E-6, 6073.708907816, 4.007881169},
{ 0.000292E-6, 742.990060533, 2.714333592},
{ 0.000362E-6, 29088.811415985, 3.215977013},
{ 0.000280E-6, 12359.966151546, 0.710872502},
{ 0.000267E-6, 10440.274292604, 4.730108488},
{ 0.000262E-6, 838.969287750, 1.327720272},
{ 0.000250E-6, 16496.361396202, 0.898769761},
{ 0.000325E-6, 20597.243963041, 0.180044365},
{ 0.000268E-6, 6148.010769956, 5.152666276},
{ 0.000284E-6, 5636.065016677, 5.655385808},
{ 0.000301E-6, 6080.822454817, 2.135396205},
{ 0.000294E-6, -377.373607916, 3.708784168},
{ 0.000236E-6, 2118.763860378, 1.733578756},
{ 0.000234E-6, 5867.523359379, 5.575209112},
{ 0.000268E-6, -226858.238553767, 0.069432392},
{ 0.000265E-6, 167283.761587465, 4.369302826},
{ 0.000280E-6, 28237.233459389, 5.304829118},
{ 0.000292E-6, 12345.739057544, 4.096094132},
{ 0.000223E-6, 19800.945956225, 3.069327406},
{ 0.000301E-6, 43232.306658416, 6.205311188},
{ 0.000264E-6, 18875.525869774, 1.417263408},
{ 0.000304E-6, -1823.175188677, 3.409035232},
{ 0.000301E-6, 109.945688789, 0.510922054},
{ 0.000260E-6, 813.550283960, 2.389438934},
{ 0.000299E-6, 316428.228673312, 5.384595078},
{ 0.000211E-6, 5756.566278634, 3.789392838},
{ 0.000209E-6, 5750.203491159, 1.661943545},
{ 0.000240E-6, 12489.885628707, 5.684549045},
{ 0.000216E-6, 6303.851245484, 3.862942261},
{ 0.000203E-6, 1581.959348283, 5.549853589},
{ 0.000200E-6, 5642.198242609, 1.016115785},
{ 0.000197E-6, -70.849445304, 4.690702525},
{ 0.000227E-6, 6287.008003254, 2.911891613},
{ 0.000197E-6, 533.623118358, 1.048982898},
{ 0.000205E-6, -6279.485421340, 1.829362730},
{ 0.000209E-6, -10988.808157535, 2.636140084},
{ 0.000208E-6, -227.526189440, 4.127883842},
{ 0.000191E-6, 415.552490612, 4.401165650},
{ 0.000190E-6, 29296.615389579, 4.175658539},
{ 0.000264E-6, 66567.485864652, 4.601102551},
{ 0.000256E-6, -3646.350377354, 0.506364778},
{ 0.000188E-6, 13119.721102825, 2.032195842},
{ 0.000185E-6, -209.366942175, 4.694756586},
{ 0.000198E-6, 25934.124331089, 3.832703118},
{ 0.000195E-6, 4061.219215394, 3.308463427},
{ 0.000234E-6, 5113.487598583, 1.716090661},
{ 0.000188E-6, 1478.866574064, 5.686865780},
{ 0.000222E-6, 11823.161639450, 1.942386641},
{ 0.000181E-6, 10770.893256262, 1.999482059},
{ 0.000171E-6, 6546.159773364, 1.182807992},
{ 0.000206E-6, 70.328180442, 5.934076062},
{ 0.000169E-6, 20995.392966449, 2.169080622},
{ 0.000191E-6, 10660.686935042, 5.405515999},
{ 0.000228E-6, 33019.021112205, 4.656985514},
{ 0.000184E-6, -4933.208440333, 3.327476868},
{ 0.000220E-6, -135.625325010, 1.765430262},
{ 0.000166E-6, 23141.558382925, 3.454132746},
{ 0.000191E-6, 6144.558353121, 5.020393445},
{ 0.000180E-6, 6084.003848555, 0.602182191},
{ 0.000163E-6, 17782.732072784, 4.960593133},
{ 0.000225E-6, 16460.333529525, 2.596451817},
{ 0.000222E-6, 5905.702242076, 3.731990323},
{ 0.000204E-6, 227.476132789, 5.636192701},
{ 0.000159E-6, 16737.577236597, 3.600691544},
{ 0.000200E-6, 6805.653268085, 0.868220961},
{ 0.000187E-6, 11919.140866668, 2.629456641},
{ 0.000161E-6, 127.471796607, 2.862574720},
{ 0.000205E-6, 6286.666278643, 1.742882331},
{ 0.000189E-6, 153.778810485, 4.812372643},
{ 0.000168E-6, 16723.350142595, 0.027860588},
{ 0.000149E-6, 11720.068865232, 0.659721876},
{ 0.000189E-6, 5237.921013804, 5.245313000},
{ 0.000143E-6, 6709.674040867, 4.317625647},
{ 0.000146E-6, 4487.817406270, 4.815297007},
{ 0.000144E-6, -664.756045130, 5.381366880},
{ 0.000175E-6, 5127.714692584, 4.728443327},
{ 0.000162E-6, 6254.626662524, 1.435132069},
{ 0.000187E-6, 47162.516354635, 1.354371923},
{ 0.000146E-6, 11080.171578918, 3.369695406},
{ 0.000180E-6, -348.924420448, 2.490902145},
{ 0.000148E-6, 151.047669843, 3.799109588},
{ 0.000157E-6, 6197.248551160, 1.284375887},
{ 0.000167E-6, 146.594251718, 0.759969109},
{ 0.000133E-6, -5331.357443741, 5.409701889},
{ 0.000154E-6, 95.979227218, 3.366890614},
{ 0.000148E-6, -6418.140930027, 3.384104996},
{ 0.000128E-6, -6525.804453965, 3.803419985},
{ 0.000130E-6, 11293.470674356, 0.939039445},
{ 0.000152E-6, -5729.506447149, 0.734117523},
{ 0.000138E-6, 210.117701700, 2.564216078},
{ 0.000123E-6, 6066.595360816, 4.517099537},
{ 0.000140E-6, 18451.078546566, 0.642049130},
{ 0.000126E-6, 11300.584221356, 3.485280663},
{ 0.000119E-6, 10027.903195729, 3.217431161},
{ 0.000151E-6, 4274.518310832, 4.404359108},
{ 0.000117E-6, 6072.958148291, 0.366324650},
{ 0.000165E-6, -7668.637425143, 4.298212528},
{ 0.000117E-6, -6245.048177356, 5.379518958},
{ 0.000130E-6, -5888.449964932, 4.527681115},
{ 0.000121E-6, -543.918059096, 6.109429504},
{ 0.000162E-6, 9683.594581116, 5.720092446},
{ 0.000141E-6, 6219.339951688, 0.679068671},
{ 0.000118E-6, 22743.409379516, 4.881123092},
{ 0.000129E-6, 1692.165669502, 0.351407289},
{ 0.000126E-6, 5657.405657679, 5.146592349},
{ 0.000114E-6, 728.762966531, 0.520791814},
{ 0.000120E-6, 52.596639600, 0.948516300},
{ 0.000115E-6, 65.220371012, 3.504914846},
{ 0.000126E-6, 5881.403728234, 5.577502482},
{ 0.000158E-6, 163096.180360983, 2.957128968},
{ 0.000134E-6, 12341.806904281, 2.598576764},
{ 0.000151E-6, 16627.370915377, 3.985702050},
{ 0.000109E-6, 1368.660252845, 0.014730471},
{ 0.000131E-6, 6211.263196841, 0.085077024},
{ 0.000146E-6, 5792.741760812, 0.708426604},
{ 0.000146E-6, -77.750543984, 3.121576600},
{ 0.000107E-6, 5341.013788022, 0.288231904},
{ 0.000138E-6, 6281.591377283, 2.797450317},
{ 0.000113E-6, -6277.552925684, 2.788904128},
{ 0.000115E-6, -525.758811831, 5.895222200},
{ 0.000138E-6, 6016.468808270, 6.096188999},
{ 0.000139E-6, 23539.707386333, 2.028195445},
{ 0.000146E-6, -4176.041342449, 4.660008502},
{ 0.000107E-6, 16062.184526117, 4.066520001},
{ 0.000142E-6, 83783.548222473, 2.936315115},
{ 0.000128E-6, 9380.959672717, 3.223844306},
{ 0.000135E-6, 6205.325306007, 1.638054048},
{ 0.000101E-6, 2699.734819318, 5.481603249},
{ 0.000104E-6, -568.821874027, 2.205734493},
{ 0.000103E-6, 6321.103522627, 2.440421099},
{ 0.000119E-6, 6321.208885629, 2.547496264},
{ 0.000138E-6, 1975.492545856, 2.314608466},
{ 0.000121E-6, 137.033024162, 4.539108237},
{ 0.000123E-6, 19402.796952817, 4.538074405},
{ 0.000119E-6, 22805.735565994, 2.869040566},
{ 0.000133E-6, 64471.991241142, 6.056405489},
{ 0.000129E-6, -85.827298831, 2.540635083},
{ 0.000131E-6, 13613.804277336, 4.005732868},
{ 0.000104E-6, 9814.604100291, 1.959967212},
{ 0.000112E-6, 16097.679950283, 3.589026260},
{ 0.000123E-6, 2107.034507542, 1.728627253},
{ 0.000121E-6, 36949.230808424, 6.072332087},
{ 0.000108E-6, -12539.853380183, 3.716133846},
{ 0.000113E-6, -7875.671863624, 2.725771122},
{ 0.000109E-6, 4171.425536614, 4.033338079},
{ 0.000101E-6, 6247.911759770, 3.441347021},
{ 0.000113E-6, 7330.728427345, 0.656372122},
{ 0.000113E-6, 51092.726050855, 2.791483066},
{ 0.000106E-6, 5621.842923210, 1.815323326},
{ 0.000101E-6, 111.430161497, 5.711033677},
{ 0.000103E-6, 909.818733055, 2.812745443},
{ 0.000101E-6, 1790.642637886, 1.965746028},
{ 102.156724E-6, 6283.075849991, 4.249032005},
{ 1.706807E-6, 12566.151699983, 4.205904248},
{ 0.269668E-6, 213.299095438, 3.400290479},
{ 0.265919E-6, 529.690965095, 5.836047367},
{ 0.210568E-6, -3.523118349, 6.262738348},
{ 0.077996E-6, 5223.693919802, 4.670344204},
{ 0.054764E-6, 1577.343542448, 4.534800170},
{ 0.059146E-6, 26.298319800, 1.083044735},
{ 0.034420E-6, -398.149003408, 5.980077351},
{ 0.032088E-6, 18849.227549974, 4.162913471},
{ 0.033595E-6, 5507.553238667, 5.980162321},
{ 0.029198E-6, 5856.477659115, 0.623811863},
{ 0.027764E-6, 155.420399434, 3.745318113},
{ 0.025190E-6, 5746.271337896, 2.980330535},
{ 0.022997E-6, -796.298006816, 1.174411803},
{ 0.024976E-6, 5760.498431898, 2.467913690},
{ 0.021774E-6, 206.185548437, 3.854787540},
{ 0.017925E-6, -775.522611324, 1.092065955},
{ 0.013794E-6, 426.598190876, 2.699831988},
{ 0.013276E-6, 6062.663207553, 5.845801920},
{ 0.011774E-6, 12036.460734888, 2.292832062},
{ 0.012869E-6, 6076.890301554, 5.333425680},
{ 0.012152E-6, 1059.381930189, 6.222874454},
{ 0.011081E-6, -7.113547001, 5.154724984},
{ 0.010143E-6, 4694.002954708, 4.044013795},
{ 0.009357E-6, 5486.777843175, 3.416081409},
{ 0.010084E-6, 522.577418094, 0.749320262},
{ 0.008587E-6, 10977.078804699, 2.777152598},
{ 0.008628E-6, 6275.962302991, 4.562060226},
{ 0.008158E-6, -220.412642439, 5.806891533},
{ 0.007746E-6, 2544.314419883, 1.603197066},
{ 0.007670E-6, 2146.165416475, 3.000200440},
{ 0.007098E-6, 74.781598567, 0.443725817},
{ 0.006180E-6, -536.804512095, 1.302642751},
{ 0.005818E-6, 5088.628839767, 4.827723531},
{ 0.004945E-6, -6286.598968340, 0.268305170},
{ 0.004774E-6, 1349.867409659, 5.808636673},
{ 0.004687E-6, -242.728603974, 5.154890570},
{ 0.006089E-6, 1748.016413067, 4.403765209},
{ 0.005975E-6, -1194.447010225, 2.583472591},
{ 0.004229E-6, 951.718406251, 0.931172179},
{ 0.005264E-6, 553.569402842, 2.336107252},
{ 0.003049E-6, 5643.178563677, 1.362634430},
{ 0.002974E-6, 6812.766815086, 1.583012668},
{ 0.003403E-6, -2352.866153772, 2.552189886},
{ 0.003030E-6, 419.484643875, 5.286473844},
{ 0.003210E-6, -7.046236698, 1.863796539},
{ 0.003058E-6, 9437.762934887, 4.226420633},
{ 0.002589E-6, 12352.852604545, 1.991935820},
{ 0.002927E-6, 5216.580372801, 2.319951253},
{ 0.002425E-6, 5230.807466803, 3.084752833},
{ 0.002656E-6, 3154.687084896, 2.487447866},
{ 0.002445E-6, 10447.387839604, 2.347139160},
{ 0.002990E-6, 4690.479836359, 6.235872050},
{ 0.002890E-6, 5863.591206116, 0.095197563},
{ 0.002498E-6, 6438.496249426, 2.994779800},
{ 0.001889E-6, 8031.092263058, 3.569003717},
{ 0.002567E-6, 801.820931124, 3.425611498},
{ 0.001803E-6, -71430.695617928, 2.192295512},
{ 0.001782E-6, 3.932153263, 5.180433689},
{ 0.001694E-6, -4705.732307544, 4.641779174},
{ 0.001704E-6, -1592.596013633, 3.997097652},
{ 0.001735E-6, 5849.364112115, 0.417558428},
{ 0.001643E-6, 8429.241266467, 2.180619584},
{ 0.001680E-6, 38.133035638, 4.164529426},
{ 0.002045E-6, 7084.896781115, 0.526323854},
{ 0.001458E-6, 4292.330832950, 1.356098141},
{ 0.001437E-6, 20.355319399, 3.895439360},
{ 0.001738E-6, 6279.552731642, 0.087484036},
{ 0.001367E-6, 14143.495242431, 3.987576591},
{ 0.001344E-6, 7234.794256242, 0.090454338},
{ 0.001438E-6, 11499.656222793, 0.974387904},
{ 0.001257E-6, 6836.645252834, 1.509069366},
{ 0.001358E-6, 11513.883316794, 0.495572260},
{ 0.001628E-6, 7632.943259650, 4.968445721},
{ 0.001169E-6, 103.092774219, 2.838496795},
{ 0.001162E-6, 4164.311989613, 3.408387778},
{ 0.001092E-6, 6069.776754553, 3.617942651},
{ 0.001008E-6, 17789.845619785, 0.286350174},
{ 0.001008E-6, 639.897286314, 1.610762073},
{ 0.000918E-6, 10213.285546211, 5.532798067},
{ 0.001011E-6, -6256.777530192, 0.661826484},
{ 0.000753E-6, 16730.463689596, 3.905030235},
{ 0.000737E-6, 11926.254413669, 4.641956361},
{ 0.000694E-6, 3340.612426700, 2.111120332},
{ 0.000701E-6, 3894.181829542, 2.760823491},
{ 0.000689E-6, -135.065080035, 4.768800780},
{ 0.000700E-6, 13367.972631107, 5.760439898},
{ 0.000664E-6, 6040.347246017, 1.051215840},
{ 0.000654E-6, 5650.292110678, 4.911332503},
{ 0.000788E-6, 6681.224853400, 4.699648011},
{ 0.000628E-6, 5333.900241022, 5.024608847},
{ 0.000755E-6, -110.206321219, 4.370971253},
{ 0.000628E-6, 6290.189396992, 3.660478857},
{ 0.000635E-6, 25132.303399966, 4.121051532},
{ 0.000534E-6, 5966.683980335, 1.173284524},
{ 0.000543E-6, -433.711737877, 0.345585464},
{ 0.000517E-6, -1990.745017041, 5.414571768},
{ 0.000504E-6, 5767.611978898, 2.328281115},
{ 0.000485E-6, 5753.384884897, 1.685874771},
{ 0.000463E-6, 7860.419392439, 5.297703006},
{ 0.000604E-6, 515.463871093, 0.591998446},
{ 0.000443E-6, 12168.002696575, 4.830881244},
{ 0.000570E-6, 199.072001436, 3.899190272},
{ 0.000465E-6, 10969.965257698, 0.476681802},
{ 0.000424E-6, -7079.373856808, 1.112242763},
{ 0.000427E-6, 735.876513532, 1.994214480},
{ 0.000478E-6, -6127.655450557, 3.778025483},
{ 0.000414E-6, 10973.555686350, 5.441088327},
{ 0.000512E-6, 1589.072895284, 0.107123853},
{ 0.000378E-6, 10984.192351700, 0.915087231},
{ 0.000402E-6, 11371.704689758, 4.107281715},
{ 0.000453E-6, 9917.696874510, 1.917490952},
{ 0.000395E-6, 149.563197135, 2.763124165},
{ 0.000371E-6, 5739.157790895, 3.112111866},
{ 0.000350E-6, 11790.629088659, 0.440639857},
{ 0.000356E-6, 6133.512652857, 5.444568842},
{ 0.000344E-6, 412.371096874, 5.676832684},
{ 0.000383E-6, 955.599741609, 5.559734846},
{ 0.000333E-6, 6496.374945429, 0.261537984},
{ 0.000340E-6, 6055.549660552, 5.975534987},
{ 0.000334E-6, 1066.495477190, 2.335063907},
{ 0.000399E-6, 11506.769769794, 5.321230910},
{ 0.000314E-6, 18319.536584880, 2.313312404},
{ 0.000424E-6, 1052.268383188, 1.211961766},
{ 0.000307E-6, 63.735898303, 3.169551388},
{ 0.000329E-6, 29.821438149, 6.106912080},
{ 0.000357E-6, 6309.374169791, 4.223760346},
{ 0.000312E-6, -3738.761430108, 2.180556645},
{ 0.000301E-6, 309.278322656, 1.499984572},
{ 0.000268E-6, 12043.574281889, 2.447520648},
{ 0.000257E-6, 12491.370101415, 3.662331761},
{ 0.000290E-6, 625.670192312, 1.272834584},
{ 0.000256E-6, 5429.879468239, 1.913426912},
{ 0.000339E-6, 3496.032826134, 4.165930011},
{ 0.000283E-6, 3930.209696220, 4.325565754},
{ 0.000241E-6, 12528.018664345, 3.832324536},
{ 0.000304E-6, 4686.889407707, 1.612348468},
{ 0.000259E-6, 16200.772724501, 3.470173146},
{ 0.000238E-6, 12139.553509107, 1.147977842},
{ 0.000236E-6, 6172.869528772, 3.776271728},
{ 0.000296E-6, -7058.598461315, 0.460368852},
{ 0.000306E-6, 10575.406682942, 0.554749016},
{ 0.000251E-6, 17298.182327326, 0.834332510},
{ 0.000290E-6, 4732.030627343, 4.759564091},
{ 0.000261E-6, 5884.926846583, 0.298259862},
{ 0.000249E-6, 5547.199336460, 3.749366406},
{ 0.000213E-6, 11712.955318231, 5.415666119},
{ 0.000223E-6, 4701.116501708, 2.703203558},
{ 0.000268E-6, -640.877607382, 0.283670793},
{ 0.000209E-6, 5636.065016677, 1.238477199},
{ 0.000193E-6, 10177.257679534, 1.943251340},
{ 0.000182E-6, 6283.143160294, 2.456157599},
{ 0.000184E-6, -227.526189440, 5.888038582},
{ 0.000182E-6, -6283.008539689, 0.241332086},
{ 0.000228E-6, -6284.056171060, 2.657323816},
{ 0.000166E-6, 7238.675591600, 5.930629110},
{ 0.000167E-6, 3097.883822726, 5.570955333},
{ 0.000159E-6, -323.505416657, 5.786670700},
{ 0.000154E-6, -4136.910433516, 1.517805532},
{ 0.000176E-6, 12029.347187887, 3.139266834},
{ 0.000167E-6, 12132.439962106, 3.556352289},
{ 0.000153E-6, 202.253395174, 1.463313961},
{ 0.000157E-6, 17267.268201691, 1.586837396},
{ 0.000142E-6, 83996.847317911, 0.022670115},
{ 0.000152E-6, 17260.154654690, 0.708528947},
{ 0.000144E-6, 6084.003848555, 5.187075177},
{ 0.000135E-6, 5756.566278634, 1.993229262},
{ 0.000134E-6, 5750.203491159, 3.457197134},
{ 0.000144E-6, 5326.786694021, 6.066193291},
{ 0.000160E-6, 11015.106477335, 1.710431974},
{ 0.000133E-6, 3634.621024518, 2.836451652},
{ 0.000134E-6, 18073.704938650, 5.453106665},
{ 0.000134E-6, 1162.474704408, 5.326898811},
{ 0.000128E-6, 5642.198242609, 2.511652591},
{ 0.000160E-6, 632.783739313, 5.628785365},
{ 0.000132E-6, 13916.019109642, 0.819294053},
{ 0.000122E-6, 14314.168113050, 5.677408071},
{ 0.000125E-6, 12359.966151546, 5.251984735},
{ 0.000121E-6, 5749.452731634, 2.210924603},
{ 0.000136E-6, -245.831646229, 1.646502367},
{ 0.000120E-6, 5757.317038160, 3.240883049},
{ 0.000134E-6, 12146.667056108, 3.059480037},
{ 0.000137E-6, 6206.809778716, 1.867105418},
{ 0.000141E-6, 17253.041107690, 2.069217456},
{ 0.000129E-6, -7477.522860216, 2.781469314},
{ 0.000116E-6, 5540.085789459, 4.281176991},
{ 0.000116E-6, 9779.108676125, 3.320925381},
{ 0.000129E-6, 5237.921013804, 3.497704076},
{ 0.000113E-6, 5959.570433334, 0.983210840},
{ 0.000122E-6, 6282.095528923, 2.674938860},
{ 0.000140E-6, -11.045700264, 4.957936982},
{ 0.000108E-6, 23543.230504682, 1.390113589},
{ 0.000106E-6, -12569.674818332, 0.429631317},
{ 0.000110E-6, -266.607041722, 5.501340197},
{ 0.000115E-6, 12559.038152982, 4.691456618},
{ 0.000134E-6, -2388.894020449, 0.577313584},
{ 0.000109E-6, 10440.274292604, 6.218148717},
{ 0.000102E-6, -543.918059096, 1.477842615},
{ 0.000108E-6, 21228.392023546, 2.237753948},
{ 0.000101E-6, -4535.059436924, 3.100492232},
{ 0.000103E-6, 76.266071276, 5.594294322},
{ 0.000104E-6, 949.175608970, 5.674287810},
{ 0.000101E-6, 13517.870106233, 2.196632348},
{ 0.000100E-6, 11933.367960670, 4.056084160},
{ 4.322990E-6, 6283.075849991, 2.642893748},
{ 0.406495E-6, 0.000000000, 4.712388980},
{ 0.122605E-6, 12566.151699983, 2.438140634},
{ 0.019476E-6, 213.299095438, 1.642186981},
{ 0.016916E-6, 529.690965095, 4.510959344},
{ 0.013374E-6, -3.523118349, 1.502210314},
{ 0.008042E-6, 26.298319800, 0.478549024},
{ 0.007824E-6, 155.420399434, 5.254710405},
{ 0.004894E-6, 5746.271337896, 4.683210850},
{ 0.004875E-6, 5760.498431898, 0.759507698},
{ 0.004416E-6, 5223.693919802, 6.028853166},
{ 0.004088E-6, -7.113547001, 0.060926389},
{ 0.004433E-6, 77713.771467920, 3.627734103},
{ 0.003277E-6, 18849.227549974, 2.327912542},
{ 0.002703E-6, 6062.663207553, 1.271941729},
{ 0.003435E-6, -775.522611324, 0.747446224},
{ 0.002618E-6, 6076.890301554, 3.633715689},
{ 0.003146E-6, 206.185548437, 5.647874613},
{ 0.002544E-6, 1577.343542448, 6.232904270},
{ 0.002218E-6, -220.412642439, 1.309509946},
{ 0.002197E-6, 5856.477659115, 2.407212349},
{ 0.002897E-6, 5753.384884897, 5.863842246},
{ 0.001766E-6, 426.598190876, 0.754113147},
{ 0.001738E-6, -796.298006816, 2.714942671},
{ 0.001695E-6, 522.577418094, 2.629369842},
{ 0.001584E-6, 5507.553238667, 1.341138229},
{ 0.001503E-6, -242.728603974, 0.377699736},
{ 0.001552E-6, -536.804512095, 2.904684667},
{ 0.001370E-6, -398.149003408, 1.265599125},
{ 0.001889E-6, -5573.142801634, 4.413514859},
{ 0.001722E-6, 6069.776754553, 2.445966339},
{ 0.001124E-6, 1059.381930189, 5.041799657},
{ 0.001258E-6, 553.569402842, 3.849557278},
{ 0.000831E-6, 951.718406251, 2.471094709},
{ 0.000767E-6, 4694.002954708, 5.363125422},
{ 0.000756E-6, 1349.867409659, 1.046195744},
{ 0.000775E-6, -11.045700264, 0.245548001},
{ 0.000597E-6, 2146.165416475, 4.543268798},
{ 0.000568E-6, 5216.580372801, 4.178853144},
{ 0.000711E-6, 1748.016413067, 5.934271972},
{ 0.000499E-6, 12036.460734888, 0.624434410},
{ 0.000671E-6, -1194.447010225, 4.136047594},
{ 0.000488E-6, 5849.364112115, 2.209679987},
{ 0.000621E-6, 6438.496249426, 4.518860804},
{ 0.000495E-6, -6286.598968340, 1.868201275},
{ 0.000456E-6, 5230.807466803, 1.271231591},
{ 0.000451E-6, 5088.628839767, 0.084060889},
{ 0.000435E-6, 5643.178563677, 3.324456609},
{ 0.000387E-6, 10977.078804699, 4.052488477},
{ 0.000547E-6, 161000.685737473, 2.841633844},
{ 0.000522E-6, 3154.687084896, 2.171979966},
{ 0.000375E-6, 5486.777843175, 4.983027306},
{ 0.000421E-6, 5863.591206116, 4.546432249},
{ 0.000439E-6, 7084.896781115, 0.522967921},
{ 0.000309E-6, 2544.314419883, 3.172606705},
{ 0.000347E-6, 4690.479836359, 1.479586566},
{ 0.000317E-6, 801.820931124, 3.553088096},
{ 0.000262E-6, 419.484643875, 0.606635550},
{ 0.000248E-6, 6836.645252834, 3.014082064},
{ 0.000245E-6, -1592.596013633, 5.519526220},
{ 0.000225E-6, 4292.330832950, 2.877956536},
{ 0.000214E-6, 7234.794256242, 1.605227587},
{ 0.000205E-6, 5767.611978898, 0.625804796},
{ 0.000180E-6, 10447.387839604, 3.499954526},
{ 0.000229E-6, 199.072001436, 5.632304604},
{ 0.000214E-6, 639.897286314, 5.960227667},
{ 0.000175E-6, -433.711737877, 2.162417992},
{ 0.000209E-6, 515.463871093, 2.322150893},
{ 0.000173E-6, 6040.347246017, 2.556183691},
{ 0.000184E-6, 6309.374169791, 4.732296790},
{ 0.000227E-6, 149854.400134205, 5.385812217},
{ 0.000154E-6, 8031.092263058, 5.120720920},
{ 0.000151E-6, 5739.157790895, 4.815000443},
{ 0.000197E-6, 7632.943259650, 0.222827271},
{ 0.000197E-6, 74.781598567, 3.910456770},
{ 0.000138E-6, 6055.549660552, 1.397484253},
{ 0.000149E-6, -6127.655450557, 5.333727496},
{ 0.000137E-6, 3894.181829542, 4.281749907},
{ 0.000135E-6, 9437.762934887, 5.979971885},
{ 0.000139E-6, -2352.866153772, 4.715630782},
{ 0.000142E-6, 6812.766815086, 0.513330157},
{ 0.000120E-6, -4705.732307544, 0.194160689},
{ 0.000131E-6, -71430.695617928, 0.000379226},
{ 0.000124E-6, 6279.552731642, 2.122264908},
{ 0.000108E-6, -6256.777530192, 0.883445696},
{ 0.143388E-6, 6283.075849991, 1.131453581},
{ 0.006671E-6, 12566.151699983, 0.775148887},
{ 0.001480E-6, 155.420399434, 0.480016880},
{ 0.000934E-6, 213.299095438, 6.144453084},
{ 0.000795E-6, 529.690965095, 2.941595619},
{ 0.000673E-6, 5746.271337896, 0.120415406},
{ 0.000672E-6, 5760.498431898, 5.317009738},
{ 0.000389E-6, -220.412642439, 3.090323467},
{ 0.000373E-6, 6062.663207553, 3.003551964},
{ 0.000360E-6, 6076.890301554, 1.918913041},
{ 0.000316E-6, -21.340641002, 5.545798121},
{ 0.000315E-6, -242.728603974, 1.884932563},
{ 0.000278E-6, 206.185548437, 1.266254859},
{ 0.000238E-6, -536.804512095, 4.532664830},
{ 0.000185E-6, 522.577418094, 4.578313856},
{ 0.000245E-6, 18849.227549974, 0.587467082},
{ 0.000180E-6, 426.598190876, 5.151178553},
{ 0.000200E-6, 553.569402842, 5.355983739},
{ 0.000141E-6, 5223.693919802, 1.336556009},
{ 0.000104E-6, 5856.477659115, 4.239842759},
{ 0.003826E-6, 6283.075849991, 5.705257275},
{ 0.000303E-6, 12566.151699983, 5.407132842},
{ 0.000209E-6, 155.420399434, 1.989815753}
};
/* -------------------------------------------------------------------- */
/* Local Variables: */
double t, tsol, w, elsun, emsun, d, elj, els, wt, w0, w1, w2, w3, w4,
wf, wj;
int i;
/* Time since J2000.0 in Julian millennia. */
t = ( tdb - 51544.5 )/365250;
/* -------------------- Topocentric terms ----------------------------- */
/* Convert UT1 to local solar time in radians. */
tsol = fmod( ut1, 1.0 )*D2PI - wl;
/* FUNDAMENTAL ARGUMENTS: Simon et al 1994 */
/* Combine time argument (millennia ) with deg/arcsec factor. */
w = t / 3600.0;
/* Sun Mean Longitude. */
elsun = fmod( 280.46645683 + 1296027711.03429*w, 360.0 )*D2R;
/* Sun Mean Anomaly. */
emsun = fmod( 357.52910918 + 1295965810.481*w, 360.0 )*D2R;
/* Mean Elongation of Moon from Sun. */
d = fmod( 297.85019547 + 16029616012.090*w, 360.0 )*D2R;
/* Mean Longitude of Jupiter. */
elj = fmod( 34.35151874 + 109306899.89453*w, 360.0 )*D2R;
/* Mean Longitude of Saturn. */
els = fmod( 50.07744430 + 44046398.47038*w, 360.0 )*D2R;
/* TOPOCENTRIC TERMS: Moyer 1981 and Murray 1983. */
wt = + 0.00029E-10*u*sin( tsol + elsun - els )
+ 0.00100E-10*u*sin( tsol - 2*emsun )
+ 0.00133E-10*u*sin( tsol - d )
+ 0.00133E-10*u*sin( tsol + elsun - elj )
- 0.00229E-10*u*sin( tsol + 2*elsun + emsun )
- 0.0220E-10*v*cos( elsun + emsun )
+ 0.05312E-10*u*sin( tsol - emsun )
- 0.13677E-10*u*sin( tsol + 2*elsun )
- 1.3184E-10*v*cos( elsun )
+ 3.17679E-10*u*sin( tsol );
/* --------------- Fairhead model --------------------------------------- */
/* t**0 */
w0 = 0;
for( i = 473; i >= 0; i-- ) {
w0 = w0 + fairhd[ i ][ 0 ]*sin( fairhd[ i ][ 1 ]*t + fairhd[ i ][ 2 ] );
}
/* t**1 */
w1 = 0;
for( i = 678; i >= 474; i-- ) {
w1 = w1 + fairhd[ i ][ 0 ]*sin( fairhd[ i ][ 1 ]*t + fairhd[ i ][ 2 ] );
}
/* t**2 */
w2 = 0;
for( i = 763; i >= 679; i-- ) {
w2 = w2 + fairhd[ i ][ 0 ]*sin( fairhd[ i ][ 1 ]*t + fairhd[ i ][ 2 ] );
}
/* t**3 */
w3 = 0;
for( i = 783; i >= 764; i-- ) {
w3 = w3 + fairhd[ i ][ 0 ]*sin( fairhd[ i ][ 1 ]*t + fairhd[ i ][ 2 ] );
}
/* t**4 */
w4 = 0;
for( i = 786; i >= 784; i-- ) {
w4 = w4 + fairhd[ i ][ 0 ]*sin( fairhd[ i ][ 1 ]*t + fairhd[ i ][ 2 ] );
}
/* Multiply by powers of T and combine. */
wf = t*( t*( t*( t*w4 + w3 ) + w2 ) + w1 ) + w0;
/* Adjustments to use JPL planetary masses instead of IAU. */
wj = 0.00065E-6 * sin( 6069.776754 *t + 4.021194 ) +
0.00033E-6 * sin( 213.299095 *t + 5.543132 ) +
( -0.00196E-6 * sin( 6208.294251 *t + 5.696701 ) ) +
( -0.00173E-6 * sin( 74.781599 *t + 2.435900 ) ) +
0.03638E-6*t*t;
/* -------------------------------------------------------------------- */
/* Final result: TDB-TT in seconds. */
return wt + wf + wj;
}
static void TimeAdd( AstTimeMap *this, const char *cvt, const double args[], int *status ) {
/*
*++
* Name:
c astTimeAdd
f AST_TIMEADD
* Purpose:
* Add a time coordinate conversion to a TimeMap.
* Type:
* Public virtual function.
* Synopsis:
c #include "timemap.h"
c void astTimeAdd( AstTimeMap *this, const char *cvt, const double args[] )
f CALL AST_TIMEADD( THIS, CVT, ARGS, STATUS )
* Class Membership:
* TimeMap method.
* Description:
c This function adds one of the standard time coordinate
f This routine adds one of the standard time coordinate
* system conversions listed below to an existing TimeMap.
*
c When a TimeMap is first created (using astTimeMap), it simply
f When a TimeMap is first created (using AST_TIMEMAP), it simply
c performs a unit (null) Mapping. By using astTimeAdd (repeatedly
f performs a unit (null) Mapping. By using AST_TIMEADD (repeatedly
* if necessary), one or more coordinate conversion steps may then
* be added, which the TimeMap will perform in sequence. This allows
* multi-step conversions between a variety of time coordinate
* systems to be assembled out of the building blocks provided by
* this class.
*
* Normally, if a TimeMap's Invert attribute is zero (the default),
* then its forward transformation is performed by carrying out
* each of the individual coordinate conversions specified by
c astTimeAdd in the order given (i.e. with the most recently added
f AST_TIMEADD in the order given (i.e. with the most recently added
* conversion applied last).
*
* This order is reversed if the TimeMap's Invert attribute is
* non-zero (or if the inverse transformation is requested by any
* other means) and each individual coordinate conversion is also
* replaced by its own inverse. This process inverts the overall
* effect of the TimeMap. In this case, the first conversion to be
* applied would be the inverse of the one most recently added.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the TimeMap.
c cvt
f CVT = CHARACTER * ( * ) (Given)
c Pointer to a null-terminated string which identifies the
f A character string which identifies the
* time coordinate conversion to be added to the
* TimeMap. See the "Available Conversions" section for details of
* those available.
c args
f ARGS( * ) = DOUBLE PRECISION (Given)
* An array containing argument values for the time
* coordinate conversion. The number of arguments required, and
* hence the number of array elements used, depends on the
* conversion specified (see the "Available Conversions"
* section). This array is ignored
c and a NULL pointer may be supplied
* if no arguments are needed.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Notes:
* - When assembling a multi-stage conversion, it can sometimes be
* difficult to determine the most economical conversion path. A solution
* to this is to include all the steps which are (logically) necessary,
* but then to use
c astSimplify to simplify the resulting
f AST_SIMPLIFY to simplify the resulting
* TimeMap. The simplification process will eliminate any steps
* which turn out not to be needed.
c - This function does not check to ensure that the sequence of
f - This routine does not check to ensure that the sequence of
* coordinate conversions added to a TimeMap is physically
* meaningful.
* Available Conversions:
* The following strings (which are case-insensitive) may be supplied
c via the "cvt" parameter to indicate which time coordinate
f via the CVT argument to indicate which time coordinate
* conversion is to be added to the TimeMap. Where arguments are needed by
* the conversion, they are listed in parentheses. Values for
c these arguments should be given, via the "args" array, in the
f these arguments should be given, via the ARGS array, in the
* order indicated. Units and argument names are described at the end of
* the list of conversions, and "MJD" means Modified Julian Date.
*
* - "MJDTOMJD" (MJDOFF1,MJDOFF2): Convert MJD from one offset to another.
* - "MJDTOJD" (MJDOFF,JDOFF): Convert MJD to Julian Date.
* - "JDTOMJD" (JDOFF,MJDOFF): Convert Julian Date to MJD.
* - "MJDTOBEP" (MJDOFF,BEPOFF): Convert MJD to Besselian epoch.
* - "BEPTOMJD" (BEPOFF,MJDOFF): Convert Besselian epoch to MJD.
* - "MJDTOJEP" (MJDOFF,JEPOFF): Convert MJD to Julian epoch.
* - "JEPTOMJD" (JEPOFF,MJDOFF): Convert Julian epoch to MJD.
* - "TAITOUTC" (MJDOFF): Convert a TAI MJD to a UTC MJD.
* - "UTCTOTAI" (MJDOFF): Convert a UTC MJD to a TAI MJD.
* - "TAITOTT" (MJDOFF): Convert a TAI MJD to a TT MJD.
* - "TTTOTAI" (MJDOFF): Convert a TT MJD to a TAI MJD.
* - "TTTOTDB" (MJDOFF, OBSLON, OBSLAT, OBSALT): Convert a TT MJD to a TDB MJD.
* - "TDBTOTT" (MJDOFF, OBSLON, OBSLAT, OBSALT): Convert a TDB MJD to a TT MJD.
* - "TTTOTCG" (MJDOFF): Convert a TT MJD to a TCG MJD.
* - "TCGTOTT" (MJDOFF): Convert a TCG MJD to a TT MJD.
* - "TDBTOTCB" (MJDOFF): Convert a TDB MJD to a TCB MJD.
* - "TCBTOTDB" (MJDOFF): Convert a TCB MJD to a TDB MJD.
* - "UTTOGMST" (MJDOFF): Convert a UT MJD to a GMST MJD.
* - "GMSTTOUT" (MJDOFF): Convert a GMST MJD to a UT MJD.
* - "GMSTTOLMST" (MJDOFF, OBSLON, OBSLAT): Convert a GMST MJD to a LMST MJD.
* - "LMSTTOGMST" (MJDOFF, OBSLON, OBSLAT): Convert a LMST MJD to a GMST MJD.
* - "LASTTOLMST" (MJDOFF, OBSLON, OBSLAT): Convert a GMST MJD to a LMST MJD.
* - "LMSTTOLAST" (MJDOFF, OBSLON, OBSLAT): Convert a LMST MJD to a GMST MJD.
* - "UTTOUTC" (DUT1): Convert a UT1 MJD to a UTC MJD.
* - "UTCTOUT" (DUT1): Convert a UTC MJD to a UT1 MJD.
* - "LTTOUTC" (LTOFF): Convert a Local Time MJD to a UTC MJD.
* - "UTCTOLT" (LTOFF): Convert a UTC MJD to a Local Time MJD.
*
* The units for the values processed by the above conversions are as
* follows:
*
* - Julian epochs and offsets: Julian years
* - Besselian epochs and offsets: Tropical years
* - Modified Julian Dates and offsets: days
* - Julian Dates and offsets: days
*
* The arguments used in the above conversions are the zero-points
* used by the
c astTransform function.
f AST_TRANSFORM routine.
* The axis values supplied and returned by
c astTransform
f AST_TRANSFORM
* are offsets away from these zero-points:
*
* - MJDOFF: The zero-point being used with MJD values.
* - JDOFF: The zero-point being used with Julian Date values.
* - BEPOFF: The zero-point being used with Besselian epoch values.
* - JEPOFF: The zero-point being used with Julian epoch values.
* - OBSLON: Observer longitude in radians (+ve westwards).
* - OBSLAT: Observer geodetic latitude (IAU 1975) in radians (+ve northwards).
* - OBSALT: Observer geodetic altitude (IAU 1975) in metres.
* - DUT1: The UT1-UTC value to use.
* - LTOFF: The offset between Local Time and UTC (in hours, positive
* for time zones east of Greenwich).
*--
*/
/* Local Variables: */
int cvttype; /* Conversion type code */
/* Check the inherited status. */
if ( !astOK ) return;
/* Validate the type string supplied and obtain the equivalent
conversion type code. */
cvttype = CvtCode( cvt, status );
/* If the string was not recognised, then report an error. */
if ( astOK && ( cvttype == AST__TIME_NULL ) ) {
astError( AST__TIMIN,
"%s(%s): Invalid TimeMap time coordinate "
"conversion type \"%s\".", status, "astAddTime", astGetClass( this ), cvt );
}
/* Add the new conversion to the TimeMap. */
AddTimeCvt( this, cvttype, args, status );
}
static AstPointSet *Transform( AstMapping *this, AstPointSet *in,
int forward, AstPointSet *out, int *status ) {
/*
* Name:
* Transform
* Purpose:
* Apply a TimeMap to transform a set of points.
* Type:
* Private function.
* Synopsis:
* #include "timemap.h"
* AstPointSet *Transform( AstMapping *this, AstPointSet *in,
* int forward, AstPointSet *out, int *status )
* Class Membership:
* TimeMap member function (over-rides the astTransform method inherited
* from the Mapping class).
* Description:
* This function takes a TimeMap and a set of points encapsulated
* in a PointSet and transforms the points so as to perform the
* sequence of time coordinate conversions specified by
* previous invocations of astTimeAdd.
* Parameters:
* this
* Pointer to the TimeMap.
* in
* Pointer to the PointSet holding the input coordinate data.
* forward
* A non-zero value indicates that the forward coordinate transformation
* should be applied, while a zero value requests the inverse
* transformation.
* out
* Pointer to a PointSet which will hold the transformed (output)
* coordinate values. A NULL value may also be given, in which case a
* new PointSet will be created by this function.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the output (possibly new) PointSet.
* 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.
* - The number of coordinate values per point in the input PointSet must
* match the number of coordinates for the TimeMap being applied.
* - If an output PointSet is supplied, it must have space for sufficient
* number of points and coordinate values per point to accommodate the
* result. Any excess space will be ignored.
*/
/* Local Variables: */
AstPointSet *result; /* Pointer to output PointSet */
AstTimeMap *map; /* Pointer to TimeMap to be applied */
double **ptr_in; /* Pointer to input coordinate data */
double **ptr_out; /* Pointer to output coordinate data */
double *args; /* Pointer to argument list for conversion */
double *time; /* Pointer to output time axis value array */
double gmstx; /* GMST offset (in days) */
double tai; /* Absolute TAI value (in days) */
double tdb; /* Absolute TDB value (in days) */
double tt; /* Absolute TT value (in days) */
double utc; /* Absolute UTC value (in days) */
int ct; /* Conversion type */
int cvt; /* Loop counter for conversions */
int end; /* Termination index for conversion loop */
int inc; /* Increment for conversion loop */
int ncoord_in; /* Number of coordinates per input point */
int npoint; /* Number of points */
int point; /* Loop counter for points */
int start; /* Starting index for conversion loop */
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Obtain a pointer to the TimeMap. */
map = (AstTimeMap *) this;
/* Apply the parent mapping using the stored pointer to the Transform member
function inherited from the parent Mapping class. This function validates
all arguments and generates an output PointSet if necessary, but does not
actually transform any coordinate values. */
result = (*parent_transform)( this, in, forward, out, status );
/* We will now extend the parent astTransform method by performing the
coordinate conversions needed to generate the output coordinate values. */
/* Determine the numbers of points and coordinates per point from the input
PointSet and obtain pointers for accessing the input and output coordinate
values. */
ncoord_in = astGetNcoord( in );
npoint = astGetNpoint( in );
ptr_in = astGetPoints( in );
ptr_out = astGetPoints( result );
/* Determine whether to apply the forward or inverse transformation, according
to the direction specified and whether the mapping has been inverted. */
if ( astGetInvert( this ) ) forward = !forward;
/* Transform the coordinate values. */
/* -------------------------------- */
/* Use "time" as a synonym for the array of time axis values stored in
the output PointSet. */
if ( astOK ) {
time = ptr_out[ 0 ];
/* Initialise the output coordinate values by copying the input ones. */
if( time != ptr_in[ 0 ] ) {
(void) memcpy( time, ptr_in[ 0 ], sizeof( double ) * (size_t) npoint );
}
/* We will loop to apply each time coordinate conversion in turn to the
(time) array. However, if the inverse transformation was requested,
we must loop through these transformations in reverse order, so set up
appropriate limits and an increment to control this loop. */
start = forward ? 0 : map->ncvt - 1;
end = forward ? map->ncvt : -1;
inc = forward ? 1 : -1;
/* Loop through the coordinate conversions in the required order and obtain a
pointer to the argument list for the current conversion. */
for ( cvt = start; cvt != end; cvt += inc ) {
args = map->cvtargs[ cvt ];
/* Classify the SLALIB sky coordinate conversion to be applied. */
ct = map->cvttype[ cvt ];
switch ( ct ) {
/* MJD to MJD. */
/* ---------- */
case AST__MJDTOMJD:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += args[ 2 ];
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] -= args[ 2 ];
}
}
}
break;
/* MJD to JD. */
/* ---------- */
case AST__MJDTOJD:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += args[ 2 ];
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] -= args[ 2 ];
}
}
}
break;
/* JD to MJD. */
/* ---------- */
case AST__JDTOMJD:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += args[ 2 ];
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] -= args[ 2 ];
}
}
}
break;
/* MJD to Besselian epoch. */
/* ----------------------- */
case AST__MJDTOBEP:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] = palEpb( time[ point ] ) + args[ 2 ];
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] = palEpb2d( time[ point ] ) + args[ 3 ];
}
}
}
break;
/* Besselian epoch to MJD. */
/* ----------------------- */
case AST__BEPTOMJD:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] = palEpb2d( time[ point ] ) + args[ 2 ];
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] = palEpb( time[ point ] ) + args[ 3 ];
}
}
}
break;
/* MJD to Julian epoch. */
/* -------------------- */
case AST__MJDTOJEP:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] = palEpj( time[ point ] ) + args[ 2 ];
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] = palEpj2d( time[ point ] ) + args[ 3 ];
}
}
}
break;
/* Julian epoch to MJD. */
/* -------------------- */
case AST__JEPTOMJD:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] = palEpj2d( time[ point ] ) + args[ 2 ];
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] = palEpj( time[ point ] ) + args[ 3 ];
}
}
}
break;
/* TAI to UTC. */
/* ----------- */
case AST__TAITOUTC:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += astDat( time[ point ] +
args[ 0 ], 0 )/SPD;
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += astDat( time[ point ] +
args[ 0 ], 1 )/SPD;
}
}
}
break;
/* UTC to TAI. */
/* ----------- */
case AST__UTCTOTAI:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += astDat( time[ point ] +
args[ 0 ], 1 )/SPD;
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += astDat( time[ point ] +
args[ 0 ], 0 )/SPD;
}
}
}
break;
/* TAI to TT. */
/* ---------- */
case AST__TAITOTT:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += (TTOFF/SPD);
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] -= (TTOFF/SPD);
}
}
}
break;
/* TT to TAI. */
/* ---------- */
case AST__TTTOTAI:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] -= (TTOFF/SPD);
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += (TTOFF/SPD);
}
}
}
break;
/* TT to TDB. */
/* ---------- */
/* For the purpose of estimating TDB-TT, we assume UTC is a good approximation
to UT1, and that TT is a good approximation to TDB. */
case AST__TTTOTDB:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
tt = time[ point ] + args[ 0 ];
tai = tt - (TTOFF/SPD);
utc = tai + astDat( tai, 0 )/SPD;
time[ point ] += Rcc( tt, utc, args[ 1 ], args[ 4 ],
args[ 5 ], status )/SPD;
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
tdb = time[ point ] + args[ 0 ];
tai = tdb - (TTOFF/SPD);
utc = tai + astDat( tai, 0 )/SPD;
time[ point ] -= Rcc( tdb, utc, args[ 1 ], args[ 4 ],
args[ 5 ], status )/SPD;
}
}
}
break;
/* TDB to TT. */
/* ---------- */
/* For the purpose of estimating TDB-TT, we assume UTC is a good approximation
to UT1, and that TT is a good approximation to TDB. */
case AST__TDBTOTT:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
tdb = time[ point ] + args[ 0 ];
tai = tdb - (TTOFF/SPD);
utc = tai + astDat( tai, 0 )/SPD;
time[ point ] -= Rcc( tdb, utc, args[ 1 ], args[ 4 ],
args[ 5 ], status )/SPD;
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
tt = time[ point ] + args[ 0 ];
tai = tt - (TTOFF/SPD);
utc = tai + astDat( tai, 0 )/SPD;
time[ point ] += Rcc( tt, utc, args[ 1 ], args[ 4 ],
args[ 5 ], status )/SPD;
}
}
}
break;
/* TT to TCG. */
/* ---------- */
case AST__TTTOTCG:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += time[ point ]*LG + args[ 1 ];
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] = ( time[ point ] - args[ 1 ] ) /
( 1.0 + LG );
}
}
}
break;
/* TCG to TT. */
/* ---------- */
case AST__TCGTOTT:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] = ( time[ point ] - args[ 1 ] ) /
( 1.0 + LG );
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += time[ point ]*LG + args[ 1 ];
}
}
}
break;
/* TDB to TCB. */
/* ----------- */
/* For the purpose of estimating TDB-TT, we assume UTC is a good approximation
to UT1, and that TT is a good approximation to both TDB and TCB. */
case AST__TDBTOTCB:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += time[ point ]*LB + args[ 1 ];
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] = ( time[ point ] - args[ 1 ] ) /
( 1.0 + LB );
}
}
}
break;
/* TCB to TDB. */
/* ----------- */
/* For the purpose of estimating TDB-TT, we assume UTC is a good approximation
to UT1, and that TT is a good approximation to TDB. */
case AST__TCBTOTDB:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] = ( time[ point ] - args[ 1 ] ) /
( 1.0 + LB );
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += time[ point ]*LB + args[ 1 ];
}
}
}
break;
/* UT to GMST . */
/* ------------ */
case AST__UTTOGMST:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] = Gmsta( time[ point ], args[ 0 ], 1, status );
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] = Gmsta( time[ point ], args[ 0 ], 0, status );
}
}
}
break;
/* GMST to UT. */
/* ----------- */
case AST__GMSTTOUT:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] = Gmsta( time[ point ], args[ 0 ], 0, status );
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] = Gmsta( time[ point ], args[ 0 ], 1, status );
}
}
}
break;
/* GMST to LMST. */
/* ------------- */
case AST__GMSTTOLMST:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] -= args[ 1 ]/D2PI;
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += args[ 1 ]/D2PI;
}
}
}
break;
/* LMST to GMST. */
/* ------------- */
case AST__LMSTTOGMST:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += args[ 1 ]/D2PI;
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] -= args[ 1 ]/D2PI;
}
}
}
break;
/* UT1 to UTC. */
/* ------------- */
case AST__UTTOUTC:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] -= args[ 0 ]/86400.0;
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += args[ 0 ]/86400.0;
}
}
}
break;
/* UTC to UT1. */
/* ------------- */
case AST__UTCTOUT:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += args[ 0 ]/86400.0;
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] -= args[ 0 ]/86400.0;
}
}
}
break;
/* LT to UTC. */
/* ---------- */
case AST__LTTOUTC:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] -= args[ 0 ]/24.0;
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += args[ 0 ]/24.0;
}
}
}
break;
/* UTC to LT. */
/* ---------- */
case AST__UTCTOLT:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] += args[ 0 ]/24.0;
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
time[ point ] -= args[ 0 ]/24.0;
}
}
}
break;
/* LMST to LAST. */
/* ------------- */
/* Calculating the equation of the equinoxes required TDB. So we need to
convert the given LMST to TDB. We first convert LMST to UT1. UT1 is
equal to UTC to within 1 second. We then add on 32 seconds to get TAI
(this value is correct since 1999 - for earlier epochs an error of the
order of a minute will be introduced in the TAI value). We then add on
TTOFF seconds to get TT. This TT is then used as an approximation to
TDB. The total error in TDB is of the order of a few minutes, which
corresponds to an error of a few tens of microseconds in the equation of
the equinoxes. The sla precession-nutation model is accurate to around 3
mas = 200 us, so the error in TDB will be insignificant. */
case AST__LMSTTOLAST:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
gmstx = time[ point ] + args[ 1 ]/D2PI;
tdb = Gmsta( gmstx, args[ 0 ], 0, status )
+ args[ 0 ] + (32 + TTOFF)/SPD;
time[ point ] += palEqeqx( tdb )/D2PI;
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
gmstx = time[ point ] + args[ 1 ]/D2PI;
tdb = Gmsta( gmstx, args[ 0 ], 0, status )
+ args[ 0 ] + (32+TTOFF)/SPD;
time[ point ] -= palEqeqx( tdb )/D2PI;
}
}
}
break;
/* LAST to LMST. */
/* ------------- */
case AST__LASTTOLMST:
if ( forward ) {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
gmstx = time[ point ] + args[ 1 ]/D2PI;
tdb = Gmsta( gmstx, args[ 0 ], 0, status )
+ args[ 0 ] + (32+TTOFF)/SPD;
time[ point ] -= palEqeqx( tdb )/D2PI;
}
}
} else {
for ( point = 0; point < npoint; point++ ) {
if ( time[ point ] != AST__BAD ) {
gmstx = time[ point ] + args[ 1 ]/D2PI;
tdb = Gmsta( gmstx, args[ 0 ], 0, status )
+ args[ 0 ] + (32 + TTOFF)/SPD;
time[ point ] += palEqeqx( tdb )/D2PI;
}
}
}
}
}
}
/* If an error has occurred and a new PointSet may have been created, then
clean up by annulling it. In any case, ensure that a NULL result is
returned.*/
if ( !astOK ) {
if ( !out ) result = astAnnul( result );
result = NULL;
}
/* Return a pointer to the output PointSet. */
return result;
}
/* Copy constructor. */
/* ----------------- */
static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
/*
* Name:
* Copy
* Purpose:
* Copy constructor for TimeMap objects.
* Type:
* Private function.
* Synopsis:
* void Copy( const AstObject *objin, AstObject *objout, int *status )
* Description:
* This function implements the copy constructor for TimeMap objects.
* Parameters:
* objin
* Pointer to the object to be copied.
* objout
* Pointer to the object being constructed.
* status
* Pointer to the inherited status variable.
* Returned Value:
* void
* Notes:
* - This constructor makes a deep copy.
*/
/* Local Variables: */
AstTimeMap *in; /* Pointer to input TimeMap */
AstTimeMap *out; /* Pointer to output TimeMap */
int cvt; /* Loop counter for coordinate conversions */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain pointers to the input and output TimeMap structures. */
in = (AstTimeMap *) objin;
out = (AstTimeMap *) objout;
/* For safety, first clear any references to the input memory from the output
TimeMap. */
out->cvtargs = NULL;
out->cvttype = NULL;
/* Allocate memory for the output array of argument list pointers. */
out->cvtargs = astMalloc( sizeof( double * ) * (size_t) in->ncvt );
/* If necessary, allocate memory and make a copy of the input array of
coordinate conversion codes. */
if ( in->cvttype ) out->cvttype = astStore( NULL, in->cvttype,
sizeof( int )
* (size_t) in->ncvt );
/* If OK, loop through each conversion in the input TimeMap and make a copy of
its argument list, storing the new pointer in the output argument list
array. */
if ( astOK ) {
for ( cvt = 0; cvt < in->ncvt; cvt++ ) {
out->cvtargs[ cvt ] = astStore( NULL, in->cvtargs[ cvt ],
astSizeOf( in->cvtargs[ cvt ] ) );
}
/* If an error occurred while copying the argument lists, loop through the
conversions again and clean up by ensuring that the new memory allocated for
each argument list is freed. */
if ( !astOK ) {
for ( cvt = 0; cvt < in->ncvt; cvt++ ) {
out->cvtargs[ cvt ] = astFree( out->cvtargs[ cvt ] );
}
}
}
/* If an error occurred, free all other memory allocated above. */
if ( !astOK ) {
out->cvtargs = astFree( out->cvtargs );
out->cvttype = astFree( out->cvttype );
}
}
/* Destructor. */
/* ----------- */
static void Delete( AstObject *obj, int *status ) {
/*
* Name:
* Delete
* Purpose:
* Destructor for TimeMap objects.
* Type:
* Private function.
* Synopsis:
* void Delete( AstObject *obj, int *status )
* Description:
* This function implements the destructor for TimeMap objects.
* Parameters:
* obj
* Pointer to the object to be deleted.
* status
* Pointer to the inherited status variable.
* Returned Value:
* void
* Notes:
* This function attempts to execute even if the global error status is
* set.
*/
/* Local Variables: */
AstTimeMap *this; /* Pointer to TimeMap */
int cvt; /* Loop counter for coordinate conversions */
/* Obtain a pointer to the TimeMap structure. */
this = (AstTimeMap *) obj;
/* Loop to free the memory containing the argument list for each coordinate
conversion. */
for ( cvt = 0; cvt < this->ncvt; cvt++ ) {
this->cvtargs[ cvt ] = astFree( this->cvtargs[ cvt ] );
}
/* Free the memory holding the array of conversion types and the array of
argument list pointers. */
this->cvtargs = astFree( this->cvtargs );
this->cvttype = astFree( this->cvttype );
}
/* Dump function. */
/* -------------- */
static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
/*
* Name:
* Dump
* Purpose:
* Dump function for TimeMap objects.
* Type:
* Private function.
* Synopsis:
* #include "timemap.h"
* void Dump( AstObject *this, AstChannel *channel, int *status )
* Description:
* This function implements the Dump function which writes out data
* for the TimeMap class to an output Channel.
* Parameters:
* this
* Pointer to the TimeMap 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 Constants: */
#define KEY_LEN 50 /* Maximum length of a keyword */
/* Local Variables: */
AstTimeMap *this; /* Pointer to the TimeMap structure */
char key[ KEY_LEN + 1 ]; /* Buffer for keyword string */
const char *argdesc[ MAX_ARGS ]; /* Pointers to argument descriptions */
const char *comment; /* Pointer to comment string */
const char *sval; /* Pointer to string value */
int iarg; /* Loop counter for arguments */
int icvt; /* Loop counter for conversion steps */
int ival; /* Integer value */
int nargs; /* Number of user-supplied arguments */
int szargs; /* Number of stored arguments */
int set; /* Attribute value set? */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the TimeMap structure. */
this = (AstTimeMap *) this_object;
/* Write out values representing the instance variables for the TimeMap
class. Accompany these with appropriate comment strings, possibly
depending on the values being written.*/
/* In the case of attributes, we first use the appropriate (private)
Test... member function to see if they are set. If so, we then use
the (private) Get... function to obtain the value to be written
out.
For attributes which are not set, we use the astGet... method to
obtain the value instead. This will supply a default value
(possibly provided by a derived class which over-rides this method)
which is more useful to a human reader as it corresponds to the
actual default attribute value. Since "set" will be zero, these
values are for information only and will not be read back. */
/* Number of conversion steps. */
/* --------------------------- */
/* Regard this as "set" if it is non-zero. */
ival = this->ncvt;
set = ( ival != 0 );
astWriteInt( channel, "Ntime", set, 0, ival, "Number of conversion steps" );
/* Write out data for each conversion step... */
for ( icvt = 0; icvt < this->ncvt; icvt++ ) {
/* Conversion type. */
/* ---------------- */
/* Change each conversion type code into an equivalent string and
obtain associated descriptive information. If the conversion code
was not recognised, report an error and give up. */
if ( astOK ) {
sval = CvtString( this->cvttype[ icvt ], &comment,
&nargs, &szargs, argdesc, status );
if ( astOK && !sval ) {
astError( AST__TIMIN,
"astWrite(%s): Corrupt %s contains invalid TimeMap "
"time coordinate conversion code (%d).", status,
astGetClass( channel ), astGetClass( this ),
(int) this->cvttype[ icvt ] );
break;
}
/* Create an appropriate keyword and write out the conversion code
information. */
(void) sprintf( key, "Time%d", icvt + 1 );
astWriteString( channel, key, 1, 1, sval, comment );
/* Write out data for each conversion argument... */
for ( iarg = 0; iarg < szargs; iarg++ ) {
/* Arguments. */
/* ---------- */
/* Create an appropriate keyword and write out the argument value,
accompanied by the descriptive comment obtained above. */
if( this->cvtargs[ icvt ][ iarg ] != AST__BAD ) {
(void) sprintf( key, "Time%d%c", icvt + 1, ALPHABET[ iarg ] );
astWriteDouble( channel, key, 1, 1, this->cvtargs[ icvt ][ iarg ],
argdesc[ iarg ] );
}
}
/* Quit looping if an error occurs. */
if ( !astOK ) break;
}
}
/* Undefine macros local to this function. */
#undef KEY_LEN
}
/* Standard class functions. */
/* ========================= */
/* Implement the astIsATimeMap and astCheckTimeMap functions using the macros
defined for this purpose in the "object.h" header file. */
astMAKE_ISA(TimeMap,Mapping)
astMAKE_CHECK(TimeMap)
AstTimeMap *astTimeMap_( int flags, const char *options, int *status, ...) {
/*
*++
* Name:
c astTimeMap
f AST_TIMEMAP
* Purpose:
* Create a TimeMap.
* Type:
* Public function.
* Synopsis:
c #include "timemap.h"
c AstTimeMap *astTimeMap( int flags, const char *options, ... )
f RESULT = AST_TIMEMAP( FLAGS, OPTIONS, STATUS )
* Class Membership:
* TimeMap constructor.
* Description:
* This function creates a new TimeMap and optionally initialises
* its attributes.
*
* A TimeMap is a specialised form of 1-dimensional Mapping which can be
* used to represent a sequence of conversions between standard time
* coordinate systems.
*
* When a TimeMap is first created, it simply performs a unit
c (null) Mapping. Using the astTimeAdd
f (null) Mapping. Using the AST_TIMEADD
c function, a series of coordinate conversion steps may then be
f routine, a series of coordinate conversion steps may then be
* added. This allows multi-step conversions between a variety of
* time coordinate systems to be assembled out of a set of building
* blocks.
*
* For details of the individual coordinate conversions available,
c see the description of the astTimeAdd function.
f see the description of the AST_TIMEADD routine.
* Parameters:
c flags
f FLAGS = INTEGER (Given)
c This parameter is reserved for future use and should currently
f This argument is reserved for future use and should currently
* always be set to zero.
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 TimeMap. 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.
c If no initialisation is required, a zero-length string may be
c supplied.
f A character string containing an optional comma-separated
f list of attribute assignments to be used for initialising the
f new TimeMap. The syntax used is identical to that for the
f AST_SET routine. If no initialisation is required, a blank
f value may be supplied.
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 astTimeMap()
f AST_TIMEMAP = INTEGER
* A pointer to the new TimeMap.
* Notes:
* - The nature and units of the coordinate values supplied for the
* first input (i.e. the time input) of a TimeMap must be appropriate
* to the first conversion step applied by the TimeMap. For instance, if
* the first conversion step is "MJDTOBEP" (Modified Julian Date to
* Besselian epoch) then the coordinate values for the first input should
* be date in units of days. Similarly, the nature and units of the
* coordinate values returned by a TimeMap will be determined by the
* last conversion step applied by the TimeMap.
* - A null Object pointer (AST__NULL) will be returned if this
c function is invoked with the AST error status set, or if it
f function is invoked with STATUS set to an error value, or if it
* should fail for any reason.
*--
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstTimeMap *new; /* Pointer to the new TimeMap */
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 TimeMap, allocating memory and initialising the virtual
function table as well if necessary. */
new = astInitTimeMap( NULL, sizeof( AstTimeMap ), !class_init, &class_vtab,
"TimeMap", flags );
/* 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 TimeMap'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 TimeMap. */
return new;
}
AstTimeMap *astTimeMapId_( int flags, const char *options, ... ) {
/*
* Name:
* astTimeMapId_
* Purpose:
* Create a TimeMap.
* Type:
* Private function.
* Synopsis:
* #include "timemap.h"
* AstTimeMap *astTimeMapId_( int flags, const char *options, ... )
* Class Membership:
* TimeMap constructor.
* Description:
* This function implements the external (public) interface to the
* astTimeMap constructor function. It returns an ID value (instead
* of a true C pointer) to external users, and must be provided
* because astTimeMap_ 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 astTimeMap_ 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 astTimeMap_.
* Returned Value:
* The ID value associated with the new TimeMap.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstTimeMap *new; /* Pointer to the new TimeMap */
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 TimeMap, allocating memory and initialising the virtual
function table as well if necessary. */
new = astInitTimeMap( NULL, sizeof( AstTimeMap ), !class_init, &class_vtab,
"TimeMap", flags );
/* 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 TimeMap'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 TimeMap. */
return astMakeId( new );
}
AstTimeMap *astInitTimeMap_( void *mem, size_t size, int init,
AstTimeMapVtab *vtab, const char *name,
int flags, int *status ) {
/*
*+
* Name:
* astInitTimeMap
* Purpose:
* Initialise a TimeMap.
* Type:
* Protected function.
* Synopsis:
* #include "timemap.h"
* AstTimeMap *astInitTimeMap( void *mem, size_t size, int init,
* AstTimeMapVtab *vtab, const char *name,
* int flags )
* Class Membership:
* TimeMap initialiser.
* Description:
* This function is provided for use by class implementations to initialise
* a new TimeMap object. It allocates memory (if necessary) to accommodate
* the TimeMap plus any additional data associated with the derived class.
* It then initialises a TimeMap structure at the start of this memory. If
* the "init" flag is set, it also initialises the contents of a virtual
* function table for a TimeMap at the start of the memory passed via the
* "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory in which the TimeMap is to be initialised.
* This must be of sufficient size to accommodate the TimeMap data
* (sizeof(TimeMap)) 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 TimeMap (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 TimeMap
* structure, so a valid value must be supplied even if not required for
* allocating memory.
* init
* A logical flag indicating if the TimeMap'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 TimeMap.
* 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 astClass
* method).
* flags
* This parameter is reserved for future use. It is currently ignored.
* Returned Value:
* A pointer to the new TimeMap.
* 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: */
AstTimeMap *new; /* Pointer to the new TimeMap */
/* Check the global status. */
if ( !astOK ) return NULL;
/* If necessary, initialise the virtual function table. */
if ( init ) astInitTimeMapVtab( vtab, name );
/* Initialise a 1D Mapping structure (the parent class) as the first component
within the TimeMap structure, allocating memory if necessary. Specify that
the Mapping should be defined in both the forward and inverse directions. */
new = (AstTimeMap *) astInitMapping( mem, size, 0,
(AstMappingVtab *) vtab, name,
1, 1, 1, 1 );
if ( astOK ) {
/* Initialise the TimeMap data. */
/* --------------------------- */
/* The initial state is with no conversions set, in which condition the
TimeMap simply implements a unit mapping. */
new->ncvt = 0;
new->cvtargs = NULL;
new->cvttype = NULL;
/* 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;
}
AstTimeMap *astLoadTimeMap_( void *mem, size_t size,
AstTimeMapVtab *vtab, const char *name,
AstChannel *channel, int *status ) {
/*
*+
* Name:
* astLoadTimeMap
* Purpose:
* Load a TimeMap.
* Type:
* Protected function.
* Synopsis:
* #include "timemap.h"
* AstTimeMap *astLoadTimeMap( void *mem, size_t size,
* AstTimeMapVtab *vtab, const char *name,
* AstChannel *channel )
* Class Membership:
* TimeMap loader.
* Description:
* This function is provided to load a new TimeMap 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
* TimeMap 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 a TimeMap at the start of the memory
* passed via the "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory into which the TimeMap is to be
* loaded. This must be of sufficient size to accommodate the
* TimeMap data (sizeof(TimeMap)) 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 TimeMap (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 TimeMap 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(AstTimeMap) is used instead.
* vtab
* Pointer to the start of the virtual function table to be
* associated with the new TimeMap. If this is NULL, a pointer to
* the (static) virtual function table for the TimeMap 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 "TimeMap" is used instead.
* Returned Value:
* A pointer to the new TimeMap.
* 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 Constants: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
#define KEY_LEN 50 /* Maximum length of a keyword */
/* Local Variables: */
AstTimeMap *new; /* Pointer to the new TimeMap */
char *sval; /* Pointer to string value */
char key[ KEY_LEN + 1 ]; /* Buffer for keyword string */
const char *argdesc[ MAX_ARGS ]; /* Pointers to argument descriptions */
const char *comment; /* Pointer to comment string */
int iarg; /* Loop counter for arguments */
int icvt; /* Loop counter for conversion steps */
int nargs; /* Number of user-supplied arguments */
int szargs; /* Number of stored arguments */
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(channel);
/* Initialise. */
new = NULL;
/* Check the global error status. */
if ( !astOK ) return new;
/* If a NULL virtual function table has been supplied, then this is
the first loader to be invoked for this TimeMap. In this case the
TimeMap belongs to this class, so supply appropriate values to be
passed to the parent class loader (and its parent, etc.). */
if ( !vtab ) {
size = sizeof( AstTimeMap );
vtab = &class_vtab;
name = "TimeMap";
/* If required, initialise the virtual function table for this class. */
if ( !class_init ) {
astInitTimeMapVtab( 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 TimeMap. */
new = astLoadMapping( mem, size, (AstMappingVtab *) 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, "TimeMap" );
/* Now read each individual data item from this list and use it to
initialise the appropriate instance variable(s) for this class. */
/* In the case of attributes, we first read the "raw" input value,
supplying the "unset" value as the default. If a "set" value is
obtained, we then use the appropriate (private) Set... member
function to validate and set the value properly. */
/* Number of conversion steps. */
/* --------------------------- */
/* Read the number of conversion steps and allocate memory to hold
data for each step. */
new->ncvt = astReadInt( channel, "ntime", 0 );
if ( new->ncvt < 0 ) new->ncvt = 0;
new->cvttype = astMalloc( sizeof( int ) * (size_t) new->ncvt );
new->cvtargs = astMalloc( sizeof( double * ) * (size_t) new->ncvt );
/* If an error occurred, ensure that all allocated memory is freed. */
if ( !astOK ) {
new->cvttype = astFree( new->cvttype );
new->cvtargs = astFree( new->cvtargs );
/* Otherwise, initialise the argument pointer array. */
} else {
for ( icvt = 0; icvt < new->ncvt; icvt++ ) {
new->cvtargs[ icvt ] = NULL;
}
/* Read in data for each conversion step... */
for ( icvt = 0; icvt < new->ncvt; icvt++ ) {
/* Conversion type. */
/* ---------------- */
/* Create an appropriate keyword and read the string representation of
the conversion type. */
(void) sprintf( key, "time%d", icvt + 1 );
sval = astReadString( channel, key, NULL );
/* If no value was read, report an error. */
if ( astOK ) {
if ( !sval ) {
astError( AST__BADIN,
"astRead(%s): A time coordinate conversion "
"type is missing from the input TimeMap data.", status,
astGetClass( channel ) );
/* Otherwise, convert the string representation into the required
conversion type code. */
} else {
new->cvttype[ icvt ] = CvtCode( sval, status );
/* If the string was not recognised, report an error. */
if ( new->cvttype[ icvt ] == AST__TIME_NULL ) {
astError( AST__BADIN,
"astRead(%s): Invalid time conversion "
"type \"%s\" in TimeMap data.", status,
astGetClass( channel ), sval );
}
}
/* Free the memory holding the string value. */
sval = astFree( sval );
}
/* Obtain the number of arguments associated with the conversion and
allocate memory to hold them. */
(void) CvtString( new->cvttype[ icvt ], &comment,
&nargs, &szargs, argdesc, status );
new->cvtargs[ icvt ] = astMalloc( sizeof( double ) *
(size_t) szargs );
/* Read in data for each argument... */
if ( astOK ) {
for ( iarg = 0; iarg < szargs; iarg++ ) {
/* Arguments. */
/* ---------- */
/* Create an appropriate keyword and read each argument value. */
(void) sprintf( key, "time%d%c", icvt + 1, ALPHABET[ iarg ] );
new->cvtargs[ icvt ][ iarg ] = astReadDouble( channel, key,
AST__BAD );
}
}
/* Quit looping if an error occurs. */
if ( !astOK ) break;
}
}
/* If an error occurred, clean up by deleting the new TimeMap. */
if ( !astOK ) new = astDelete( new );
}
/* Return the new TimeMap pointer. */
return new;
/* Undefine macros local to this function. */
#undef KEY_LEN
}
/* 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. */
void astTimeAdd_( AstTimeMap *this, const char *cvt, const double args[], int *status ) {
if ( !astOK ) return;
(**astMEMBER(this,TimeMap,TimeAdd))( this, cvt, args, status );
}
ast-8.0.7/grf3d.c 0000664 0001750 0001750 00000005402 12610415012 010402 0000000 0000000 /*
* Name:
* grf3d.c
* Purpose:
* Implement the grf3D interface if no graphics system is available.
* Description:
* This file implements the low level 3D graphics functions required
* by the rest of AST. These implementations simply report an error
* when called.
* Inheritance:
* This module is not a class and does not inherit.
* Copyright:
* Copyright (C) 2007 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
* .
* Authors:
* DSB: David S. Berry (JACH - UCLan)
* History:
* 20-JUN-2007 (DSB):
* Original version.
*/
/* Header files */
/* ============ */
#include "grf3d.h" /* Declare the functions in this module */
#include "error.h" /* AST error reporting facilities */
#include "ast_err.h" /* AST error codes */
/* Function Prototypes */
/* =================== */
static void Report( const char * );
/* Function definitions */
/* ==================== */
int astG3DCap( int cap, int value ){
return 0;
}
int astG3DFlush( void ){
Report( "astG3DFlush");
return 0;
}
int astG3DLine( int n, float *x, float *y, float *z ){
Report( "astG3DLine" );
return 0;
}
int astG3DQch( float *ch ){
Report( "astG3DQch" );
return 0;
}
int astG3DMark( int n, float *x, float *y, float *z, int type, float norm[3] ){
Report( "astG3DMark" );
return 0;
}
int astG3DText( const char *text, float ref[3], const char *just,
float up[3], float norm[3] ){
Report( "astG3DText" );
return 0;
}
int astG3DTxExt( const char *text, float ref[3], const char *just,
float up[3], float norm[3], float *xb, float *yb,
float *zb, float bl[3] ){
Report( "astG3DTxExt" );
return 0;
}
int astG3DAttr( int attr, double value, double *old_value, int prim ){
Report( "astG3DAttr" );
return 0;
}
static void Report( const char *name ){
astError( AST__GRFER, "%s: No graphics facilities are available.", name );
astError( AST__GRFER, "Re-link using an option such as '-pgplot' with "
"the ast_link script." );
}
ast-8.0.7/starlink.cls 0000664 0001750 0001750 00000034014 12527336466 011612 0000000 0000000 % Latex2E class for writing starlink documents.
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{starlink}
%-----------------------------------------------------%
% .. Class options.
% With chapters...
\newif\ifwithchapters
\withchaptersfalse
% If twoside...
\newif\iftwoside
\twosidefalse
% If list of figures (lof)
\newif\ifwithlof
\withloftrue
% If no abstract
\newif\ifwithabs
\withabstrue
% If all one page (affects html output only)
\newif\ifmultipage
\multipagetrue
% Declare the options.
\DeclareOption{chapters}{\withchapterstrue}
\DeclareOption{twoside}{\twosidetrue}
\DeclareOption{nolof}{\withloffalse}
\DeclareOption{noabs}{\withabsfalse}
\DeclareOption{onepage}{\multipagefalse}
% Pass all options not defined above to the classes.
% (Must be done before process options)
\ifwithchapters
\typeout{..... passing options to report .....}
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{report}}
\else
\typeout{........passing options to article......}
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}
\fi
% Process custom options.
\ProcessOptions\relax
\ifwithchapters
\LoadClass{report}
\typeout{........Report!...........}
\else
\LoadClass{article}
\typeout{..........Article!..........}
\fi
%--------------------------------------
% Packages required for all reports
% chek if in a pdf or not
\RequirePackage{ifpdf}
% Font types and encoding.
\RequirePackage[T1]{fontenc}
\RequirePackage[utf8]{inputenc}
% microtype For improved pdf typography (must come after loading class)
\RequirePackage{microtype}
%maths
\RequirePackage{amsmath}
\RequirePackage{mathpazo}
% units
\RequirePackage{siunitx}
% Titlesec.
\RequirePackage{titlesec}
% Package to allow graphics to be loaded (\includegraphics) and the
% default extensions it will look for (and their order).
\RequirePackage{graphicx}
\DeclareGraphicsExtensions{.pdf,.png,.jpg,.jpeg}
%.. Probably needed for something?
\RequirePackage{multirow}
% formatting of list enivornments
\RequirePackage{enumitem}
%.. Using color
\RequirePackage[usenames,dvipsnames,svgnames,table]{xcolor}
%.. Allow boxes with frames and backgrounds, over multiple pages
\RequirePackage[framemethod=TikZ]{mdframed}
%.. Allow tables on multiple pages
\RequirePackage{longtable}
%.. Allow sideways tables
\RequirePackage{rotating}
%.. Allow landscape pdf pages
\RequirePackage{pdflscape}
%.. Set up the page
\RequirePackage[text={160mm,230mm},centering]{geometry}
%.. title page formatting
\RequirePackage{titling}
%... Set up the headers.
\RequirePackage{fancyhdr}
%.... table of contents formatting
\ifpdf
\RequirePackage{tocloft}
\fi
%.. hyperref
\RequirePackage[pdfusetitle=true,backref,
breaklinks=True,pdfdisplaydoctitle=true]{hyperref}
%... allow environments using verbatim
\RequirePackage{fancyvrb}
%... allow starlink docs to use indexes
\RequirePackage{makeidx}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Starlink document identification commands
\newcommand{\stardoccategory}[1]{\def \@stardoccategory {#1}}
\newcommand{\stardocinitials}[1]{\def \@stardocinitials {#1}}
\newcommand{\stardoccopyright}[1]{\def \@stardoccopyright {#1}}
\newcommand{\stardocnumber}[1]{\def \@stardocnumber{#1}}
% Define a stardoctitle that takes an optional 'short' argument; this
% can then be used in various places. E.g. if you have a multi line
% full title, please ensure you also have a single line short title.
\def\stardoctitle{\@ifnextchar [{\@stardoctitletwo}{\@stardoctitleone}}
\def\@stardoctitletwo[#1]#2{\gdef\@stardoctitle{#2}\gdef\@shorttitle{#1}}
\def\@stardoctitleone#1{\gdef\@stardoctitle{#1}\gdef\@shorttitle{#1}}
\newcommand{\stardocversion}[1]{\def \@stardocversion{#1}}
\newcommand{\stardocmanual}[1]{\def \@stardocmanual{#1}}
\newcommand{\stardocabstract}[1]{\def \@stardocabstract{#1}}
\newcommand{\stardocauthors}[1]{\def\@stardocauthors{#1}}
\newcommand{\stardocdate}[1]{\def\@stardocdate{#1}}
\newcommand{\startitlepic}[1]{\def\@startitlepic{#1}}
\newcommand{\starfunders}[1]{\def\@starfunders{#1}}
\newcommand{\starproject}[1]{\def\@starproject{#1}}
\newcommand{\stardocsource}[1]{\def\@stardocsource{#1}}
\newcommand{\stardocname}[1]{\def\@stardocname{#1}}
% Defaults for current data?
\starfunders{}
\starproject{Starlink Project}
% initalise to nothing
\stardoccategory{}
\stardocinitials{}
\stardoccopyright{}
\stardocnumber{}
\stardoctitle{}
\stardocversion{}
\stardocmanual{}
\stardocabstract{}
\stardocauthors{}
\startitlepic{}
\stardocname{}
\stardocauthors{}
\stardocdate{}
% Provide \the... versions of these commands so you don't need to use @
% in latex.
\newcommand{\thestardoccategory}{\@stardoccategory}
\newcommand{\thestardocinitials}{\@stardocinitials}
\newcommand{\thestardoccopyright}{\@stardoccopyright}
\newcommand{\thestardocnumber}{\@stardocnumber}
\newcommand{\thestardoctitle}{\@stardoctitle}
\newcommand{\theshorttitle}{\@shorttitle}
\newcommand{\thestardocversion}{\@stardocversion}
\newcommand{\thestardocmanual}{\@stardocmanual}
\newcommand{\thestardocabstract}{\@stardocabstract}
\newcommand{\thestardocauthors}{\@stardocauthors}
\newcommand{\thestarfunders}{\@starfunders}
\newcommand{\thestarproject}{\@starproject}
\newcommand{\thestartitlepic}{\@startitlepic}
\newcommand{\thestardocsource}{\@stardocsource}
\newcommand{\thestardocdate}{\@stardocdate}
% Ensure the stardoctitle etc is available as \thetitle.
\newcommand{\thetitle}{\@stardoctitle}
\title{\@stardoctitle}
% Ensure the author list is available as \theauthor.
\newcommand{\theauthor}{\@stardocauthors}
\author{\@stardocauthors}
%%--------------------------------------------------------
%% Various commands to setup the frontmatter of starlink docs.
%% This should consist of 1) the title, 2) the abstract, 3) the table
%% of contents, 4) the list of figures (unless class option nolof is given).
%.. Format the initial header.
\newcommand{\titleheader}{%
\begin{flushright}
\textbf{\thestardocinitials /\thestardocnumber}
\end{flushright}
\thestarproject\\
\thestardoccategory\ \thestardocnumber\\
}
%... Format the main ttile
\newcommand{\titlemain}{%
\begin{center}
{\Huge\textbf{\thestardoctitle}}
{\Huge\textbf{\thestardocversion}}
{\Huge\textbf{\thestardocmanual}}
\end{center}
}
% Graphics for front page
\newcommand{\thestargraphics}{%
\centering
\thestartitlepic
\vspace{7.5mm}
\rule{\textwidth}{0.5mm}%
}
% .. Provide a command \startitle page that will produce a consistent
% starlink title page
\newcommand{\startitlepage}{%
\null
\vskip 2em%
\vspace*{-1\baselineskip}
\thispagestyle{empty}
\titleheader
%\vspace{-7mm}
\begin{flushright}
\par\thestardocauthors
\par\thestardocdate
\par\thestardoccopyright
\end{flushright}
%\vspace{-2mm}
\rule{\textwidth}{0.5mm}
\vspace{7.5mm}
\titlemain%\par
%\vspace{5mm}
\thestargraphics\\
}
%%.. command to print the abstract (with copyright at bottom of page)
\newcommand{\Abstract}{%
\ifwithabs
\ifwithchapters
\chapter*{}
\fi
\section*{Abstract}
\thispagestyle{fancy}
\markboth{Abstract}{}
\thestardocabstract{}
\\
\vspace*{\fill}
\\
{\small\thestardoccopyright{}}
\clearpage
\fi
}
%% General Front matter command -- title page, abstract, toc, lof This
%% command \scfrontmatter is what should be called
%% after \begin{document} in any starlink tex file.
\newcommand*{\scfrontmatter}{
% Use roman page numbers
\renewcommand{\thepage}{\roman{page}}
\setcounter{page}{1}
% Create the titlepage
\begin{titlepage}%
\startitlepage
\end{titlepage}
% Show the abstract (defined to do nothing if noabs is set)
\Abstract{}
\clearpage
% Table of contents (catcode stuff to prevent errors with _)
\begingroup \catcode`\_=12 \tableofcontents \endgroup
\clearpage
% unless the class option 'nolof' has been given, create a list of
% figures.
\ifwithlof
\begingroup \catcode`\_=12 \listoffigures \endgroup
\fi
\clearpage
% Reset the page counting to arabic and start from 1.
\renewcommand{\thepage}{\arabic{page}}
\setcounter{page}{1}
}
%%%-------------------------------------
%% Back matter commands (references and index)
% Ensure index shows up in toc.
\let\oldprintindex\printindex
\renewcommand{\printindex}{%
\phantomsection \addcontentsline{toc}{section}{Index}
\oldprintindex}
%%------------------------------------
%% Various class specific macros
%% Starlink list enivornments
%% enumdesc: An enumerated description list
\newcounter{enumdescc}
\newcounter{enumdescci}
\newlist{enumdesc}{description}{2}
\setlist[enumdesc,1]{%
before={\stepcounter{enumdescc}\setcounter{enumdescci}{0}},%
style=nextline,leftmargin=0.5cm,labelindent=0.5cm,rightmargin=0.5cm,
topsep=0.5\baselineskip, font={%
\phantomsection\normalfont\normalsize\bfseries\refstepcounter{enumdescci}\theenumdescci~}
}
%% A description list which has the labels in a box on the left with
%% the length of the widest label, and the definitions aligned past
%% it. In HTML output, starstyle.4ht will format this as a table.
%% This uses the package eqparbox to get the box of width of the
%% widest label (takes 2 runs of pdflatex).
\usepackage{eqparbox}
\newcounter{desc}
\newcommand{\descriptionmakelabel}[1]{\eqparbox{descnb\romannumeral\value{desc}}{#1\hfill}}
\newlist{aligndesc}{description}{2}
\setlist[aligndesc]{before={\refstepcounter{desc}\renewcommand{\makelabel}{\descriptionmakelabel}},
leftmargin=\dimexpr\eqboxwidth{descnb\romannumeral\numexpr\value{desc}+1\relax}+3em\relax,
labelsep=1em, labelindent=2em, rightmargin=2em}
%%%--------------------------------------------------------------
%% Linking and referencing commands.
%%.. Starlink xref command
%% By default use the starlink.ac.uk; this will be fixed up at the
%% end of make world by a different program.
\newcommand{\xref}[3]{%
\href{http://www.starlink.ac.uk/cgi-bin/htxserver/#2.htx/#2.html?xref_#3}{#1}}
\newcommand{\xlabel}[1]{\label{\protect{xref_#1}}}
\newcommand{\cref}[3]{#1~\ref{#2}}
%problems with _ in labels (e.g. in xrefs)
\let\oldunderscore\_
\renewcommand{\_}{\ifmmode \oldunderscore \else \string_\fi}
%%---------------------------------------------------------------
%% Deprecated commands (for compatability only)
% % Graphics commands
\newcommand{\starfig}[6]{
\begin{figure}#2
\centering\includegraphics[#3]{#1}
\typeout{#1 inserted on page \arabic{page}}
\caption[#5]{\label{#4} #6}
\end{figure}
}
% A starlink Hyperref (defined a bit differently to regular hyperref,
% and with a first argument that doesn't do anything. Deprecated; only
% provided for consistency with old documents. Include the string, not
% just the cross reference number or letter in the hyperlink.
\newcommand{\slhyperref}[4]{\hyperref[#4]{#2\ref*{#4}#3}}
\newcommand{\latexhtml}[2]{#1}
% %.. Empty environment latex only.
\newenvironment{latexonly}{}{}
%%%% Command that doesn't do anything in latex
\newcommand{\html}[1]{}
%.. environments that don't do anything
\def\makeinnocent#1{\catcode`#1=12 }
\def\csarg#1#2{\expandafter#1\csname#2\endcsname}
\def\ThrowAwayComment#1{\begingroup
\def\CurrentComment{#1}%
\let\do\makeinnocent \dospecials
\makeinnocent\^^L% and whatever other special cases
\endlinechar`\^^M \catcode`\^^M=12 \xComment}
{\catcode`\^^M=12 \endlinechar=-1 %
\gdef\xComment#1^^M{\def\test{#1}
\csarg\ifx{PlainEnd\CurrentComment Test}\test
\let\html@next\endgroup
\else \csarg\ifx{LaLaEnd\CurrentComment Test}\test
\edef\html@next{\endgroup\noexpand\end{\CurrentComment}}
\else \let\html@next\xComment
\fi \fi \html@next}
}
\def\excludecomment
#1{\expandafter\def\csname#1\endcsname{\ThrowAwayComment{#1}}%
{\escapechar=-1\relax
\csarg\xdef{PlainEnd#1Test}{\string\\end#1}%
\csarg\xdef{LaLaEnd#1Test}{\string\\end\string\{#1\string\}}%
}}
\excludecomment{htmlonly}
\newcommand{\latex}[1]{#1}
%------------------------------------------------------------------------
%.. Define additional colours.
\definecolor{mygray}{gray}{0.7}
\definecolor{MidnightBlue}{RGB}{25, 25, 112}
\definecolor{bblue}{RGB}{172,207,230}
%--------------------------------------------------
%.. Miscellanous commands
%.. Create a command to remove all space from input
\def\RemoveSpaces#1{\zap@space#1 \@empty}
% % Command for text that should be pushed to the right of the line (eg
% % following an hfill, on a single line of text
\newcommand*{\scpushright}[1]{\hfill #1}
% %.. verbatim environment for quoting terminal.
\DefineVerbatimEnvironment{terminalv}{Verbatim}{%
xleftmargin=.5in,formatcom=\color{MidnightBlue},fontsize=\small}
% command for a text box that floats around and pops out from the text (framed)
\mdfsetup{%
backgroundcolor=white,%
middlelinewidth=4pt,%
middlelinecolor=bblue,%
userdefinedwidth=0.8\textwidth,%
roundcorner=10pt, %
innertopmargin=\topskip}%
\newenvironment{sltextbox}[1]{%
\begin{figure*}[h]\begin{mdframed}[userdefinedwidth=0.8\textwidth,
align=center,%
frametitle=#1,%
frametitlebackgroundcolor=bblue]%
\setlength{\parskip}{\medskipamount}%
}%
{\end{mdframed}\end{figure*}}
%Framed boxes (obsolete).
\newsavebox{\fmbox}
\newenvironment{fmpage}[1]{\begin{lrbox}{\fmbox}\begin{minipage}{#1}}{\end{minipage}\end{lrbox}\fbox{\usebox{\fmbox}}}
%.. Tip box
\newenvironment{tip}%
{\begin{figure*}[h]\begin{mdframed}[userdefinedwidth=0.8\textwidth,align=center,frametitle=Tip,frametitlebackgroundcolor=bblue]%
\setlength{\parskip}{\medskipamount}%
}%
{\end{mdframed}\end{figure*}}
%.. starlink long table (used so that its easier to fix it up for html output)
\setlength{\LTcapwidth}{\textwidth}
\newenvironment{sllongtable}[2]{%
\begin{longtable}{#1}\caption{#2}\\}
{\end{longtable}}
%.........................................
\newcommand{\Acronyms}{%
\ifwithchapters
\chapter*{}
\fi
\section*{Acronyms}
\markboth{Acronyms}{}
\addcontentsline{toc}{section}{\protect\numberline{}Acronyms}
}
%--------------------------------------------------------------------
%% Load the reamining starlink specific classes.
%.. The remaining starlink specific definitions.
\RequirePackage{starabbrev}
\RequirePackage{starstyle}
\RequirePackage{sst}
%--------------------------------------------------------------------
ast-8.0.7/starstyle.sty 0000664 0001750 0001750 00000007654 12527336466 012065 0000000 0000000 \ProvidesPackage{starstyle}
%%%%
%% Package for styling the output (primarily in pdf)
%%%%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Number sections down to subsubsection level
\setcounter{secnumdepth}{4}
% %%% Formatting of the section headging
\newcommand{\colorsection}[1]{%
\colorbox{bblue}{\strut\parbox{\dimexpr\textwidth-2\fboxsep}{\thesection\ \quad #1}{\huge\strut}}}
\newcommand{\colorsectionnumberless}[1]{%
\colorbox{bblue}{\strut\parbox{\dimexpr\textwidth-2\fboxsep}{#1}\strut}}
% % Section headings (including numberless)
\ifpdf
\titleformat{name=\section}%
{\normalfont\Large\bfseries}{}{0pt}{\colorsection}
\titleformat{name=\section,numberless}%
{\normalfont\Large\bfseries}{}{0pt}{\colorsectionnumberless}
\fi
%... Chapter headings
\ifwithchapters
\titleformat{name=\chapter}[display]%
{\normalfont\huge\bfseries\thispagestyle{plain}}{%
\chaptertitlename\ \thechapter}{0pt}{}
\titleformat{name=\chapter,numberless}[display]%
{\normalfont\huge\bfseries\thispagestyle{plain}}{}{0pt}{\Huge}
%... spacing after chapter headings
\titlespacing*{\chapter}{0pt}{0pt}{20pt}
\fi
%.. section spacing
\titlespacing*{\section}{0pt}{3.5ex plus 1ex minus .2ex}{2.3ex plus .2ex}
\titlespacing*{\subsection}{0pt}{2ex plus 1ex minus .2ex}{1.5ex plus .2ex}
\titlespacing*{\subsubsection}{0pt}{2ex plus 1ex minus .2ex}{1ex plus .2ex}
\titlespacing*{\paragraph}{0pt}{2ex plus 1ex minus .2ex}{1em}
\titlespacing*{\subparagraph}{\parindent}{2ex plus 1ex minus
.2ex}{1em}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Page styles (e.g. headers and footers)
\pagestyle{fancy}
\fancyhf{}
\rhead{\slshape \thestardocinitials{}/\thestardocnumber{} \leftmark}
\lhead{\thepage}
\renewcommand{\headrulewidth}{0.0pt}
\ifwithchapters
\renewcommand{\chaptermark}[1]{
\markboth{#1}{}}
\else
\renewcommand{\sectionmark}[1]{
\markboth{#1}{}}
\fi
% .. Reset the plain pagestyle (used on chapter pages) to only have
% the page number and no rules.
\fancypagestyle{plain}{%
\fancyhf{}%
\iftwoside
\fancyhead[RE,LO]{\thepage}%
\fancyhead[LE,RO]{\slshape
\thestardocinitials{}/\thestardocnumber{}\nouppercase{---\leftmark}}%
\else
\fancyhead[LO]{\thepage}
\fancyhead[RO]{\slshape
\thestardocinitials{}/\thestardocnumber{}\nouppercase{---\leftmark}}%
\fi
\renewcommand{\headrulewidth}{0pt}%
\renewcommand{\footrulewidth}{0pt}%
}
% ensure correct chaptermakr is used when in pagestyle plain
\pagestyle{plain}
\fancyhf{}
\iftwoside
\fancyhead[LE,RO]{\slshape \thestardocinitials{}/\thestardocnumber{} \nouppercase{---\leftmark}}
\fancyhead[RE,LO]{\thepage}
\else
\lhead{\slshape \thestardocinitials{}/\thestardocnumber{} \nouppercase{---\leftmark}}
\rhead{\thepage}
\fi
\renewcommand{\headrulewidth}{0.0pt}
\ifwithchapters
\renewcommand{\chaptermark}[1]{
\markboth{#1}{}}
\else
\renewcommand{\sectionmark}[1]{
\markboth{#1}{}}
\fi
\renewcommand{\headrulewidth}{0pt}%
\renewcommand{\footrulewidth}{0pt}%
\ifwithchapters
\renewcommand{\chaptermark}[1]{\markboth{{#1}}{}}%
\else
\renewcommand{\sectionmark}[1]{\markboth{{#1}}{}}%
\fi
%% Select the fancy pagestyle as default
\pagestyle{fancy}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% General spacing
\setlength{\headheight}{1.5cm}
\setlength{\parskip}{\medskipamount}
\setlength{\parindent}{0pt}
\renewcommand{\arraystretch}{1.5}
%%%% Set up defaults for floats
% maximum size of 'top area'
\renewcommand{\topfraction}{0.9}
% maximum size of 'bottom area'
\renewcommand{\bottomfraction}{0.9}
% minimum amount of text on a non-float page
\renewcommand{\textfraction}{0.1}
%Enumerate list appearance
\setlist[enumerate,1]{label=(\arabic*)}
% macros for typesetting parameters
\providecommand{\aparam}[1]{\texttt{#1}} % ADAM parameter
\providecommand{\cparam}[1]{\texttt{#1}} % CONFIG parameter
\providecommand{\ndfcomp}[1]{\texttt{#1}} % NDF component
\newcommand{\menuitem}[2]{\item[\protect{\hyperref[#1]{#1}}] #2}
\newcommand{\classitem}[1]{\item [\protect{\hyperref[#1]{#1}}]}
ast-8.0.7/prism.h 0000664 0001750 0001750 00000016072 12610415012 010541 0000000 0000000 #if !defined( PRISM_INCLUDED ) /* Include this file only once */
#define PRISM_INCLUDED
/*
*+
* Name:
* prism.h
* Type:
* C include file.
* Purpose:
* Define the interface to the Prism class.
* Invocation:
* #include "prism.h"
* Description:
* This include file defines the interface to the Prism class and
* provides the type definitions, function prototypes and macros,
* etc. needed to use this class.
*
* The Prism class implement a Region which represents an extrusion of
* another Region into higher dimensions. For instance, a Prism can be
* used to represent a cylinder, which is an extrusion of a circle into a
* 3rd dimension.
* Inheritance:
* The Prism class inherits from the Region class.
* Feature Test Macros:
* astCLASS
* If the astCLASS macro is undefined, only public symbols are
* made available, otherwise protected symbols (for use in other
* class implementations) are defined. This macro also affects
* the reporting of error context information, which is only
* provided for external calls to the AST library.
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* 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
* .
* Authors:
* DSB: David S. Berry (Starlink)
* History:
* 17-DEC-2004 (DSB):
* Original version.
*-
*/
/* Include files. */
/* ============== */
/* Interface definitions. */
/* ---------------------- */
#include "region.h" /* Coordinate regions (parent class) */
#if defined(astCLASS) /* Protected */
#include "channel.h" /* I/O channels */
#endif
/* C header files. */
/* --------------- */
#if defined(astCLASS) /* Protected */
#include
#endif
/* Macros. */
/* ------- */
/* Define a dummy __attribute__ macro for use on non-GNU compilers. */
#ifndef __GNUC__
# define __attribute__(x) /*NOTHING*/
#endif
/* Type Definitions. */
/* ================= */
/* Prism structure. */
/* ------------------ */
/* This structure contains all information that is unique to each object in
the class (e.g. its instance variables). */
#if defined(astCLASS) || defined(astFORTRAN77)
#define STATUS_PTR status
#else
#define STATUS_PTR astGetStatusPtr
#endif
typedef struct AstPrism {
/* Attributes inherited from the parent class. */
AstRegion region; /* Parent class structure */
/* Attributes specific to objects in this class. */
AstRegion *region1; /* First component Region */
AstRegion *region2; /* Second component Region */
} AstPrism;
/* Virtual function table. */
/* ----------------------- */
/* This table contains all information that is the same for all
objects in the class (e.g. pointers to its virtual functions). */
#if defined(astCLASS) /* Protected */
typedef struct AstPrismVtab {
/* Properties (e.g. methods) inherited from the parent class. */
AstRegionVtab region_vtab; /* Parent class virtual function table */
/* A Unique identifier to determine class membership. */
AstClassIdentifier id;
/* Properties (e.g. methods) specific to this class. */
} AstPrismVtab;
#if defined(THREAD_SAFE)
/* Define a structure holding all data items that are global within the
object.c file. */
typedef struct AstPrismGlobals {
AstPrismVtab Class_Vtab;
int Class_Init;
} AstPrismGlobals;
/* Thread-safe initialiser for all global data used by this module. */
void astInitPrismGlobals_( AstPrismGlobals * );
#endif
#endif
/* Function prototypes. */
/* ==================== */
/* Prototypes for standard class functions. */
/* ---------------------------------------- */
astPROTO_CHECK(Prism) /* Check class membership */
astPROTO_ISA(Prism) /* Test class membership */
/* Constructor. */
#if defined(astCLASS) /* Protected. */
AstPrism *astPrism_( void *, void *, const char *, int *, ...);
#else
AstPrism *astPrismId_( void *, void *, const char *, ... )__attribute__((format(printf,3,4)));
#endif
#if defined(astCLASS) /* Protected */
/* Initialiser. */
AstPrism *astInitPrism_( void *, size_t, int, AstPrismVtab *,
const char *, AstRegion *, AstRegion *, int * );
/* Vtab initialiser. */
void astInitPrismVtab_( AstPrismVtab *, const char *, int * );
/* Loader. */
AstPrism *astLoadPrism_( void *, size_t, AstPrismVtab *,
const char *, AstChannel *, int * );
#endif
/* Prototypes for member functions. */
/* -------------------------------- */
# if defined(astCLASS) /* Protected */
AstRegion *astConvertToPrism_( AstRegion *, int * );
#endif
/* Function interfaces. */
/* ==================== */
/* These macros are wrap-ups for the functions defined by this class
to make them easier to invoke (e.g. to avoid type mis-matches when
passing pointers to objects from derived classes). */
/* Interfaces to standard class functions. */
/* --------------------------------------- */
/* Some of these functions provide validation, so we cannot use them
to validate their own arguments. We must use a cast when passing
object pointers (so that they can accept objects from derived
classes). */
/* Check class membership. */
#define astCheckPrism(this) astINVOKE_CHECK(Prism,this,0)
#define astVerifyPrism(this) astINVOKE_CHECK(Prism,this,1)
/* Test class membership. */
#define astIsAPrism(this) astINVOKE_ISA(Prism,this)
/* Constructor. */
#if defined(astCLASS) /* Protected. */
#define astPrism astINVOKE(F,astPrism_)
#else
#define astPrism astINVOKE(F,astPrismId_)
#endif
#if defined(astCLASS) /* Protected */
/* Initialiser. */
#define astInitPrism(mem,size,init,vtab,name,reg1,reg2) \
astINVOKE(O,astInitPrism_(mem,size,init,vtab,name,reg1,reg2,STATUS_PTR))
/* Vtab Initialiser. */
#define astInitPrismVtab(vtab,name) astINVOKE(V,astInitPrismVtab_(vtab,name,STATUS_PTR))
/* Loader. */
#define astLoadPrism(mem,size,vtab,name,channel) \
astINVOKE(O,astLoadPrism_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR))
#endif
/* Interfaces to public member functions. */
/* -------------------------------------- */
/* Here we make use of astCheckPrism to validate Prism pointers
before use. This provides a contextual error report if a pointer
to the wrong sort of Object is supplied. */
#if defined(astCLASS) /* Protected */
#define astConvertToPrism(this) astConvertToPrism_(this,STATUS_PTR)
#endif
#endif
ast-8.0.7/permmap.h 0000664 0001750 0001750 00000024475 12610415012 011056 0000000 0000000 #if !defined( PERMMAP_INCLUDED ) /* Include this file only once */
#define PERMMAP_INCLUDED
/*
*+
* Name:
* permmap.h
* Type:
* C include file.
* Purpose:
* Define the interface to the PermMap class.
* Invocation:
* #include "permmap.h"
* Description:
* This include file defines the interface to the PermMap class and
* provides the type definitions, function prototypes and macros,
* etc. needed to use this class.
*
* The PermMap class implements Mappings that perform permutation
* of the order of coordinate values, possibly also accompanied by
* changes in the number of coordinates (between input and output).
*
* In addition to permuting the coordinate order, coordinates may
* also be assigned constant values which are unrelated to other
* coordinate values. This facility is useful when the number of
* coordinates is being increased, as it allows fixed values to be
* assigned to the new coordinates.
* Inheritance:
* The PermMap class inherits from the Mapping class.
* Attributes Over-Ridden:
* None.
* New Attributes Defined:
* None.
* Methods Over-Ridden:
* Public:
* None.
*
* Protected:
* astTransform
* Transform a set of points.
* New Methods Defined:
* Public:
* None.
*
* Protected:
* astGetConstants
* Obtain a copy of the constants array
* astGetInPerm
* Obtain a copy of the input permutation array
* astGetOutPerm
* Obtain a copy of the output permutation array
* Other Class Functions:
* Public:
* astIsAPermMap
* Test class membership.
* astPermMap
* Create a PermMap.
*
* Protected:
* astCheckPermMap
* Validate class membership.
* astInitPermMap
* Initialise a PermMap.
* astInitPermMapVtab
* Initialise the virtual function table for the PermMap class.
* astLoadPermMap
* Load a PermMap.
* Macros:
* None.
* Type Definitions:
* Public:
* AstPermMap
* PermMap object type.
*
* Protected:
* AstPermMapVtab
* PermMap virtual function table type.
* Feature Test Macros:
* astCLASS
* If the astCLASS macro is undefined, only public symbols are
* made available, otherwise protected symbols (for use in other
* class implementations) are defined. This macro also affects
* the reporting of error context information, which is only
* provided for external calls to the AST library.
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* 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
* .
* Authors:
* RFWS: R.F. Warren-Smith (Starlink)
* History:
* 29-FEB-1996 (RFWS):
* Original version.
* 26-SEP-1996 (RFWS):
* Added external interface and I/O facilities.
* 8-JAN-2003 (DSB):
* Changed private InitVtab method to protected astInitPermMapVtab
* method.
*-
*/
/* Include files. */
/* ============== */
/* Interface definitions. */
/* ---------------------- */
#include "mapping.h" /* Coordinate mappings (parent class) */
#if defined(astCLASS) /* Protected */
#include "pointset.h" /* Sets of points/coordinates */
#include "channel.h" /* I/O channels */
#endif
/* C header files. */
/* --------------- */
#if defined(astCLASS) /* Protected */
#include
#endif
/* Macros */
/* ====== */
/* Define a dummy __attribute__ macro for use on non-GNU compilers. */
#ifndef __GNUC__
# define __attribute__(x) /*NOTHING*/
#endif
/* Type Definitions. */
/* ================= */
/* PermMap structure. */
/* ------------------ */
/* This structure contains all information that is unique to each
object in the class (e.g. its instance variables). */
typedef struct AstPermMap {
/* Attributes inherited from the parent class. */
AstMapping mapping; /* Parent class structure */
/* Attributes specific to objects in this class. */
int *inperm; /* Pointer to input permutation array */
int *outperm; /* Pointer to output permutation array */
double *constant; /* Pointer to array of constant values */
int permsplit; /* Method to use within MapSplit */
} AstPermMap;
/* Virtual function table. */
/* ----------------------- */
/* This table contains all information that is the same for all
objects in the class (e.g. pointers to its virtual functions). */
#if defined(astCLASS) /* Protected */
typedef struct AstPermMapVtab {
/* Properties (e.g. methods) inherited from the parent class. */
AstMappingVtab mapping_vtab; /* Parent class virtual function table */
/* A Unique identifier to determine class membership. */
AstClassIdentifier id;
/* Properties (e.g. methods) specific to this class. */
double *(* GetConstants)( AstPermMap *, int * );
int *(* GetInPerm)( AstPermMap *, int * );
int *(* GetOutPerm)( AstPermMap *, int * );
void (* SetPermSplit)( AstPermMap *, int, int * );
void (* ClearPermSplit)( AstPermMap *, int * );
int (* TestPermSplit)( AstPermMap *, int * );
int (* GetPermSplit)( AstPermMap *, int * );
} AstPermMapVtab;
#if defined(THREAD_SAFE)
/* Define a structure holding all data items that are global within the
object.c file. */
typedef struct AstPermMapGlobals {
AstPermMapVtab Class_Vtab;
int Class_Init;
} AstPermMapGlobals;
/* Thread-safe initialiser for all global data used by this module. */
void astInitPermMapGlobals_( AstPermMapGlobals * );
#endif
#endif
/* Function prototypes. */
/* ==================== */
/* Prototypes for standard class functions. */
/* ---------------------------------------- */
astPROTO_CHECK(PermMap) /* Check class membership */
astPROTO_ISA(PermMap) /* Test class membership */
/* Constructor. */
#if defined(astCLASS) /* Protected. */
AstPermMap *astPermMap_( int, const int [], int, const int [],
const double [], const char *, int *, ...);
#else
AstPermMap *astPermMapId_( int, const int [], int, const int [],
const double [], const char *, ... )__attribute__((format(printf,6,7)));
#endif
#if defined(astCLASS) /* Protected */
/* Initialiser. */
AstPermMap *astInitPermMap_( void *, size_t, int, AstPermMapVtab *,
const char *, int, const int [], int,
const int [], const double [], int * );
/* Vtab initialiser. */
void astInitPermMapVtab_( AstPermMapVtab *, const char *, int * );
/* Loader. */
AstPermMap *astLoadPermMap_( void *, size_t, AstPermMapVtab *,
const char *, AstChannel *, int * );
#endif
/* Prototypes for member functions. */
/* -------------------------------- */
# if defined(astCLASS) /* Protected */
double *astGetConstants_( AstPermMap *, int * );
int *astGetInPerm_( AstPermMap *, int * );
int *astGetOutPerm_( AstPermMap *, int * );
void astSetPermSplit_( AstPermMap *, int, int * );
void astClearPermSplit_( AstPermMap *, int * );
int astTestPermSplit_( AstPermMap *, int * );
int astGetPermSplit_( AstPermMap *, int * );
#endif
/* Function interfaces. */
/* ==================== */
/* These macros are wrap-ups for the functions defined by this class
to make them easier to invoke (e.g. to avoid type mis-matches when
passing pointers to objects from derived classes). */
/* Interfaces to standard class functions. */
/* --------------------------------------- */
/* Some of these functions provide validation, so we cannot use them
to validate their own arguments. We must use a cast when passing
object pointers (so that they can accept objects from derived
classes). */
/* Check class membership. */
#define astCheckPermMap(this) astINVOKE_CHECK(PermMap,this,0)
#define astVerifyPermMap(this) astINVOKE_CHECK(PermMap,this,1)
/* Test class membership. */
#define astIsAPermMap(this) astINVOKE_ISA(PermMap,this)
/* Constructor. */
#if defined(astCLASS) /* Protected. */
#define astPermMap astINVOKE(F,astPermMap_)
#else
#define astPermMap astINVOKE(F,astPermMapId_)
#endif
#if defined(astCLASS) /* Protected */
/* Initialiser. */
#define astInitPermMap(mem,size,init,vtab,name,nin,inperm,nout,outperm,constant) \
astINVOKE(O,astInitPermMap_(mem,size,init,vtab,name,nin,inperm,nout,outperm,constant,STATUS_PTR))
/* Vtab Initialiser. */
#define astInitPermMapVtab(vtab,name) astINVOKE(V,astInitPermMapVtab_(vtab,name,STATUS_PTR))
/* Loader. */
#define astLoadPermMap(mem,size,vtab,name,channel) \
astINVOKE(O,astLoadPermMap_(mem,size,vtab,name,astCheckChannel(channel),STATUS_PTR))
#endif
/* Interfaces to public member functions. */
/* -------------------------------------- */
/* Here we make use of astCheckPermMap to validate PermMap pointers
before use. This provides a contextual error report if a pointer
to the wrong sort of Object is supplied. */
#if defined(astCLASS) /* Protected */
#define astGetConstants(this) astINVOKE(V,astGetConstants_(astCheckPermMap(this),STATUS_PTR))
#define astGetInPerm(this) astINVOKE(V,astGetInPerm_(astCheckPermMap(this),STATUS_PTR))
#define astGetOutPerm(this) astINVOKE(V,astGetOutPerm_(astCheckPermMap(this),STATUS_PTR))
#define astSetPermSplit(this,permsplit) astINVOKE(V,astSetPermSplit_(astCheckPermMap(this),permsplit,STATUS_PTR))
#define astClearPermSplit(this) astINVOKE(V,astClearPermSplit_(astCheckPermMap(this),STATUS_PTR))
#define astTestPermSplit(this) astINVOKE(V,astTestPermSplit_(astCheckPermMap(this),STATUS_PTR))
#define astGetPermSplit(this) astINVOKE(V,astGetPermSplit_(astCheckPermMap(this),STATUS_PTR))
#endif
#endif
ast-8.0.7/fkeymap.c 0000664 0001750 0001750 00000122760 12610415012 011040 0000000 0000000 /*
*+
* Name:
* fkeymap.c
* Purpose:
* Define a FORTRAN 77 interface to the AST KeyMap class.
* Type of Module:
* C source file.
* Description:
* This file defines FORTRAN 77-callable C functions which provide
* a public FORTRAN 77 interface to the KeyMap class.
* Routines Defined:
* AST_ISAKEYMAP
* AST_KEYMAP
* AST_MAPCOPY
* AST_MAPPUT0
* AST_MAPPUT1
* AST_MAPPUTU
* AST_MAPGET0
* AST_MAPGET1
* AST_MAPGETELEM
* AST_MAPPUTELEM
* AST_MAPREMOVE
* AST_MAPRENAME
* AST_MAPSIZE
* AST_MAPLENGTH
* AST_MAPLENC
* AST_MAPTYPE
* AST_MAPKEY
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* 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
* .
* Authors:
* DSB: D.S. Berry (Starlink)
* History:
* 13-NOV-2004 (DSB):
* Original version.
* 5-JUN-2006 (DSB):
* Added support for single precision entries.
*/
/* Define the astFORTRAN77 macro which prevents error messages from
AST C functions from reporting the file and line number where the
error occurred (since these would refer to this file, they would
not be useful). */
#define astFORTRAN77
/* Header files. */
/* ============= */
#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */
#include "c2f77.h" /* F77 <-> C support functions/macros */
#include "error.h" /* Error reporting facilities */
#include "memory.h" /* Memory handling facilities */
#include "keymap.h" /* C interface to the KeyMap class */
F77_LOGICAL_FUNCTION(ast_isakeymap)( INTEGER(THIS), INTEGER(STATUS) ) {
GENPTR_INTEGER(THIS)
F77_LOGICAL_TYPE(RESULT);
astAt( "AST_ISAKEYMAP", NULL, 0 );
astWatchSTATUS(
RESULT = astIsAKeyMap( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE;
)
return RESULT;
}
F77_INTEGER_FUNCTION(ast_keymap)( CHARACTER(OPTIONS),
INTEGER(STATUS)
TRAIL(OPTIONS) ) {
GENPTR_CHARACTER(OPTIONS)
F77_INTEGER_TYPE(RESULT);
char *options;
int i;
astAt( "AST_KEYMAP", NULL, 0 );
astWatchSTATUS(
options = astString( OPTIONS, OPTIONS_length );
/* Truncate the options string to exlucde any trailing spaces. */
astChrTrunc( options );
/* Change ',' to '\n' (see AST_SET in fobject.c for why). */
if ( astOK ) {
for ( i = 0; options[ i ]; i++ ) {
if ( options[ i ] == ',' ) options[ i ] = '\n';
}
}
RESULT = astP2I( astKeyMap( "%s", options ) );
astFree( options );
)
return RESULT;
}
F77_SUBROUTINE(ast_mapput0a)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(VALUE),
CHARACTER(COMMENT),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(COMMENT) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(VALUE)
GENPTR_CHARACTER(COMMENT)
char *comment, *key;
astAt( "AST_MAPPUT0A", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
comment = astString( COMMENT, COMMENT_length );
astMapPut0A( astI2P( *THIS ), key, astI2P( *VALUE ), comment );
astFree( key );
astFree( comment );
)
}
F77_SUBROUTINE(ast_mapput0c)( INTEGER(THIS),
CHARACTER(KEY),
CHARACTER(VALUE),
CHARACTER(COMMENT),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(VALUE)
TRAIL(COMMENT) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_CHARACTER(VALUE)
GENPTR_CHARACTER(COMMENT)
char *comment, *value, *key;
astAt( "AST_MAPPUT0C", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
value = astString( VALUE, VALUE_length );
comment = astString( COMMENT, COMMENT_length );
astMapPut0C( astI2P( *THIS ), key, value, comment );
astFree( key );
astFree( value );
astFree( comment );
)
}
F77_SUBROUTINE(ast_mapput0i)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(VALUE),
CHARACTER(COMMENT),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(COMMENT) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(VALUE)
GENPTR_CHARACTER(COMMENT)
char *comment, *key;
astAt( "AST_MAPPUT0I", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
comment = astString( COMMENT, COMMENT_length );
astMapPut0I( astI2P( *THIS ), key, *VALUE, comment );
astFree( key );
astFree( comment );
)
}
F77_SUBROUTINE(ast_mapput0s)( INTEGER(THIS),
CHARACTER(KEY),
WORD(VALUE),
CHARACTER(COMMENT),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(COMMENT) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_WORD(VALUE)
GENPTR_CHARACTER(COMMENT)
char *comment, *key;
astAt( "AST_MAPPUT0W", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
comment = astString( COMMENT, COMMENT_length );
astMapPut0S( astI2P( *THIS ), key, *VALUE, comment );
astFree( key );
astFree( comment );
)
}
F77_SUBROUTINE(ast_mapput0b)( INTEGER(THIS),
CHARACTER(KEY),
UBYTE(VALUE),
CHARACTER(COMMENT),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(COMMENT) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_UBYTE(VALUE)
GENPTR_CHARACTER(COMMENT)
char *comment, *key;
astAt( "AST_MAPPUT0B", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
comment = astString( COMMENT, COMMENT_length );
astMapPut0B( astI2P( *THIS ), key, *VALUE, comment );
astFree( key );
astFree( comment );
)
}
F77_SUBROUTINE(ast_mapput0d)( INTEGER(THIS),
CHARACTER(KEY),
DOUBLE(VALUE),
CHARACTER(COMMENT),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(COMMENT) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_DOUBLE(VALUE)
GENPTR_CHARACTER(COMMENT)
char *comment, *key;
astAt( "AST_MAPPUT0D", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
comment = astString( COMMENT, COMMENT_length );
astMapPut0D( astI2P( *THIS ), key, *VALUE, comment );
astFree( key );
astFree( comment );
)
}
F77_SUBROUTINE(ast_mapput0r)( INTEGER(THIS),
CHARACTER(KEY),
REAL(VALUE),
CHARACTER(COMMENT),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(COMMENT) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_REAL(VALUE)
GENPTR_CHARACTER(COMMENT)
char *comment, *key;
astAt( "AST_MAPPUT0R", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
comment = astString( COMMENT, COMMENT_length );
astMapPut0F( astI2P( *THIS ), key, *VALUE, comment );
astFree( key );
astFree( comment );
)
}
F77_SUBROUTINE(ast_mapput1a)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(SIZE),
INTEGER_ARRAY(VALUE),
CHARACTER(COMMENT),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(COMMENT) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(SIZE)
GENPTR_INTEGER_ARRAY(VALUE)
GENPTR_CHARACTER(COMMENT)
char *comment, *key;
AstObject **values;
int i;
astAt( "AST_MAPPUT1A", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
comment = astString( COMMENT, COMMENT_length );
values = astMalloc( sizeof( AstObject * )*(size_t)( *SIZE ));
if( astOK ) {
for( i = 0; i < *SIZE; i++ ) {
values[ i ] = astI2P( VALUE[ i ] );
}
}
astMapPut1A( astI2P( *THIS ), key, *SIZE, values, comment );
astFree( values );
astFree( key );
astFree( comment );
)
}
F77_SUBROUTINE(ast_mapput1c)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(SIZE),
CHARACTER_ARRAY(VALUE),
CHARACTER(COMMENT),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(VALUE)
TRAIL(COMMENT) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(SIZE)
GENPTR_CHARACTER_ARRAY(VALUE)
GENPTR_CHARACTER(COMMENT)
char *comment, *key;
const char **values;
int i;
astAt( "AST_MAPPUT1C", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
comment = astString( COMMENT, COMMENT_length );
values = astMalloc( sizeof( const char * )*(size_t)( *SIZE ));
if( astOK ) {
for( i = 0; i < *SIZE; i++ ) {
values[ i ] = astString( VALUE + i*VALUE_length, VALUE_length );
}
}
astMapPut1C( astI2P( *THIS ), key, *SIZE, values, comment );
if( astOK ) {
for( i = 0; i < *SIZE; i++ ) astFree( (void *) values[ i ] );
}
astFree( (void *) values );
astFree( key );
astFree( comment );
)
}
F77_SUBROUTINE(ast_mapput1i)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(SIZE),
INTEGER_ARRAY(VALUE),
CHARACTER(COMMENT),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(COMMENT) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(SIZE)
GENPTR_INTEGER_ARRAY(VALUE)
GENPTR_CHARACTER(COMMENT)
char *comment, *key;
astAt( "AST_MAPPUT1I", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
comment = astString( COMMENT, COMMENT_length );
astMapPut1I( astI2P( *THIS ), key, *SIZE, VALUE, comment );
astFree( key );
astFree( comment );
)
}
F77_SUBROUTINE(ast_mapput1s)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(SIZE),
WORD_ARRAY(VALUE),
CHARACTER(COMMENT),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(COMMENT) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(SIZE)
GENPTR_WORD_ARRAY(VALUE)
GENPTR_CHARACTER(COMMENT)
char *comment, *key;
astAt( "AST_MAPPUT1W", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
comment = astString( COMMENT, COMMENT_length );
astMapPut1S( astI2P( *THIS ), key, *SIZE, VALUE, comment );
astFree( key );
astFree( comment );
)
}
F77_SUBROUTINE(ast_mapput1b)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(SIZE),
UBYTE_ARRAY(VALUE),
CHARACTER(COMMENT),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(COMMENT) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(SIZE)
GENPTR_UBYTE_ARRAY(VALUE)
GENPTR_CHARACTER(COMMENT)
char *comment, *key;
astAt( "AST_MAPPUT1B", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
comment = astString( COMMENT, COMMENT_length );
astMapPut1B( astI2P( *THIS ), key, *SIZE, VALUE, comment );
astFree( key );
astFree( comment );
)
}
F77_SUBROUTINE(ast_mapput1d)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(SIZE),
DOUBLE_ARRAY(VALUE),
CHARACTER(COMMENT),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(COMMENT) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(SIZE)
GENPTR_DOUBLE_ARRAY(VALUE)
GENPTR_CHARACTER(COMMENT)
char *comment, *key;
astAt( "AST_MAPPUT1D", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
comment = astString( COMMENT, COMMENT_length );
astMapPut1D( astI2P( *THIS ), key, *SIZE, VALUE, comment );
astFree( key );
astFree( comment );
)
}
F77_SUBROUTINE(ast_mapput1r)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(SIZE),
REAL_ARRAY(VALUE),
CHARACTER(COMMENT),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(COMMENT) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(SIZE)
GENPTR_REAL_ARRAY(VALUE)
GENPTR_CHARACTER(COMMENT)
char *comment, *key;
astAt( "AST_MAPPUT1R", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
comment = astString( COMMENT, COMMENT_length );
astMapPut1F( astI2P( *THIS ), key, *SIZE, VALUE, comment );
astFree( key );
astFree( comment );
)
}
F77_SUBROUTINE(ast_mapputu)( INTEGER(THIS),
CHARACTER(KEY),
CHARACTER(COMMENT),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(COMMENT) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_CHARACTER(COMMENT)
char *comment, *key;
astAt( "AST_MAPPUTU", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
comment = astString( COMMENT, COMMENT_length );
astMapPutU( astI2P( *THIS ), key, comment );
astFree( key );
astFree( comment );
)
}
F77_LOGICAL_FUNCTION(ast_mapget0i)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPGET0I", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGet0I( astI2P( *THIS ), key, VALUE ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapget0s)( INTEGER(THIS),
CHARACTER(KEY),
WORD(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_WORD(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPGET0W", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGet0S( astI2P( *THIS ), key, VALUE ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapget0b)( INTEGER(THIS),
CHARACTER(KEY),
UBYTE(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_UBYTE(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPGET0W", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGet0B( astI2P( *THIS ), key, VALUE ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapget0d)( INTEGER(THIS),
CHARACTER(KEY),
DOUBLE(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_DOUBLE(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPGET0D", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGet0D( astI2P( *THIS ), key, VALUE ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapget0r)( INTEGER(THIS),
CHARACTER(KEY),
REAL(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_REAL(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPGET0R", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGet0F( astI2P( *THIS ), key, VALUE ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapget0c)( INTEGER(THIS),
CHARACTER(KEY),
CHARACTER(VALUE),
INTEGER(L),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(VALUE) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_CHARACTER(VALUE)
GENPTR_INTEGER(L)
F77_LOGICAL_TYPE(RESULT);
char *key;
const char *value;
int i;
astAt( "AST_MAPGET0C", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
value = NULL;
RESULT = astMapGet0C( astI2P( *THIS ), key, &value ) ? F77_TRUE : F77_FALSE;
astFree( key );
i = 0;
if( value ) {
for( ; value[ i ] && ( i < VALUE_length ); i++ ) {
VALUE[ i ] = value[ i ];
}
*L = i;
} else {
*L = 0;
}
if( VALUE ) {
for( ; i < VALUE_length; i++ ) {
VALUE[ i ] = ' ';
}
}
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapget0a)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
AstObject *value;
astAt( "AST_MAPGET0A", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGet0A( astI2P( *THIS ), key, &value ) ? F77_TRUE : F77_FALSE;
astFree( key );
*VALUE = astP2I( value );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapget1i)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(MXVAL),
INTEGER(NVAL),
INTEGER_ARRAY(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(MXVAL)
GENPTR_INTEGER(NVAL)
GENPTR_INTEGER_ARRAY(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPGET1I", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGet1I( astI2P( *THIS ), key, *MXVAL, NVAL, VALUE ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapget1d)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(MXVAL),
INTEGER(NVAL),
DOUBLE_ARRAY(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(MXVAL)
GENPTR_INTEGER(NVAL)
GENPTR_DOUBLE_ARRAY(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPGET1D", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGet1D( astI2P( *THIS ), key, *MXVAL, NVAL, VALUE ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapget1s)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(MXVAL),
INTEGER(NVAL),
WORD_ARRAY(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(MXVAL)
GENPTR_INTEGER(NVAL)
GENPTR_WORD_ARRAY(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPGET1W", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGet1S( astI2P( *THIS ), key, *MXVAL, NVAL, VALUE ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapget1b)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(MXVAL),
INTEGER(NVAL),
UBYTE_ARRAY(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(MXVAL)
GENPTR_INTEGER(NVAL)
GENPTR_UBYTE_ARRAY(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPGET1B", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGet1B( astI2P( *THIS ), key, *MXVAL, NVAL, VALUE ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapget1r)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(MXVAL),
INTEGER(NVAL),
REAL_ARRAY(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(MXVAL)
GENPTR_INTEGER(NVAL)
GENPTR_REAL_ARRAY(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPGET1R", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGet1F( astI2P( *THIS ), key, *MXVAL, NVAL, VALUE ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapget1a)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(MXVAL),
INTEGER(NVAL),
INTEGER_ARRAY(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(MXVAL)
GENPTR_INTEGER(NVAL)
GENPTR_INTEGER_ARRAY(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
AstObject **values;
int i;
astAt( "AST_MAPGET1A", NULL, 0 );
astWatchSTATUS(
values = astMalloc( sizeof( AstObject *)*(size_t) *MXVAL );
key = astString( KEY, KEY_length );
RESULT = astMapGet1A( astI2P( *THIS ), key, *MXVAL, NVAL, values ) ? F77_TRUE : F77_FALSE;
astFree( key );
if( astOK ) {
for( i = 0; i < *NVAL; i++ ) VALUE[ i ] = astP2I( values[ i ] );
}
astFree( values );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapget1c)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(MXVAL),
INTEGER(NVAL),
CHARACTER_ARRAY(VALUE),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(VALUE) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(MXVAL)
GENPTR_INTEGER(NVAL)
GENPTR_CHARACTER_ARRAY(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
char *values, *c, *d;
int i, j, term;
astAt( "AST_MAPGET1C", NULL, 0 );
astWatchSTATUS(
values = astMalloc( sizeof( char )*(size_t) (*MXVAL)*( VALUE_length + 1 ) );
key = astString( KEY, KEY_length );
RESULT = astMapGet1C( astI2P( *THIS ), key, VALUE_length + 1, *MXVAL,
NVAL, values ) ? F77_TRUE : F77_FALSE;
astFree( key );
/* Loop round each string value returned in the array */
if( astOK ) {
c = values;
d = VALUE;
for( i = 0; i < *NVAL; i++ ) {
/* Loop round each of character in the "i"th element of the returned
array. Copy characters from the work array until a terminating null is
found. Replace this null by a space and replace all subsequent
characters by spaces up to the end of the returned array element. */
term = 0;
for( j = 0; j < VALUE_length; j++, d++, c++ ) {
if( term ) {
*d = ' ';
} else if( (*d = *c) == 0 ) {
*d = ' ';
term = 1;
}
}
/* Skip over the extra character at the end of each element in the work
array. */
c++;
}
}
astFree( values );
)
return RESULT;
}
F77_SUBROUTINE(ast_mapremove)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
char *key;
astAt( "AST_MAPREMOVE", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
astMapRemove( astI2P( *THIS ), key );
astFree( key );
)
}
F77_SUBROUTINE(ast_maprename)( INTEGER(THIS),
CHARACTER(OLDKEY),
CHARACTER(NEWKEY),
INTEGER(STATUS)
TRAIL(OLDKEY)
TRAIL(NEWKEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(OLDKEY)
GENPTR_CHARACTER(NEWKEY)
char *oldkey, *newkey;
astAt( "AST_MAPRENAME", NULL, 0 );
astWatchSTATUS(
oldkey = astString( OLDKEY, OLDKEY_length );
newkey = astString( NEWKEY, NEWKEY_length );
astMapRename( astI2P( *THIS ), oldkey, newkey );
astFree( oldkey );
astFree( newkey );
)
}
F77_SUBROUTINE(ast_mapcopy)( INTEGER(THIS),
INTEGER(THAT),
INTEGER(STATUS) ) {
GENPTR_INTEGER(THIS)
GENPTR_INTEGER(THAT)
astAt( "AST_MAPCOPY", NULL, 0 );
astWatchSTATUS(
astMapCopy( astI2P( *THIS ), astI2P( *THAT ) );
)
}
F77_INTEGER_FUNCTION(ast_mapsize)( INTEGER(THIS), INTEGER(STATUS) ) {
GENPTR_INTEGER(THIS)
F77_INTEGER_TYPE(RESULT);
astAt( "AST_MAPSIZE", NULL, 0 );
astWatchSTATUS(
RESULT = astMapSize( astI2P( *THIS ) );
)
return RESULT;
}
F77_INTEGER_FUNCTION(ast_maplength)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
F77_INTEGER_TYPE(RESULT);
char *key;
astAt( "AST_MAPLENGTH", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapLength( astI2P( *THIS ), key );
astFree( key );
)
return RESULT;
}
F77_INTEGER_FUNCTION(ast_maplenc)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
F77_INTEGER_TYPE(RESULT);
char *key;
astAt( "AST_MAPLENGTH", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapLenC( astI2P( *THIS ), key );
astFree( key );
)
return RESULT;
}
F77_INTEGER_FUNCTION(ast_maptype)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
F77_INTEGER_TYPE(RESULT);
char *key;
astAt( "AST_MAPTYPE", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapType( astI2P( *THIS ), key );
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_maphaskey)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPHASKEY", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapHasKey( astI2P( *THIS ), key ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapdefined)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPDEFINED", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapDefined( astI2P( *THIS ), key ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
/* NO_CHAR_FUNCTION indicates that the f77.h method of returning a
character result doesn't work, so add an extra argument instead and
wrap this function up in a normal FORTRAN 77 function (in the file
keymap.f). */
#if NO_CHAR_FUNCTION
F77_SUBROUTINE(ast_mapkey_a)( CHARACTER(RESULT),
#else
F77_SUBROUTINE(ast_mapkey)( CHARACTER_RETURN_VALUE(RESULT),
#endif
INTEGER(THIS),
INTEGER(INDEX),
#if NO_CHAR_FUNCTION
INTEGER(STATUS)
TRAIL(RESULT) ) {
#else
INTEGER(STATUS) ) {
#endif
GENPTR_CHARACTER(RESULT)
GENPTR_INTEGER(THIS)
GENPTR_INTEGER(INDEX)
const char *result;
int i;
astAt( "AST_MAPKEY", NULL, 0 );
astWatchSTATUS(
result = astMapKey( astI2P( *THIS ), *INDEX - 1 );
i = 0;
if ( astOK ) { /* Copy result */
for ( ; result[ i ] && i < RESULT_length; i++ ) {
RESULT[ i ] = result[ i ];
}
}
while ( i < RESULT_length ) RESULT[ i++ ] = ' '; /* Pad with blanks */
)
}
F77_LOGICAL_FUNCTION(ast_mapgetelemi)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(ELEM),
INTEGER_ARRAY(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(ELEM)
GENPTR_INTEGER_ARRAY(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPGETELEMI", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGetElemI( astI2P( *THIS ), key, *ELEM - 1, VALUE ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapgetelemd)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(ELEM),
DOUBLE_ARRAY(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(ELEM)
GENPTR_DOUBLE_ARRAY(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPGETELEMD", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGetElemD( astI2P( *THIS ), key, *ELEM - 1, VALUE ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapgetelems)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(ELEM),
WORD_ARRAY(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(ELEM)
GENPTR_WORD_ARRAY(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPGETELEMW", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGetElemS( astI2P( *THIS ), key, *ELEM - 1, VALUE ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapgetelemb)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(ELEM),
UBYTE_ARRAY(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(ELEM)
GENPTR_UBYTE_ARRAY(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPGETELEMB", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGetElemB( astI2P( *THIS ), key, *ELEM - 1, VALUE ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapgetelemr)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(ELEM),
REAL_ARRAY(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(ELEM)
GENPTR_REAL_ARRAY(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
astAt( "AST_MAPGETELEMR", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGetElemF( astI2P( *THIS ), key, *ELEM - 1, VALUE ) ? F77_TRUE : F77_FALSE;
astFree( key );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapgetelema)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(ELEM),
INTEGER_ARRAY(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(ELEM)
GENPTR_INTEGER_ARRAY(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
AstObject *ptr;
astAt( "AST_MAPGETELEMA", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
RESULT = astMapGetElemA( astI2P( *THIS ), key, *ELEM - 1, &ptr ) ? F77_TRUE : F77_FALSE;
astFree( key );
if( astOK ) *VALUE = astP2I( ptr );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_mapgetelemc)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(ELEM),
CHARACTER_ARRAY(VALUE),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(VALUE) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(ELEM)
GENPTR_CHARACTER_ARRAY(VALUE)
F77_LOGICAL_TYPE(RESULT);
char *key;
char *values, *c, *d;
int j;
astAt( "AST_MAPGETELEMC", NULL, 0 );
astWatchSTATUS(
values = astMalloc( sizeof( char )*(size_t) ( VALUE_length + 1 ) );
key = astString( KEY, KEY_length );
RESULT = astMapGetElemC( astI2P( *THIS ), key, VALUE_length + 1, *ELEM - 1,
values ) ? F77_TRUE : F77_FALSE;
astFree( key );
/* Copy characters from the work array until a terminating null is
found. Replace this null by a space and replace all subsequent
characters by spaces up to the end of the returned array element. */
if( astOK ) {
c = values;
d = VALUE;
for( j = 0; j < VALUE_length; j++, d++, c++ ) {
if( (*d = *c) == 0 ) {
*d = ' ';
break;
}
}
for( ; j < VALUE_length; j++, d++ ) *d = ' ';
}
astFree( values );
)
return RESULT;
}
F77_SUBROUTINE(ast_mapputelemi)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(ELEM),
INTEGER(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(ELEM)
GENPTR_INTEGER(VALUE)
char *key;
astAt( "AST_MAPPUTELEMI", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
astMapPutElemI( astI2P( *THIS ), key, *ELEM - 1, *VALUE );
astFree( key );
)
}
F77_SUBROUTINE(ast_mapputelems)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(ELEM),
WORD(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(ELEM)
GENPTR_WORD(VALUE)
char *key;
astAt( "AST_MAPPUTELEMW", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
astMapPutElemS( astI2P( *THIS ), key, *ELEM - 1, *VALUE );
astFree( key );
)
}
F77_SUBROUTINE(ast_mapputelemb)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(ELEM),
UBYTE(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(ELEM)
GENPTR_UBYTE(VALUE)
char *key;
astAt( "AST_MAPPUTELEMB", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
astMapPutElemB( astI2P( *THIS ), key, *ELEM - 1, *VALUE );
astFree( key );
)
}
F77_SUBROUTINE(ast_mapputelemd)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(ELEM),
DOUBLE(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(ELEM)
GENPTR_DOUBLE(VALUE)
char *key;
astAt( "AST_MAPPUTELEMD", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
astMapPutElemD( astI2P( *THIS ), key, *ELEM - 1, *VALUE );
astFree( key );
)
}
F77_SUBROUTINE(ast_mapputelemr)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(ELEM),
REAL(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(ELEM)
GENPTR_REAL(VALUE)
char *key;
astAt( "AST_MAPPUTELEMR", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
astMapPutElemF( astI2P( *THIS ), key, *ELEM - 1, *VALUE );
astFree( key );
)
}
F77_SUBROUTINE(ast_mapputelema)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(ELEM),
INTEGER(VALUE),
INTEGER(STATUS)
TRAIL(KEY) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(ELEM)
GENPTR_INTEGER(VALUE)
char *key;
astAt( "AST_MAPPUTELEMA", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
astMapPutElemA( astI2P( *THIS ), key, *ELEM - 1, astI2P( *VALUE ) );
astFree( key );
)
}
F77_SUBROUTINE(ast_mapputelemc)( INTEGER(THIS),
CHARACTER(KEY),
INTEGER(ELEM),
CHARACTER(VALUE),
INTEGER(STATUS)
TRAIL(KEY)
TRAIL(VALUE) ) {
GENPTR_INTEGER(THIS)
GENPTR_CHARACTER(KEY)
GENPTR_INTEGER(ELEM)
GENPTR_CHARACTER(VALUE)
char *key;
char *value;
astAt( "AST_MAPPUTELEMC", NULL, 0 );
astWatchSTATUS(
key = astString( KEY, KEY_length );
value = astString( VALUE, VALUE_length );
astMapPutElemC( astI2P( *THIS ), key, *ELEM - 1, value );
astFree( key );
astFree( value );
)
}
ast-8.0.7/switchmap.c 0000664 0001750 0001750 00000311133 12610415012 011375 0000000 0000000 /*
*class++
* Name:
* SwitchMap
* Purpose:
* A Mapping that encapsulates a set of alternate Mappings.
* Constructor Function:
c astSwitchMap
f AST_SWITCHMAP
* Description:
* A SwitchMap is a Mapping which represents a set of alternate
* Mappings, each of which is used to transform positions within a
* particular region of the input or output coordinate system of the
* SwitchMap.
*
* A SwitchMap can encapsulate any number of Mappings, but they must
* all have the same number of inputs (Nin attribute value) and the
* same number of outputs (Nout attribute value). The SwitchMap itself
* inherits these same values for its Nin and Nout attributes. Each of
* these Mappings represents a "route" through the switch, and are
* referred to as "route" Mappings below. Each route Mapping transforms
* positions between the input and output coordinate space of the entire
* SwitchMap, but only one Mapping will be used to transform any given
* position. The selection of the appropriate route Mapping to use with
* any given input position is made by another Mapping, called the
* "selector" Mapping. Each SwitchMap encapsulates two selector
* Mappings in addition to its route Mappings; one for use with the
* SwitchMap's forward transformation (called the "forward selector
* Mapping"), and one for use with the SwitchMap's inverse transformation
* (called the "inverse selector Mapping"). The forward selector Mapping
* must have the same number of inputs as the route Mappings, but
* should have only one output. Likewise, the inverse selector Mapping
* must have the same number of outputs as the route Mappings, but
* should have only one input.
*
* When the SwitchMap is used to transform a position in the forward
* direction (from input to output), each supplied input position is
* first transformed by the forward transformation of the forward selector
* Mapping. This produces a single output value for each input position
* referred to as the selector value. The nearest integer to the selector
* value is found, and is used to index the array of route Mappings (the
* first supplied route Mapping has index 1, the second route Mapping has
* index 2, etc). If the nearest integer to the selector value is less
* than 1 or greater than the number of route Mappings, then the SwitchMap
* output position is set to a value of AST__BAD on every axis. Otherwise,
* the forward transformation of the selected route Mapping is used to
* transform the supplied input position to produce the SwitchMap output
* position.
*
* When the SwitchMap is used to transform a position in the inverse
* direction (from "output" to "input"), each supplied "output" position
* is first transformed by the inverse transformation of the inverse
* selector Mapping. This produces a selector value for each "output"
* position. Again, the nearest integer to the selector value is found,
* and is used to index the array of route Mappings. If this selector
* index value is within the bounds of the array of route Mappings, then
* the inverse transformation of the selected route Mapping is used to
* transform the supplied "output" position to produce the SwitchMap
* "input" position. If the selector index value is outside the bounds
* of the array of route Mappings, then the SwitchMap "input" position is
* set to a value of AST__BAD on every axis.
*
* In practice, appropriate selector Mappings should be chosen to
* associate a different route Mapping with each region of coordinate
* space. Note that the SelectorMap class of Mapping is particularly
* appropriate for this purpose.
*
* If a compound Mapping contains a SwitchMap in series with its own
* inverse, the combination of the two adjacent SwitchMaps will be
* replaced by a UnitMap when the compound Mapping is simplified using
c astSimplify.
f AST_SIMPLIFY.
* Inheritance:
* The SwitchMap class inherits from the Mapping class.
* Attributes:
* The SwitchMap class does not define any new attributes beyond those
* which are applicable to all Mappings.
* Functions:
c The SwitchMap class does not define any new functions beyond those
f The SwitchMap 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
* 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
* .
* Authors:
* DSB: David S. Berry (Starlink)
* History:
* 13-MAR-2006 (DSB):
* Original version.
* 17-MAR-2006 (DSB):
* Guard against AST__BAD selector values.
* 9-MAY-2006 (DSB):
* Check selector Mapping pointers are not NULL before calling
* astEqual in Equal.
*class--
*/
/* Module Macros. */
/* ============== */
/* Set the name of the class we are implementing. This indicates to
the header files that define class interfaces that they should make
"protected" symbols available. */
#define astCLASS SwitchMap
/* 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 "pointset.h" /* Sets of points/coordinates */
#include "mapping.h" /* Coordinate Mappings (parent class) */
#include "unitmap.h" /* Unit Mappings */
#include "channel.h" /* I/O channels */
#include "switchmap.h" /* Interface definition for this class */
#include "frame.h" /* Frames */
/* Error code definitions. */
/* ----------------------- */
#include "ast_err.h" /* AST error codes */
/* C header files. */
/* --------------- */
#include
#include
#include
#include
/* 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 int (* parent_getobjsize)( AstObject *, int * );
static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
#if defined(THREAD_SAFE)
static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * );
#endif
#ifdef THREAD_SAFE
/* Define how to initialise thread-specific globals. */
#define GLOBAL_inits \
globals->Class_Init = 0;
/* Create the function that initialises global data for this module. */
astMAKE_INITGLOBALS(SwitchMap)
/* Define macros for accessing each item of thread specific global data. */
#define class_init astGLOBAL(SwitchMap,Class_Init)
#define class_vtab astGLOBAL(SwitchMap,Class_Vtab)
#include
#else
/* Define the class virtual function table and its initialisation flag
as static variables. */
static AstSwitchMapVtab 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. */
AstSwitchMap *astSwitchMapId_( void *, void *, int, void **, const char *, ... );
/* Prototypes for Private Member Functions. */
/* ======================================== */
static AstMapping *RemoveRegions( AstMapping *, int * );
static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
static double Rate( AstMapping *, double *, int, int, int * );
static int Equal( AstObject *, AstObject *, int * );
static int GetObjSize( AstObject *, int * );
static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * );
static void Copy( const AstObject *, AstObject *, int * );
static void Delete( AstObject *, int * );
static void Dump( AstObject *, AstChannel *, int * );
static AstMapping *GetSelector( AstSwitchMap *, int, int *, int * );
static AstMapping *GetRoute( AstSwitchMap *, double, int *, int * );
#if defined(THREAD_SAFE)
static int ManageLock( AstObject *, int, int, AstObject **, int * );
#endif
/* Member functions. */
/* ================= */
static int Equal( AstObject *this_object, AstObject *that_object, int *status ) {
/*
* Name:
* Equal
* Purpose:
* Test if two SwitchMaps are equivalent.
* Type:
* Private function.
* Synopsis:
* #include "switchmap.h"
* int Equal( AstObject *this, AstObject *that, int *status )
* Class Membership:
* SwitchMap member function (over-rides the astEqual protected
* method inherited from the astMapping class).
* Description:
* This function returns a boolean result (0 or 1) to indicate whether
* two SwitchMaps are equivalent.
* Parameters:
* this
* Pointer to the first Object (a SwitchMap).
* that
* Pointer to the second Object.
* status
* Pointer to the inherited status variable.
* Returned Value:
* One if the SwitchMaps are equivalent, zero otherwise.
* 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: */
AstMapping *fsmap1;
AstMapping *fsmap2;
AstMapping *ismap1;
AstMapping *ismap2;
AstMapping *rmap1;
AstMapping *rmap2;
AstSwitchMap *that;
AstSwitchMap *this;
int fsinv1;
int fsinv2;
int isinv1;
int i;
int isinv2;
int nroute;
int result;
int rinv1;
int rinv2;
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain pointers to the two SwitchMap structures. */
this = (AstSwitchMap *) this_object;
that = (AstSwitchMap *) that_object;
/* Check the second object is a SwitchMap. We know the first is a
SwitchMap since we have arrived at this implementation of the virtual
function. */
if( astIsASwitchMap( that ) ) {
/* Check they have the same number of route mappings. */
nroute = this->nroute;
if( that->nroute == nroute ) {
/* Get the forward selector Mappings from the two SwitchMaps. */
fsmap1 = GetSelector( this, 1, &fsinv1, status );
fsmap2 = GetSelector( that, 1, &fsinv2, status );
/* Are they equal? */
if( ( !fsmap1 && !fsmap2 ) ||
( fsmap1 && fsmap2 && astEqual( fsmap1, fsmap2 ) ) ) {
/* Get the inverse selector Mappings from the two SwitchMaps. */
ismap1 = GetSelector( this, 0, &isinv1, status );
ismap2 = GetSelector( that, 0, &isinv2, status );
/* Are they equal? */
if( ( !ismap1 && !ismap2 ) ||
( ismap1 && ismap2 && astEqual( ismap1, ismap2 ) ) ) {
/* Loop over the route mappings, breaking as soon as two unequal route
Mappings are found. Re-instate the original values for the route
Mapping Invert flag after testing the route Mappings for equality. */
result = 1;
for( i = 0; result && i < nroute; i++ ) {
rmap1 = GetRoute( this, (double) ( i + 1 ), &rinv1, status );
rmap2 = GetRoute( that, (double) ( i + 1 ), &rinv2, status );
if( !astEqual( rmap1, rmap2 ) ) result = 0;
astSetInvert( rmap2, rinv2 );
astSetInvert( rmap1, rinv1 );
}
}
/* Reinstate the invert flags for the inverse selector Mappings. Ensure
this is done in the opposite order to which the selector Mappings were
obtained (in case they are in fact the same Mapping). */
if( ismap2 ) astSetInvert( ismap2, isinv2 );
if( ismap1 ) astSetInvert( ismap1, isinv1 );
}
/* Reinstate the invert flags for the forward selector Mappings. Ensure
this is done in the oppsote order to which the selector Mappings were
obtained (in case they are in fact the same Mapping). */
if( fsmap2 ) astSetInvert( fsmap2, fsinv2 );
if( fsmap1 ) astSetInvert( fsmap1, fsinv1 );
}
}
/* If an error occurred, clear the result value. */
if ( !astOK ) result = 0;
/* Return the result, */
return result;
}
static int GetObjSize( AstObject *this_object, int *status ) {
/*
* Name:
* GetObjSize
* Purpose:
* Return the in-memory size of an Object.
* Type:
* Private function.
* Synopsis:
* #include "switchmap.h"
* int GetObjSize( AstObject *this, int *status )
* Class Membership:
* SwitchMap member function (over-rides the astGetObjSize protected
* method inherited from the parent class).
* Description:
* This function returns the in-memory size of the supplied SwitchMap,
* in bytes.
* Parameters:
* this
* Pointer to the SwitchMap.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The Object size, in bytes.
* 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: */
AstSwitchMap *this;
int i;
int result;
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain a pointers to the SwitchMap structure. */
this = (AstSwitchMap *) this_object;
/* Invoke the GetObjSize method inherited from the parent class, and then
add on any components of the class structure defined by this class
which are stored in dynamically allocated memory. */
result = (*parent_getobjsize)( this_object, status );
result += astGetObjSize( this->fsmap );
result += astGetObjSize( this->ismap );
for( i = 0; i < this->nroute; i++ ) {
result += astGetObjSize( this->routemap[ i ] );
}
result += astGetObjSize( this->routeinv );
/* If an error occurred, clear the result value. */
if ( !astOK ) result = 0;
/* Return the result, */
return result;
}
static AstMapping *GetRoute( AstSwitchMap *this, double sel, int *inv, int *status ){
/*
* Name:
* GetRoute
* Purpose:
* Return a pointer to a route Mapping, handling all Invert flags.
* Type:
* Private function.
* Synopsis:
* #include "switchmap.h"
* AstMapping *GetRoute( AstSwitchMap *this, double sel, int *inv, int *status )
* Class Membership:
* SwitchMap method.
* Description:
* This function returns a pointer to a route Mapping (specified by a
* floating point selector value) for the given SwitchMap, taking account
* of the state of the Invert flag of both the route Mapping and the
* SwitchMap.
* Parameters:
* this
* Pointer to the SwitchMap.
* sel
* The selector value. The nearest integer value (minus 1) is used
* to index the array of route Mappings stored in the SwitchMap. A
* NULL pointer is returned if the selector value is out of range.
* inv
* Pointer to an int in which to return the original value of the
* Invert flag of the returned Mapping. The astSetInvert method
* should be used to re-instate this value once all use of the Mapping
* has been completed.
* status
* Pointer to the inherited status variable.
* Returns:
* A pointer to the route Mapping to use. Note, the returned pointer
* should NOT be annulled when no longer needed. NULL is returned
* (without error) if the SwitchMap does not have a route Mapping for the
* requested selector value. The forward transformation of the
* returned Mapping will implenment the forward transformation of the
* required route Mapping (and vice-versa).
*/
/* Local Variables: */
AstMapping *ret;
int rindex;
/* Initialise */
ret = NULL;
/* Check the global error status. */
if ( !astOK ) return ret;
/* Check selector value is good. */
if( sel != AST__BAD ) {
/* Convert the supplied floating point selector value into an integer
index into the array of route Mappings held in the supplied SwitchMap. */
rindex = (int)( sel + 0.5 ) - 1;
/* Return the null pointer if the index is out of range. */
if( rindex >= 0 && rindex < this->nroute ) {
/* Get the required route Mapping. */
ret = ( this->routemap )[ rindex ];
/* Return its original invert flag. */
*inv = astGetInvert( ret );
/* Set the Invert flag back to the value it had when the SwitchMap was
created. */
astSetInvert( ret, this->routeinv[ rindex ] );
/* If the SwitchMap has since been inverted, also invert the returned
route Mapping, so that the forward transformation of the returned
Mapping implements the forward transformation of the supplied
SwitchMap (and vice-versa). */
if( astGetInvert( this ) ) astInvert( ret );
}
}
/* Return the pointer. */
return ret;
}
static AstMapping *GetSelector( AstSwitchMap *this, int fwd, int *inv, int *status ){
/*
* Name:
* GetSelector
* Purpose:
* Return a pointer to a selector Mapping, handling all Invert flags.
* Type:
* Private function.
* Synopsis:
* #include "switchmap.h"
* AstMapping *GetSelector( AstSwitchMap *this, int fwd, int *inv, int *status )
* Class Membership:
* SwitchMap method.
* Description:
* This function returns a pointer to either the forward or inverse
* selector Mapping for the given SwitchMap, taking account of the
* state of the Invert flag of bothe the selector Mapping and the
* SwitchMap.
* Parameters:
* this
* Pointer to the SwitchMap.
* fwd
* If non-zero, return the forward selector Mapping. Otherwise,
* return the inverse selector Mapping.
* inv
* Pointer to an int in which to return the original value of the
* Invert flag of the returned Mapping. The astSetInvert method
* should be used to re-instate this value once all use of the Mapping
* has been completed.
* status
* Pointer to the inherited status variable.
* Returns:
* A pointer to the selector Mapping to use. Note, the returned pointer
* should NOT be annulled when no longer needed. NULL is returned
* (without error) if the SwitchMap does not have a Mapping for the
* requested selector.
*/
/* Local Variables: */
AstMapping *ret;
int swinv;
/* Initialise */
ret = NULL;
/* Check the global error status. */
if ( !astOK ) return ret;
/* See if the SwitchMap has been inverted. */
swinv = astGetInvert( this );
/* If the SwitchMap has been inverted, the forward and inverse selector
Mappings should be reversed. */
if( ( !swinv && !fwd ) || ( swinv && fwd ) ){
ret = this->ismap;
if( ret ) {
*inv = astGetInvert( ret );
astSetInvert( ret, this->isinv );
}
} else {
ret = this->fsmap;
if( ret ) {
*inv = astGetInvert( ret );
astSetInvert( ret, this->fsinv );
}
}
if( ret && swinv ) astInvert( ret );
/* Return the pointer. */
return ret;
}
void astInitSwitchMapVtab_( AstSwitchMapVtab *vtab, const char *name, int *status ) {
/*
*+
* Name:
* astInitSwitchMapVtab
* Purpose:
* Initialise a virtual function table for a SwitchMap.
* Type:
* Protected function.
* Synopsis:
* #include "switchmap.h"
* void astInitSwitchMapVtab( AstSwitchMapVtab *vtab, const char *name )
* Class Membership:
* SwitchMap vtab initialiser.
* Description:
* This function initialises the component of a virtual function
* table which is used by the SwitchMap 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 */
AstMappingVtab *mapping; /* Pointer to Mapping 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. */
astInitMappingVtab( (AstMappingVtab *) vtab, name );
/* Store a unique "magic" value in the virtual function table. This
will be used (by astIsASwitchMap) 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 = &(((AstMappingVtab *) vtab)->id);
/* Initialise member function pointers. */
/* ------------------------------------ */
/* Store pointers to the member functions (implemented here) that
provide virtual methods for this class. */
/* None. */
/* Save the inherited pointers to methods that will be extended, and
replace them with pointers to the new member functions. */
object = (AstObjectVtab *) vtab;
mapping = (AstMappingVtab *) vtab;
parent_getobjsize = object->GetObjSize;
object->GetObjSize = GetObjSize;
#if defined(THREAD_SAFE)
parent_managelock = object->ManageLock;
object->ManageLock = ManageLock;
#endif
parent_transform = mapping->Transform;
mapping->Transform = Transform;
/* Store replacement pointers for methods which will be over-ridden by
new member functions implemented here. */
object->Equal = Equal;
mapping->MapMerge = MapMerge;
mapping->Rate = Rate;
mapping->RemoveRegions = RemoveRegions;
/* Declare the copy constructor, destructor and class dump function. */
astSetCopy( vtab, Copy );
astSetDelete( vtab, Delete );
astSetDump( vtab, Dump, "SwitchMap", "Alternate regionalised Mapping" );
/* 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) );
}
}
#if defined(THREAD_SAFE)
static int ManageLock( AstObject *this_object, int mode, int extra,
AstObject **fail, int *status ) {
/*
* Name:
* ManageLock
* Purpose:
* Manage the thread lock on an Object.
* Type:
* Private function.
* Synopsis:
* #include "object.h"
* AstObject *ManageLock( AstObject *this, int mode, int extra,
* AstObject **fail, int *status )
* Class Membership:
* SwitchMap member function (over-rides the astManageLock protected
* method inherited from the parent class).
* Description:
* This function manages the thread lock on the supplied Object. The
* lock can be locked, unlocked or checked by this function as
* deteremined by parameter "mode". See astLock for details of the way
* these locks are used.
* Parameters:
* this
* Pointer to the Object.
* mode
* An integer flag indicating what the function should do:
*
* AST__LOCK: Lock the Object for exclusive use by the calling
* thread. The "extra" value indicates what should be done if the
* Object is already locked (wait or report an error - see astLock).
*
* AST__UNLOCK: Unlock the Object for use by other threads.
*
* AST__CHECKLOCK: Check that the object is locked for use by the
* calling thread (report an error if not).
* extra
* Extra mode-specific information.
* fail
* If a non-zero function value is returned, a pointer to the
* Object that caused the failure is returned at "*fail". This may
* be "this" or it may be an Object contained within "this". Note,
* the Object's reference count is not incremented, and so the
* returned pointer should not be annulled. A NULL pointer is
* returned if this function returns a value of zero.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A local status value:
* 0 - Success
* 1 - Could not lock or unlock the object because it was already
* locked by another thread.
* 2 - Failed to lock a POSIX mutex
* 3 - Failed to unlock a POSIX mutex
* 4 - Bad "mode" value supplied.
* Notes:
* - This function attempts to execute even if an error has already
* occurred.
*/
/* Local Variables: */
AstSwitchMap *this; /* Pointer to SwitchMap structure */
int i; /* Loop count */
int result; /* Returned status value */
/* Initialise */
result = 0;
/* Check the supplied pointer is not NULL. */
if( !this_object ) return result;
/* Obtain a pointers to the SwitchMap structure. */
this = (AstSwitchMap *) this_object;
/* Invoke the ManageLock method inherited from the parent class. */
if( !result ) result = (*parent_managelock)( this_object, mode, extra,
fail, status );
/* Invoke the astManageLock method on any Objects contained within
the supplied Object. */
if( !result ) result = astManageLock( this->fsmap, mode, extra, fail );
if( !result ) result = astManageLock( this->ismap, mode, extra, fail );
for( i = 0; i < this->nroute; i++ ) {
if( !result ) result = astManageLock( this->routemap[ i ], mode,
extra, fail );
}
return result;
}
#endif
static int MapMerge( AstMapping *this, int where, int series, int *nmap,
AstMapping ***map_list, int **invert_list, int *status ) {
/*
* Name:
* MapMerge
* Purpose:
* Simplify a sequence of Mappings containing a SwitchMap.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* int MapMerge( AstMapping *this, int where, int series, int *nmap,
* AstMapping ***map_list, int **invert_list, int *status )
* Class Membership:
* SwitchMap method (over-rides the protected astMapMerge method
* inherited from the Mapping class).
* Description:
* This function attempts to simplify a sequence of Mappings by
* merging a nominated SwitchMap in the sequence with its neighbours,
* so as to shorten the sequence if possible.
*
* In many cases, simplification will not be possible and the
* function will return -1 to indicate this, without further
* action.
*
* In most cases of interest, however, this function will either
* attempt to replace the nominated SwitchMap with one which it
* considers simpler, or to merge it with the Mappings which
* immediately precede it or follow it in the sequence (both will
* normally be considered). This is sufficient to ensure the
* eventual simplification of most Mapping sequences by repeated
* application of this function.
*
* In some cases, the function may attempt more elaborate
* simplification, involving any number of other Mappings in the
* sequence. It is not restricted in the type or scope of
* simplification it may perform, but will normally only attempt
* elaborate simplification in cases where a more straightforward
* approach is not adequate.
* Parameters:
* this
* Pointer to the nominated SwitchMap which is to be merged with
* its neighbours. This should be a cloned copy of the SwitchMap
* pointer contained in the array element "(*map_list)[where]"
* (see below). This pointer will not be annulled, and the
* SwitchMap it identifies will not be modified by this function.
* where
* Index in the "*map_list" array (below) at which the pointer
* to the nominated SwitchMap resides.
* series
* A non-zero value indicates that the sequence of Mappings to
* be simplified will be applied in series (i.e. one after the
* other), whereas a zero value indicates that they will be
* applied in parallel (i.e. on successive sub-sets of the
* input/output coordinates).
* nmap
* Address of an int which counts the number of Mappings in the
* sequence. On entry this should be set to the initial number
* of Mappings. On exit it will be updated to record the number
* of Mappings remaining after simplification.
* map_list
* Address of a pointer to a dynamically allocated array of
* Mapping pointers (produced, for example, by the astMapList
* method) which identifies the sequence of Mappings. On entry,
* the initial sequence of Mappings to be simplified should be
* supplied.
*
* On exit, the contents of this array will be modified to
* reflect any simplification carried out. Any form of
* simplification may be performed. This may involve any of: (a)
* removing Mappings by annulling any of the pointers supplied,
* (b) replacing them with pointers to new Mappings, (c)
* inserting additional Mappings and (d) changing their order.
*
* The intention is to reduce the number of Mappings in the
* sequence, if possible, and any reduction will be reflected in
* the value of "*nmap" returned. However, simplifications which
* do not reduce the length of the sequence (but improve its
* execution time, for example) may also be performed, and the
* sequence might conceivably increase in length (but normally
* only in order to split up a Mapping into pieces that can be
* more easily merged with their neighbours on subsequent
* invocations of this function).
*
* If Mappings are removed from the sequence, any gaps that
* remain will be closed up, by moving subsequent Mapping
* pointers along in the array, so that vacated elements occur
* at the end. If the sequence increases in length, the array
* will be extended (and its pointer updated) if necessary to
* accommodate any new elements.
*
* Note that any (or all) of the Mapping pointers supplied in
* this array may be annulled by this function, but the Mappings
* to which they refer are not modified in any way (although
* they may, of course, be deleted if the annulled pointer is
* the final one).
* invert_list
* Address of a pointer to a dynamically allocated array which,
* on entry, should contain values to be assigned to the Invert
* attributes of the Mappings identified in the "*map_list"
* array before they are applied (this array might have been
* produced, for example, by the astMapList method). These
* values will be used by this function instead of the actual
* Invert attributes of the Mappings supplied, which are
* ignored.
*
* On exit, the contents of this array will be updated to
* correspond with the possibly modified contents of the
* "*map_list" array. If the Mapping sequence increases in
* length, the "*invert_list" array will be extended (and its
* pointer updated) if necessary to accommodate any new
* elements.
* status
* Pointer to the inherited status variable.
* Returned Value:
* If simplification was possible, the function returns the index
* in the "map_list" array of the first element which was
* modified. Otherwise, it returns -1 (and makes no changes to the
* arrays supplied).
* Notes:
* - A value of -1 will be returned if this function is invoked
* with the global error status set, or if it should fail for any
* reason.
*/
/* Local Variables: */
AstSwitchMap *map;
AstMapping *new;
int i;
int nroute;
int result;
int fsinv_old;
int isinv_old;
int *rinv_old;
AstMapping *sfsmap;
AstMapping *sismap;
int simp;
AstMapping **srmap;
AstSwitchMap *swneb;
int ilo;
int equal;
/* Initialise.*/
result = -1;
/* Check the inherited status. */
if ( !astOK ) return result;
/* Get a pointer to this SwitchMap, and note the number of route Mappings. */
map = (AstSwitchMap *) this;
nroute = map->nroute;
/* Temporarily put the Invert flag of all encapsulated Mappings (both
route and selector) back to the values they had when the SwitchMap was
created, noting their current values so that they can be re-instated
later. If the SwitchMap itself has been inverted, swap all the original
invert flags. */
if( map->fsmap ) {
fsinv_old = astGetInvert( map->fsmap );
astSetInvert( map->fsmap, map->fsinv );
} else {
fsinv_old = 0;
}
if( map->ismap ) {
isinv_old = astGetInvert( map->ismap );
astSetInvert( map->ismap, map->isinv );
} else {
isinv_old = 0;
}
rinv_old = astMalloc( sizeof( int )*nroute );
if( astOK ) {
for( i = 0; i < nroute; i++ ) {
rinv_old[ i ] = astGetInvert( map->routemap[ i ] );
astSetInvert( map->routemap[ i ], map->routeinv[ i ] );
}
}
/* If possible, merge the SwitchMap with a neighbouring SwitchMap. */
/* =============================================================== */
/* Only do this if we are combining the Mappings in series. */
if( series ) {
/* Is the higher neighbour a SwitchMap? If so get a pointer to it, and
note the index of the lower of the two adjacent SwitchMaps. */
if( where < ( *nmap - 1 ) &&
astIsASwitchMap( ( *map_list )[ where + 1 ] ) ){
swneb = (AstSwitchMap *) ( *map_list )[ where + 1 ];
ilo = where;
/* If not, is the lower neighbour a SwitchMap? If so get a pointer to it, and
note the index of the lower of the two adjacent SwitchMaps. */
} else if( where > 0 &&
astIsASwitchMap( ( *map_list )[ where - 1 ] ) ){
swneb = (AstSwitchMap *) ( *map_list )[ where - 1 ];
ilo = where - 1;
} else {
swneb = NULL;
}
/* If a neighbouring SwitchMap was found, we can replace the pair by a
UnitMap if the two SwitchMaps are equal but have opposite values for
their Invert flags. Temporarily invert the neighbour, then compare
the two SwitchMaps for equality, then re-invert the neighbour. */
if( swneb ) {
astInvert( swneb );
equal = astEqual( map, swneb );
astInvert( swneb );
/* If the two SwitchMaps are equal but opposite, annul the first of the two
Mappings, and replace it with a UnitMap. Also set the invert flag. */
if( equal ) {
new = (AstMapping *) astUnitMap( astGetNin( ( *map_list )[ ilo ] ), "", status );
(void) astAnnul( ( *map_list )[ ilo ] );
( *map_list )[ ilo ] = new;
( *invert_list )[ ilo ] = 0;
/* Annul the second of the two Mappings, and shuffle down the rest of the
list to fill the gap. */
(void) astAnnul( ( *map_list )[ ilo + 1 ] );
for ( i = ilo + 2; i < *nmap; i++ ) {
( *map_list )[ i - 1 ] = ( *map_list )[ i ];
( *invert_list )[ i - 1 ] = ( *invert_list )[ i ];
}
/* Clear the vacated element at the end. */
( *map_list )[ *nmap - 1 ] = NULL;
( *invert_list )[ *nmap - 1 ] = 0;
/* Decrement the Mapping count and return the index of the first
modified element. */
( *nmap )--;
result = where;
}
}
}
/* Attempt to simplify the SwitchMap on its own. */
/* ============================================= */
/* Only do this if no change was made above. */
if( result == -1 ) {
/* If the SwitchMap is inverted, create an equal SwitchMap which is not
inverted. To do this, invert and swap the selector Mappings, and
invert all the route Mappings. We use astSetInvert rather than astInvert
because two or more more stored pointers may point to the same Mapping
in which case that Mapping would be inverted more than once with
unpredictable results. */
if( ( *invert_list )[ where ] ) {
if( map->fsmap ) astSetInvert( map->fsmap, !(map->fsinv) );
if( map->ismap ) astSetInvert( map->ismap, !(map->isinv) );
for( i = 0; i < nroute; i++ ) {
astSetInvert( map->routemap[ i ], !(map->routeinv[ i ]) );
}
new = (AstMapping *) astSwitchMap( map->ismap, map->fsmap, nroute, (void **) map->routemap, "", status );
(void) astAnnul( ( *map_list )[ where ] );
( *map_list )[ where ] = (AstMapping *) new;
( *invert_list )[ where ] = 0;
result = where;
/* Otherwise, try to simplify each of the encapsulated Mappings, noting
if any simplification takes place. */
} else {
sfsmap = ( map->fsmap ) ? astSimplify( map->fsmap ) : NULL;
sismap = ( map->ismap ) ? astSimplify( map->ismap ) : NULL;
simp = ( sfsmap != map->fsmap ) || ( sismap != map->ismap );
srmap = astMalloc( sizeof( AstMapping * )*nroute );
if( astOK ) {
for( i = 0; i < nroute; i++ ) {
srmap[ i ] = astSimplify( map->routemap[ i ] );
simp = simp || ( srmap[ i ] != map->routemap[ i ] );
}
}
/* If any simplification took place, construct a new SwitchMap from these
simplified Mappings. */
if( simp ) {
(void) astAnnul( ( *map_list )[ where ] );
( *map_list )[ where ] = (AstMapping *) astSwitchMap( sfsmap, sismap,
nroute, (void **) srmap, "", status );
result = where;
}
/* Release resources. */
if( sfsmap ) sfsmap = astAnnul( sfsmap );
if( sismap ) sismap = astAnnul( sismap );
if( srmap ) {
for( i = 0; i < nroute; i++ ) srmap[ i ] = astAnnul( srmap[ i ] );
srmap = astFree( srmap );
}
}
}
/* Re-instate the original Invert values for the encapsulated Mappings. */
if( map->fsmap ) astSetInvert( map->fsmap, fsinv_old );
if( map->ismap ) astSetInvert( map->ismap, isinv_old );
if( rinv_old ) {
for( i = 0; i < nroute; i++ ) {
astSetInvert( map->routemap[ i ], rinv_old[ i ] );
}
rinv_old = astFree( rinv_old );
}
/* If an error occurred, clear the result value. */
if ( !astOK ) result = -1;
/* Return the result. */
return result;
}
static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){
/*
* Name:
* Rate
* Purpose:
* Calculate the rate of change of a Mapping output.
* Type:
* Private function.
* Synopsis:
* #include "switchmap.h"
* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status )
* Class Membership:
* SwitchMap member function (overrides the astRate method inherited
* from the Mapping class ).
* Description:
* This function returns the rate of change of a specified output of
* the supplied Mapping with respect to a specified input, at a
* specified input position. Also evaluates the second derivative.
* Parameters:
* this
* Pointer to the Mapping to be applied.
* at
* The address of an array holding the axis values at the position
* at which the rate of change is to be evaluated. The number of
* elements in this array should equal the number of inputs to the
* Mapping.
* ax1
* The index of the Mapping output for which the rate of change is to
* be found (output numbering starts at 0 for the first output).
* ax2
* The index of the Mapping input which is to be varied in order to
* find the rate of change (input numbering starts at 0 for the first
* input).
* status
* Pointer to the inherited status variable.
* Returned Value:
* The rate of change of Mapping output "ax1" with respect to input
* "ax2", evaluated at "at", or AST__BAD if the value cannot be
* calculated.
*/
/* Local Variables: */
AstSwitchMap *map;
AstMapping *smap;
AstMapping *rmap;
double result;
double sel;
int fsinv;
int rinv;
int nin;
/* Initialise. */
result = AST__BAD;
/* Check inherited status */
if( !astOK ) return result;
/* Get a pointer to the SwitchMap structure. */
map = (AstSwitchMap *) this;
/* Get a pointer to the effective foward selector Mapping, and its current
invert flag (this takes account of whether the SwtichMap has been
inverted or not). This call resets the selector's invert flag temporarily
back to the value it had when the SwitchMap was created. */
smap = GetSelector( map, 1, &fsinv, status );
/* If the SwitchMap has no forward selector Mapping, return AST__BAD. */
if( smap ) {
/* Get the number of inputs */
nin = astGetNin( smap );
/* Transform the supplied position using the selector Mapping. The output
value is the selector value that indicates which route Mapping to use. */
astTranN( smap, 1, nin, 1, at, 1, 1, 1, &sel );
/* Get the index of the route Mapping to use, and check it is valid (if
not, return AST__BAD if not). This takes account of whether the
SwitchMap has been inverted, and also temporarily re-instates the
original value of the route Mapping's Invert flag . */
rmap = GetRoute( map, sel, &rinv, status );
if( rmap ) {
/* Use the astRate method of the route Mapping. */
result = astRate( rmap, at, ax1, ax2 );
/* Reset the Invert flag for the route Mapping. */
astSetInvert( rmap, rinv );
}
/* Reset the Invert flag for the selector Mapping. */
astSetInvert( smap, fsinv );
}
/* Return the result. */
return result;
}
static AstMapping *RemoveRegions( AstMapping *this_mapping, int *status ) {
/*
* Name:
* RemoveRegions
* Purpose:
* Remove any Regions from a Mapping.
* Type:
* Private function.
* Synopsis:
* #include "switchmap.h"
* AstMapping *RemoveRegions( AstMapping *this, int *status )
* Class Membership:
* SwitchMap method (over-rides the astRemoveRegions method inherited
* from the Mapping class).
* Description:
* This function searches the supplied Mapping (which may be a
* compound Mapping such as a SwitchMap) for any component Mappings
* that are instances of the AST Region class. It then creates a new
* Mapping from which all Regions have been removed. If a Region
* cannot simply be removed (for instance, if it is a component of a
* parallel SwitchMap), then it is replaced with an equivalent UnitMap
* in the returned Mapping.
*
* The implementation provided by the SwitchMap class invokes the
* astRemoveRegions method on all the component Mappings, and joins
* the results together into a new SwitchMap.
* Parameters:
* this
* Pointer to the original Region.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the modified mapping.
* Notes:
* - A NULL pointer value will be returned if this function is
* invoked with the AST error status set, or if it should fail for
* any reason.
*/
/* Local Variables: */
AstMapping **temp; /* Array of new route Mappings */
AstMapping *newfsmap; /* New forward selector Mapping */
AstMapping *newismap; /* New inverse selector Mapping */
AstMapping *result; /* Result pointer to return */
AstSwitchMap *new; /* Pointer to new SwitchMap */
AstSwitchMap *this; /* Pointer to SwitchMap structure */
int changed; /* Has any mapping been changed? */
int i; /* Loop count */
int nax; /* Number of Frame axes */
/* Initialise. */
result = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Get a pointer to the SwitchMap. */
this = (AstSwitchMap *) this_mapping;
/* Allocate an array to hold the modified Mapping pointers. */
temp = astMalloc( sizeof( AstMapping *)*( this->nroute ) );
if( astOK ) {
/* Invoke the astRemoveRegions method on all the component Mappings. */
changed = 0;
for( i = 0; i < this->nroute; i++ ) {
temp[ i ] = astRemoveRegions( this->routemap[ i ] );
/* Note if any Mapping was changed. */
if( temp[ i ] != this->routemap[ i ] ) {
changed = 1;
/* The implementation of the astRemoveRegions method provided by the
Region class returns a Frame rather than a UnitMap. But we need
Mappings here, not Frames. So if the new Mapping is a Frame, replace
it with an equivalent UnitMap. */
if( astIsAFrame( temp[ i ] ) ) {
nax = astGetNin( temp[ i ] );
(void) astAnnul( temp[ i ] );
temp[ i ] = (AstMapping *) astUnitMap( nax, " ", status );
}
}
}
/* And on the other ancillary Mappings */
if( this->fsmap ) {
newfsmap = astRemoveRegions( this->fsmap );
if( newfsmap != this->fsmap ) {
changed = 1;
if( astIsAFrame( newfsmap ) ) {
nax = astGetNin( newfsmap );
(void) astAnnul( newfsmap );
newfsmap = (AstMapping *) astUnitMap( nax, " ", status );
}
}
} else {
newfsmap = NULL;
}
if( this->ismap ) {
newismap = astRemoveRegions( this->ismap );
if( newismap != this->ismap ) {
changed = 1;
if( astIsAFrame( newismap ) ) {
nax = astGetNin( newismap );
(void) astAnnul( newismap );
newismap = (AstMapping *) astUnitMap( nax, " ", status );
}
}
} else {
newismap = NULL;
}
/* If no component was modified, just return a clone of the supplied
pointer. */
if( ! changed ) {
result = astClone( this );
/* Otherwise, we need to create a new Mapping to return. We take a deep
copy of the supplied SwitchMap and then modify the Mappings so that
we retain any extra information in the supplied SwitchMap. */
} else {
new = astCopy( this );
for( i = 0; i < this->nroute; i++ ) {
(void) astAnnul( new->routemap[ i ] );
new->routemap[ i ] = astClone( temp[ i ] );
}
if( newfsmap ) {
(void) astAnnul( new->fsmap );
new->fsmap = astClone( newfsmap );
}
if( newismap ) {
(void) astAnnul( new->ismap );
new->ismap = astClone( newismap );
}
result = (AstMapping *) new;
}
/* Free resources. */
for( i = 0; i < this->nroute; i++ ) {
temp[ i ] = astAnnul( temp[ i ] );
}
if( newfsmap ) newfsmap = astAnnul( newfsmap );
if( newismap ) newismap = astAnnul( newismap );
}
temp = astFree( temp );
/* Annul the returned Mapping if an error has occurred. */
if( !astOK ) result = astAnnul( result );
/* Return the result. */
return result;
}
int astSwitchList_( AstSwitchMap *this, int invert, int *nmap,
AstMapping ***map_list, int **invert_list, int *status ) {
/*
*+
* Name:
* astSwitchList
* Purpose:
* Extract the selector and route Mappings from a SwitchMap.
* Type:
* Protected function.
* Synopsis:
* #include "switchmap.h"
* int astSwitchList( AstSwitchMap *this, int invert, int *nmap,
* AstMapping ***map_list, int **invert_list )
* Class Membership:
* SwitchMap member function.
* Description:
* This function extracts the route and selector Mappings form a
* SwitchMap.
* Parameters:
* this
* Pointer to the SwitchMap to be decomposed (it is not actually
* modified by this function).
* invert
* The value to which the SwitchMap's Invert attribute is to be
* (notionally) set before performing the decomposition. Normally,
* the value supplied here will be the actual Invert value obtained
* from the SwitchMap (e.g. using astGetInvert). Sometimes, however,
* when a SwitchMap is encapsulated within another structure, that
* structure may retain an Invert value (in order to prevent external
* interference) which should be used instead.
*
* Note that the actual Invert value of the SwitchMap supplied is
* not used (or modified) by this function.
* nmap
* The address of an int in which to return a count of the number of
* individual Mappings in the decomposition. The supplied value is
* ignored.
* map_list
* Address of a pointer to an array of Mapping pointers. The value
* supplied on entry is ignored. On exit, it points at a dynamically
* allocated array containing Mapping pointers ("*nmap" in number) that
* result from the decomposition requested.
*
* The returned Mapping pointers returned will identify the following
* sequence of Mappings; forward selector mapping (or NULL if the
* SwitchMap has no forward selector Mapping), inverse selector
* mapping (or NULL if the SwitchMap has no inverse selector Mapping),
* the route Mappings in the order they were supplied when the
* SwitchMap was constructed.
*
* All the Mapping pointers returned by this function should be
* annulled by the caller, using astAnnul, when no longer
* required. The dynamic array holding these pointers should
* also be freed, using astFree.
* invert_list
* Address of a pointer to an array of int. The value supplied on
* entry is ignored. On exit, it points at a dynamically allocated
* array containing Invert attribute values ("*nmap" in number) that
* result from the decomposition requested.
*
* The returned Invert values returned identify the values which must
* be assigned to the Invert attributes of the corresponding
* Mappings (whose pointers are in the "*map_list" array) before
* they are applied. Note that these values may differ from the
* actual Invert attribute values of these Mappings, which are
* not relevant.
*
* The dynamic array holding these values should be freed by the
* caller, using astFree, when no longer required.
* Returned Value:
* The number of route Mappings stored in the SwitchMap.
* Notes:
* - It is unspecified to what extent the original SwitchMap and the
* individual (decomposed) Mappings are inter-dependent. Consequently,
* the individual Mappings cannot be modified without risking
* modification of the original SwitchMap.
* - If this function is invoked with the global error status set,
* or if it should fail for any reason, then the *nmap value, the
* list of Mapping pointers and the list of Invert values will all
* be returned unchanged.
*-
*/
/* Local Variables: */
AstMapping *map; /* Pointer to Mapping to return */
int inv; /* Original Invert flag for Mapping */
int i; /* Route Mapping index */
int oldinv; /* Original Invert flag for SwitchMap */
int result; /* Returned value */
/* Check the global error status. */
if ( !astOK ) return 0;
/* Store the numbe of route Mappings */
result = this->nroute;
*nmap = result + 2;
/* Allocate the required arrays. */
*map_list = astMalloc( sizeof( AstMapping * )*(size_t) *nmap );
*invert_list = astMalloc( sizeof( int )*(size_t) *nmap );
/* Check the pointers can be used safely. */
if( astOK ) {
/* Temporaily set the requested Invert flag for the SwitchMap. */
oldinv = astGetInvert( this );
astSetInvert( this, invert );
/* Get the forward selector Mapping. */
map = GetSelector( this, 1, &inv, status );
/* If the SwitchMap has a forward selector Mapping, return a clone of the
Mapping pointer, and the invert flag to be used with it, then
re-instate the original invert flag value (which was modified by
GetSelector). */
if( map ) {
( *map_list )[ 0 ] = astClone( map );
( *invert_list )[ 0 ] = astGetInvert( map );
astSetInvert( map, inv );
/* If the SwitchMap does not has a forward selector Mapping, return a
NULL pointer. */
} else {
( *map_list )[ 0 ] = NULL;
( *invert_list )[ 0 ] = 0;
}
/* Likewise, get and return the inverse selector Mapping.*/
map = GetSelector( this, 0, &inv, status );
if( map ) {
( *map_list )[ 1 ] = astClone( map );
( *invert_list )[ 1 ] = astGetInvert( map );
astSetInvert( map, inv );
} else {
( *map_list )[ 1 ] = NULL;
( *invert_list )[ 1 ] = 0;
}
/* Loop round all route Mappings. */
for( i = 0; i < result; i++ ){
/* Get the next route Mapping. */
map = GetRoute( this, (double) i + 1.0, &inv, status );
/* If the SwitchMap has a route Mapping for the current selector value,
return a clone of the Mapping pointer, and the invert flag to be used
with it, then re-instate the original invert flag value (which was
modified by GetRoute). */
if( map ) {
( *map_list )[ i + 2 ] = astClone( map );
( *invert_list )[ i + 2 ] = astGetInvert( map );
astSetInvert( map, inv );
/* If the SwitchMap does not has a route Mapping for the current selector
value, return a NULL pointer. */
} else {
( *map_list )[ i + 2 ] = NULL;
( *invert_list )[ i + 2 ] = 0;
}
}
/* Re-instate the original Ivert flag for the SwitchMap. */
astSetInvert( this, oldinv );
}
/* If an error has occurred, free the returned arrays. */
if( !astOK ) {
*map_list = astFree( *map_list );
*invert_list= astFree( *invert_list );
result= 0;
*nmap = 0;
}
/* Return the result */
return result;
}
static AstPointSet *Transform( AstMapping *this, AstPointSet *in,
int forward, AstPointSet *out, int *status ) {
/*
* Name:
* Transform
* Purpose:
* Apply a SwitchMap to transform a set of points.
* Type:
* Private function.
* Synopsis:
* #include "switchmap.h"
* AstPointSet *Transform( AstMapping *this, AstPointSet *in,
* int forward, AstPointSet *out, int *status )
* Class Membership:
* SwitchMap member function (over-rides the astTransform method inherited
* from the Mapping class).
* Description:
* This function takes a SwitchMap and a set of points encapsulated in a
* PointSet and transforms the points so as to apply the required Mapping.
* This implies applying each of the SwitchMap's component Mappings in turn,
* either in series or in parallel.
* Parameters:
* this
* Pointer to the SwitchMap.
* in
* Pointer to the PointSet associated with the input coordinate values.
* forward
* A non-zero value indicates that the forward coordinate transformation
* should be applied, while a zero value requests the inverse
* transformation.
* out
* Pointer to a PointSet which will hold the transformed (output)
* coordinate values. A NULL value may also be given, in which case a
* new PointSet will be created by this function.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the output (possibly new) PointSet.
* 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.
* - The number of coordinate values per point in the input PointSet must
* match the number of coordinates for the SwitchMap being applied.
* - If an output PointSet is supplied, it must have space for sufficient
* number of points and coordinate values per point to accommodate the
* result. Any excess space will be ignored.
*/
/* Local Variables: */
AstMapping *rmap;
AstMapping *selmap;
AstPointSet *ps1;
AstPointSet *ps1a;
AstPointSet *ps2;
AstPointSet *ps2a;
AstPointSet *result;
AstPointSet *selps;
AstSwitchMap *map;
double **in_ptr;
double **out_ptr;
double **ptr1;
double **ptr2;
double **sel_ptr;
double *outv;
double *sel;
int *popmap;
int iroute;
int ipoint;
int j;
int k;
int maxpop;
int ncin;
int ncout;
int npoint;
int nroute;
int rindex;
int rinv;
int selinv;
int totpop;
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Obtain a pointer to the SwitchMap. */
map = (AstSwitchMap *) this;
/* Apply the parent Mapping using the stored pointer to the Transform member
function inherited from the parent Mapping class. This function validates
all arguments and generates an output PointSet if necessary, but does not
actually transform any coordinate values. */
result = (*parent_transform)( this, in, forward, out, status );
/* We now extend the parent astTransform method by applying the component
Mappings of the SwitchMap to generate the output coordinate values. */
/* Get the number of input and output coords. */
if( forward ) {
ncin = astGetNin( this );
ncout = astGetNout( this );
} else {
ncin = astGetNout( this );
ncout = astGetNin( this );
}
/* Get the appropriate selector Mapping. */
selmap = GetSelector( map, forward, &selinv, status );
/* Transform the supplied positions using the above selector Mapping. */
selps = astTransform( selmap, in, forward, NULL );
/* Get a pointer to the array holding the selector value. */
sel_ptr = astGetPoints( selps );
/* Get a pointer to the array holding the input values. */
in_ptr = astGetPoints( in );
/* Get a pointer to the array in which to store the results, and the total
number of points being transformed. */
out_ptr = astGetPoints( result );
npoint = astGetNpoint( result );
/* We now count how many positions are to be tranformed by each of the
route Mappings. */
nroute = map->nroute;
popmap = astMalloc( sizeof( int )*nroute );
if( astOK ) {
for( iroute = 0; iroute < nroute; iroute++ ) popmap[ iroute ] = 0;
sel = sel_ptr[ 0 ];
for( ipoint = 0; ipoint < npoint; ipoint++,sel++ ) {
if( *sel != AST__BAD ) {
rindex = (int)( *sel + 0.5 ) - 1;
if( rindex >= 0 && rindex < nroute ) ( popmap[ rindex ] )++;
}
}
/* Find the number of points transformed by the most popular route Mapping.
Also find the total number of points transformed by any route Mapping. */
totpop = 0;
maxpop = 0;
for( iroute = 0; iroute < nroute; iroute++ ) {
if( popmap[ iroute ] > maxpop ) maxpop = popmap[ iroute ];
totpop += popmap[ iroute ];
}
if( maxpop == 0 ) maxpop = 1;
/* If some of the points are not transformed by any route Mapping.
Initialise the whole output array to hold AST__BAD at every point. */
if( totpop < npoint ) {
for( j = 0; j < ncout; j++ ) {
outv = out_ptr[ j ];
for( ipoint = 0; ipoint < npoint; ipoint++ ) *(outv++) = AST__BAD;
}
}
/* Create a PointSet large enough to hold all the supplied positions
which are to be transformed by the most popular route Mapping. */
ps1 = astPointSet( maxpop, ncin, "", status );
ptr1 = astGetPoints( ps1 );
/* Create a PointSet large enough to hold all the output positions
created by the most popular route Mapping. */
ps2 = astPointSet( maxpop, ncout, "", status );
ptr2 = astGetPoints( ps2 );
if( astOK ) {
/* Loop round each route Mapping which is used by at least 1 point. */
for( iroute = 0; iroute < nroute; iroute++ ) {
if( popmap[ iroute ] >0 ) {
rmap = GetRoute( map, (double)( iroute + 1 ), &rinv, status );
/* Construct two PointSets of the correct size to hold the input and
output points to be processed with the current route Mapping. We
re-use the memory allocated for the largest route Mapping's PointSet. */
if( popmap[ iroute ] != maxpop ) {
ps1a = astPointSet( popmap[ iroute ], ncin, "", status );
astSetPoints( ps1a, ptr1 );
ps2a = astPointSet( popmap[ iroute ], ncout, "", status );
astSetPoints( ps2a, ptr2 );
} else {
ps1a = astClone( ps1 );
ps2a = astClone( ps2 );
}
/* Fill the input PointSet with the input positions which are to be
transformed using the current route Mapping. */
sel = sel_ptr[ 0 ];
k = 0;
for( ipoint = 0; ipoint < npoint; ipoint++,sel++ ) {
if( *sel != AST__BAD ) {
rindex = (int)( *sel + 0.5 ) - 1;
if( rindex == iroute ) {
for( j = 0; j < ncin; j++ ) {
ptr1[ j ][ k ] = in_ptr[ j ][ ipoint ];
}
k++;
}
}
}
/* Use the route Mapping to transform this PointSet. */
(void) astTransform( rmap, ps1a, forward, ps2a );
/* Copy the axis values from the resulting PointSet back into the results
array. */
sel = sel_ptr[ 0 ];
k = 0;
for( ipoint = 0; ipoint < npoint; ipoint++,sel++ ) {
if( *sel != AST__BAD ) {
rindex = (int)( *sel + 0.5 ) - 1;
if( rindex == iroute ) {
for( j = 0; j < ncout; j++ ) {
out_ptr[ j ][ ipoint ] = ptr2[ j ][ k ];
}
k++;
}
}
}
/* Free resources. */
ps1a = astAnnul( ps1a );
ps2a = astAnnul( ps2a );
/* Re-instate the Invert flag for the route Mapping. */
astSetInvert( rmap, rinv );
}
}
}
/* Free resources. */
ps1 = astAnnul( ps1 );
ps2 = astAnnul( ps2 );
}
selps = astAnnul( selps );
popmap = astFree( popmap );
/* Re-instate the Invert flag of the selector Mapping. */
astSetInvert( selmap, selinv );
/* If an error occurred, clean up by deleting the output PointSet (if
allocated by this function) and setting a NULL result pointer. */
if ( !astOK ) {
if ( !out ) result = astDelete( result );
result = NULL;
}
/* Return a pointer to the output PointSet. */
return result;
}
/* Copy constructor. */
/* ----------------- */
static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
/*
* Name:
* Copy
* Purpose:
* Copy constructor for SwitchMap objects.
* Type:
* Private function.
* Synopsis:
* void Copy( const AstObject *objin, AstObject *objout, int *status )
* Description:
* This function implements the copy constructor for SwitchMap objects.
* Parameters:
* objin
* Pointer to the object to be copied.
* objout
* Pointer to the object being constructed.
* status
* Pointer to the inherited status variable.
* Returned Value:
* void
* Notes:
* - This constructor makes a deep copy, including a copy of the component
* Mappings within the SwitchMap.
*/
/* Local Variables: */
AstSwitchMap *in; /* Pointer to input SwitchMap */
AstSwitchMap *out; /* Pointer to output SwitchMap */
int i; /* Loop count */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain pointers to the input and output SwitchMaps. */
in = (AstSwitchMap *) objin;
out = (AstSwitchMap *) objout;
/* For safety, start by clearing any references to the input component
Mappings,etc, from the output SwitchMap. */
out->fsmap = NULL;
out->ismap = NULL;
out->routemap = NULL;
out->routeinv = NULL;
/* Make copies of these Mappings, etc, and store pointers to them in the output
SwitchMap structure. */
if( in->fsmap ) out->fsmap = astCopy( in->fsmap );
if( in->ismap ) out->ismap = astCopy( in->ismap );
out->routemap = astMalloc( sizeof( AstMapping * )*( in->nroute ) );
out->routeinv = astMalloc( sizeof( int )*( in->nroute ) );
if( astOK ) {
for( i = 0; i < in->nroute; i++ ) {
out->routemap[ i ] = astCopy( in->routemap[ i ] );
out->routeinv[ i ] = in->routeinv[ i ];
}
}
}
/* Destructor. */
/* ----------- */
static void Delete( AstObject *obj, int *status ) {
/*
* Name:
* Delete
* Purpose:
* Destructor for SwitchMap objects.
* Type:
* Private function.
* Synopsis:
* void Delete( AstObject *obj, int *status )
* Description:
* This function implements the destructor for SwitchMap objects.
* Parameters:
* obj
* Pointer to the object to be deleted.
* status
* Pointer to the inherited status variable.
* Returned Value:
* void
* Notes:
* This function attempts to execute even if the global error status is
* set.
*/
/* Local Variables: */
AstSwitchMap *this; /* Pointer to SwitchMap */
int i;
/* Obtain a pointer to the SwitchMap structure. */
this = (AstSwitchMap *) obj;
/* Free dynamically allocated resources. */
if( this->fsmap ) this->fsmap = astAnnul( this->fsmap );
if( this->ismap ) this->ismap = astAnnul( this->ismap );
for( i = 0; i < this->nroute; i++ ) {
this->routemap[ i ] = astAnnul( this->routemap[ i ] );
}
this->routemap = astFree( this->routemap );
this->routeinv = astFree( this->routeinv );
/* Clear the remaining SwitchMap variables. */
this->nroute = 0;
this->fsinv = 0;
this->isinv = 0;
}
/* Dump function. */
/* -------------- */
static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
/*
* Name:
* Dump
* Purpose:
* Dump function for SwitchMap 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 SwitchMap class to an output Channel.
* Parameters:
* this
* Pointer to the SwitchMap 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: */
AstSwitchMap *this;
int ival;
int set;
int i;
char buf[ 20 ];
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the SwitchMap structure. */
this = (AstSwitchMap *) this_object;
/* Write out values representing the instance variables for the SwitchMap
class. Accompany these with appropriate comment strings, possibly
depending on the values being written.*/
/* In the case of attributes, we first use the appropriate (private)
Test... member function to see if they are set. If so, we then use
the (private) Get... function to obtain the value to be written
out.
For attributes which are not set, we use the astGet... method to
obtain the value instead. This will supply a default value
(possibly provided by a derived class which over-rides this method)
which is more useful to a human reader as it corresponds to the
actual default attribute value. Since "set" will be zero, these
values are for information only and will not be read back. */
/* Forward selector Mapping */
/* ------------------------ */
if( this->fsmap ) {
astWriteObject( channel, "FSMap", 1, 1, this->fsmap,
"Forward selector Mapping" );
/* Forward selector Invert flag. */
/* ----------------------------- */
ival = this->fsinv;
set = ( ival != 0 );
astWriteInt( channel, "FSInv", set, 0, ival,
ival ? "Fwd selector used in inverse direction" :
"Fwd selector used in forward direction" );
}
/* Inverse selector Mapping */
/* ------------------------ */
if( this->ismap ) {
astWriteObject( channel, "ISMap", 1, 1, this->ismap,
"Inverse selector Mapping" );
/* Forward selector Invert flag. */
/* ----------------------------- */
ival = this->isinv;
set = ( ival != 0 );
astWriteInt( channel, "ISInv", set, 0, ival,
ival ? "Inv selector used in inverse direction" :
"Inv selector used in forward direction" );
}
/* Loop to dump each route Mapping and its invert flag. */
/* ---------------------------------------------------- */
for( i = 0; i < this->nroute; i++ ) {
sprintf( buf, "RMap%d", i + 1 );
astWriteObject( channel, buf, 1, 1, this->routemap[ i ],
"Route Mapping" );
ival = this->routeinv[ i ];
set = ( ival != 0 );
sprintf( buf, "RInv%d", i + 1 );
astWriteInt( channel, buf, set, 0, ival,
ival ? "Route Mapping used in inverse direction" :
"Route Mapping used in forward direction" );
}
}
/* Standard class functions. */
/* ========================= */
/* Implement the astIsASwitchMap and astCheckSwitchMap functions using the
macros defined for this purpose in the "object.h" header file. */
astMAKE_ISA(SwitchMap,Mapping)
astMAKE_CHECK(SwitchMap)
AstSwitchMap *astSwitchMap_( void *fsmap_void, void *ismap_void, int nroute,
void **routemaps_void, const char *options, int *status, ...) {
/*
*+
* Name:
* astSwitchMap
* Purpose:
* Create a SwitchMap.
* Type:
* Protected function.
* Synopsis:
* #include "switchmap.h"
* AstSwitchMap *astSwitchMap( AstMapping *fsmap, AstMapping *ismap,
* int nroute, AstMapping **routemaps,
* const char *options, ... )
* Class Membership:
* SwitchMap constructor.
* Description:
* This function creates a new SwitchMap and optionally initialises its
* attributes.
* Parameters:
* fsmap
* Pointer to the forward selector Mapping
* ismap
* Pointer to the inverse selector Mapping
* nroute
* The number of route Mappings.
* routemaps
* An array of pointers to the route Mappings.
* options
* Pointer to a null terminated string containing an optional
* comma-separated list of attribute assignments to be used for
* initialising the new SwitchMap. The syntax used is the same as for the
* astSet method 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 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 method (and for the C "printf"
* function).
* Returned Value:
* A pointer to the new SwitchMap.
* 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.
*-
* Implementation Notes:
* - This function implements the basic SwitchMap constructor which is
* available via the protected interface to the SwitchMap class. A
* public interface is provided by the astSwitchMapId_ function.
* - Because this function has a variable argument list, it is
* invoked by a macro that evaluates to a function pointer (not a
* function invocation) and no checking or casting of arguments is
* performed before the function is invoked. Because of this, the
* "map1" and "map2" parameters are of type (void *) and are
* converted and validated within the function itself.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstSwitchMap *new; /* Pointer to new SwitchMap */
AstMapping *fsmap; /* Pointer to fwd selector Mapping */
AstMapping *ismap; /* Pointer to inv selector Mapping */
AstMapping **routemaps; /* Array of route Mapping pointers */
int i; /* Route Mappings index */
va_list args; /* Variable argument list */
/* Initialise. */
new = NULL;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(NULL);
/* Check the global status. */
if ( !astOK ) return new;
/* Report an error if no route Mappings have been supplied. */
if( nroute <= 0 ) astError( AST__BDPAR, "astSwitchMap(SwitchMap): "
"Bad number of route Mappings (%d) specified.", status,
nroute );
/* Otherwise create an array to hold the route Mapping pointers. */
routemaps = astMalloc( sizeof( AstMapping * )*nroute );
/* Obtain and validate pointers to the Mapping structures provided. */
if( astOK ) {
fsmap = fsmap_void ? astCheckMapping( fsmap_void ) : NULL;
ismap = ismap_void ? astCheckMapping( ismap_void ) : NULL;
for( i = 0; i < nroute; i++ ) {
routemaps[ i ] = astCheckMapping( routemaps_void[ i ] );
}
}
if ( astOK ) {
/* Initialise the SwitchMap, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitSwitchMap( NULL, sizeof( AstSwitchMap ), !class_init, &class_vtab,
"SwitchMap", fsmap, ismap, nroute, routemaps );
/* 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 SwitchMap'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 );
}
}
/* Free memory used to hold the route Mapping pointers. */
routemaps = astFree( routemaps );
/* Return a pointer to the new SwitchMap. */
return new;
}
AstSwitchMap *astSwitchMapId_( void *fsmap_void, void *ismap_void, int nroute,
void **routemaps_void, const char *options, ... ) {
/*
*++
* Name:
c astSwitchMap
f AST_SWITCHMAP
* Purpose:
* Create a SwitchMap.
* Type:
* Public function.
* Synopsis:
c #include "switchmap.h"
c AstSwitchMap *astSwitchMap( AstMapping *fsmap, AstMapping *ismap,
c int nroute, AstMapping *routemaps[],
c const char *options, ... )
f RESULT = AST_SWITCHMAP( FSMAP, ISMAP, NROUTE, ROUTEMAPS, OPTIONS,
f STATUS )
* Class Membership:
* SwitchMap constructor.
* Description:
* This function creates a new SwitchMap and optionally initialises
* its attributes.
*
* A SwitchMap is a Mapping which represents a set of alternate
* Mappings, each of which is used to transform positions within a
* particular region of the input or output coordinate system of the
* SwitchMap.
*
* A SwitchMap can encapsulate any number of Mappings, but they must
* all have the same number of inputs (Nin attribute value) and the
* same number of outputs (Nout attribute value). The SwitchMap itself
* inherits these same values for its Nin and Nout attributes. Each of
* these Mappings represents a "route" through the switch, and are
* referred to as "route" Mappings below. Each route Mapping transforms
* positions between the input and output coordinate space of the entire
* SwitchMap, but only one Mapping will be used to transform any given
* position. The selection of the appropriate route Mapping to use with
* any given input position is made by another Mapping, called the
* "selector" Mapping. Each SwitchMap encapsulates two selector
* Mappings in addition to its route Mappings; one for use with the
* SwitchMap's forward transformation (called the "forward selector
* Mapping"), and one for use with the SwitchMap's inverse transformation
* (called the "inverse selector Mapping"). The forward selector Mapping
* must have the same number of inputs as the route Mappings, but
* should have only one output. Likewise, the inverse selector Mapping
* must have the same number of outputs as the route Mappings, but
* should have only one input.
*
* When the SwitchMap is used to transform a position in the forward
* direction (from input to output), each supplied input position is
* first transformed by the forward transformation of the forward selector
* Mapping. This produces a single output value for each input position
* referred to as the selector value. The nearest integer to the selector
* value is found, and is used to index the array of route Mappings (the
* first supplied route Mapping has index 1, the second route Mapping has
* index 2, etc). If the nearest integer to the selector value is less
* than 1 or greater than the number of route Mappings, then the SwitchMap
* output position is set to a value of AST__BAD on every axis. Otherwise,
* the forward transformation of the selected route Mapping is used to
* transform the supplied input position to produce the SwitchMap output
* position.
*
* When the SwitchMap is used to transform a position in the inverse
* direction (from "output" to "input"), each supplied "output" position
* is first transformed by the inverse transformation of the inverse
* selector Mapping. This produces a selector value for each "output"
* position. Again, the nearest integer to the selector value is found,
* and is used to index the array of route Mappings. If this selector
* index value is within the bounds of the array of route Mappings, then
* the inverse transformation of the selected route Mapping is used to
* transform the supplied "output" position to produce the SwitchMap
* "input" position. If the selector index value is outside the bounds
* of the array of route Mappings, then the SwitchMap "input" position is
* set to a value of AST__BAD on every axis.
*
* In practice, appropriate selector Mappings should be chosen to
* associate a different route Mapping with each region of coordinate
* space. Note that the SelectorMap class of Mapping is particularly
* appropriate for this purpose.
*
* If a compound Mapping contains a SwitchMap in series with its own
* inverse, the combination of the two adjacent SwitchMaps will be
* replaced by a UnitMap when the compound Mapping is simplified using
c astSimplify.
f AST_SIMPLIFY.
* Parameters:
c fsmap
f FSMAP = INTEGER (Given)
* Pointer to the forward selector Mapping. This must have a
* defined forward transformation, but need not have a defined
* inverse transformation. It must have one output, and the number of
* inputs must match the number of inputs of each of the supplied
* route Mappings.
c NULL
f AST__NULL
* may be supplied, in which case the SwitchMap will have an undefined
* forward Mapping.
c ismap
f ISMAP = INTEGER (Given)
* Pointer to the inverse selector Mapping. This must have a
* defined inverse transformation, but need not have a defined
* forward transformation. It must have one input, and the number of
* outputs must match the number of outputs of each of the supplied
* route Mappings.
c NULL
f AST__NULL
* may be supplied, in which case the SwitchMap will have an undefined
* inverse Mapping.
c nroute
f NROUTE = INTEGER (Given)
* The number of supplied route Mappings.
c routemaps
f ROUTEMAPS( NROUTE ) = INTEGER (Given)
* An array of pointers to the route Mappings. All the supplied
* route Mappings must have common values for the Nin and Nout
* attributes, and these values define the number of inputs and
* outputs of the SwitchMap.
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 SwitchMap. 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 SwitchMap. 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 astSwitchMap()
f AST_SWITCHMAP = INTEGER
* A pointer to the new SwitchMap.
* Notes:
c - Note that the component Mappings supplied are not copied by
c astSwitchMap (the new SwitchMap simply retains a reference to
c them). They may continue to be used for other purposes, but
c should not be deleted. If a SwitchMap containing a copy of its
c component Mappings is required, then a copy of the SwitchMap should
c be made using astCopy.
f - Note that the component Mappings supplied are not copied by
f AST_SWITCHMAP (the new SwitchMap simply retains a reference to
f them). They may continue to be used for other purposes, but
f should not be deleted. If a SwitchMap containing a copy of its
f component Mappings is required, then a copy of the SwitchMap should
f be made using AST_COPY.
* - A null Object pointer (AST__NULL) will be returned if this
c function is invoked with the AST error status set, or if it
f function is invoked with STATUS set to an error value, or if it
* should fail for any reason.
*--
* Implementation Notes:
* - This function implements the external (public) interface to
* the astSwitchMap constructor function. It returns an ID value
* (instead of a true C pointer) to external users, and must be
* provided because astSwitchMap_ has a variable argument list which
* cannot be encapsulated in a macro (where this conversion would
* otherwise occur).
* - Because no checking or casting of arguments is performed
* before the function is invoked, the "map1" and "map2" parameters
* are of type (void *) and are converted from an ID value to a
* pointer and validated within the function itself.
* - The variable argument list also prevents this function from
* invoking astSwitchMap_ directly, so it must be a re-implementation
* of it in all respects, except for the conversions between IDs
* and pointers on input/output of Objects.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstSwitchMap *new; /* Pointer to new SwitchMap */
AstMapping *fsmap; /* Pointer to fwd selector Mapping */
AstMapping *ismap; /* Pointer to inv selector Mapping */
AstMapping **routemaps; /* Array of route Mapping pointers */
int i; /* Route Mappings index */
va_list args; /* Variable argument list */
int *status; /* Pointer to inherited status value */
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(NULL);
/* Initialise. */
new = NULL;
/* Get a pointer to the inherited status value. */
status = astGetStatusPtr;
/* Check the global status. */
if ( !astOK ) return new;
/* Report an error if no route Mappings have been supplied. */
if( nroute <= 0 ) astError( AST__BDPAR, "astSwitchMap(SwitchMap): "
" Bad number of route Mappings (%d) specified.", status,
nroute );
/* Otherwise create an array to hold the route Mapping pointers. */
routemaps = astMalloc( sizeof( AstMapping * )*nroute );
/* Obtain and validate pointers to the Mapping structures provided. */
if( astOK ) {
fsmap = fsmap_void ? astCheckMapping( astMakePointer(fsmap_void) ) : NULL;
ismap = ismap_void ? astCheckMapping( astMakePointer(ismap_void) ) : NULL;
for( i = 0; i < nroute; i++ ) {
routemaps[ i ] = astVerifyMapping( astMakePointer(routemaps_void[ i ]) );
}
}
if ( astOK ) {
/* Initialise the SwitchMap, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitSwitchMap( NULL, sizeof( AstSwitchMap ), !class_init, &class_vtab,
"SwitchMap", fsmap, ismap, nroute, routemaps );
/* 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 SwitchMap'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 );
}
}
/* Free memory used to hold the route Mapping pointers. */
routemaps = astFree( routemaps );
/* Return an ID value for the new SwitchMap. */
return astMakeId( new );
}
AstSwitchMap *astInitSwitchMap_( void *mem, size_t size, int init,
AstSwitchMapVtab *vtab, const char *name,
AstMapping *fsmap, AstMapping *ismap,
int nroute, AstMapping **routemaps, int *status ) {
/*
*+
* Name:
* astInitSwitchMap
* Purpose:
* Initialise a SwitchMap.
* Type:
* Protected function.
* Synopsis:
* #include "switchmap.h"
* AstSwitchMap *astInitSwitchMap( void *mem, size_t size, int init,
* AstSwitchMapVtab *vtab, const char *name,
* AstMapping *fsmap, AstMapping *ismap,
* int nroute, AstMapping **routemaps )
* Class Membership:
* SwitchMap initialiser.
* Description:
* This function is provided for use by class implementations to initialise
* a new SwitchMap object. It allocates memory (if necessary) to
* accommodate the SwitchMap plus any additional data associated with the
* derived class. It then initialises a SwitchMap structure at the start
* of this memory. If the "init" flag is set, it also initialises the
* contents of a virtual function table for a SwitchMap at the start of
* the memory passed via the "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory in which the SwitchMap is to be initialised.
* This must be of sufficient size to accommodate the SwitchMap data
* (sizeof(SwitchMap)) 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 SwitchMap (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
* SwitchMap structure, so a valid value must be supplied even if not
* required for allocating memory.
* init
* A logical flag indicating if the SwitchMap'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 SwitchMap.
* 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 Object
* astClass function).
* fsmap
* Pointer to the forward selector Mapping.
* ismap
* Pointer to the inverse selector Mapping.
* nroute
* The number of route Mappings supplied.
* routemaps
* An array holdiong pointers to the route Mappings.
* Returned Value:
* A pointer to the new SwitchMap.
* 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: */
AstSwitchMap *new; /* Pointer to new SwitchMap */
int i; /* Loop count */
int nin; /* No. input coordinates for SwitchMap */
int nout; /* No. output coordinates for SwitchMap */
/* Check the global status. */
if ( !astOK ) return NULL;
/* If necessary, initialise the virtual function table. */
if ( init ) astInitSwitchMapVtab( vtab, name );
/* Initialise. */
new = NULL;
/* Check that all route Mappings have common values for Nin and Nout.*/
nin = astGetNin( routemaps[ 0 ] );
nout = astGetNout( routemaps[ 0 ] );
for( i = 1; i < nroute; i++ ) {
if( nin != astGetNin( routemaps[ i ] ) ){
if( astOK ) {
astError( AST__BADNI, "astInitSwitchMap(%s): Route Mapping "
"number %d has %d input(s) but the first route "
"Mapping has %d input(s).", status, name, i + 1,
astGetNin( routemaps[ i ] ), nin );
}
} else if( nout != astGetNout( routemaps[ i ] ) ){
if( astOK ) {
astError( AST__BADNO, "astInitSwitchMap(%s): Route Mapping "
"number %d has %d output(s) but the first route "
"Mapping has %d output(s).", status, name, i + 1,
astGetNin( routemaps[ i ] ), nin );
}
}
}
/* If supplied, report an error if fsmap has no forward transformation,
or if it has an incorrect number of inputs or output. */
if( fsmap && astOK ) {
if( !astGetTranForward( fsmap ) ) {
astError( AST__INTRD, "astInitSwitchMap(%s): The forward selector Mapping "
"is not able to transform coordinates in the forward direction.", status,
name );
} else if( astGetNin( fsmap ) != nin ){
astError( AST__BADNI, "astInitSwitchMap(%s): The forward selector "
"Mapping has %d input(s) but the SwitchMap has %d "
"input(s).", status, name, astGetNin( fsmap ), nin );
} else if( astGetNout( fsmap ) != 1 ){
astError( AST__BADNO, "astInitSwitchMap(%s): The forward selector "
"Mapping has %d outputs but should only have 1.", status, name,
astGetNout( fsmap ) );
}
}
/* If supplied, report an error if ismap has no inverse transformation,
or if it has an incorrect number of inputs or outputs. */
if( ismap && astOK ) {
if( !astGetTranInverse( ismap ) ) {
astError( AST__INTRD, "astInitSwitchMap(%s): The inverse selector Mapping "
"is not able to transform coordinates in the inverse direction.", status,
name );
} else if( nout != astGetNout( ismap ) ){
astError( AST__BADNO, "astInitSwitchMap(%s): The inverse selector "
"Mapping has %d output(s) but the SwitchMap has %d "
"output(s).", status, name, astGetNout( ismap ), nout );
} else if( astGetNin( ismap ) != 1 ){
astError( AST__BADNI, "astInitSwitchMap(%s): The inverse selector "
"Mapping has %d inputs but should only have 1.", status, name,
astGetNin( ismap ) );
}
}
/* Report an error if neither ismap nor fsmap were supplied. */
if( !fsmap && !ismap && astOK ) {
astError( AST__INTRD, "astInitSwitchMap(%s): No selector Mappings "
"supplied.", status, name );
}
/* Initialise a Mapping structure (the parent class) as the first component
within the SwitchMap structure, allocating memory if necessary. Specify
the number of input and output coordinates and in which directions the
Mapping should be defined. */
if ( astOK ) {
new = (AstSwitchMap *) astInitMapping( mem, size, 0,
(AstMappingVtab *) vtab, name,
nin, nout,
( fsmap != NULL ),
( ismap != NULL ) );
if ( astOK ) {
/* Initialise the SwitchMap data. */
/* --------------------------- */
/* Store pointers to the selector Mappings. */
new->fsmap = fsmap ? astClone( fsmap ) : NULL;
new->ismap = ismap ? astClone( ismap ) : NULL;
/* Save the initial values of the inversion flags for these Mappings. */
new->fsinv = fsmap ? astGetInvert( fsmap ) : 0;
new->isinv = ismap ? astGetInvert( ismap ) : 0;
/* Create arrays for the route Mappings. */
new->routemap = astMalloc( sizeof( AstMapping * )*nroute );
new->routeinv = astMalloc( sizeof( int )*nroute );
/* Store pointers to the route Mappings and their invert flags. */
if( astOK ) {
new->nroute = nroute;
for( i = 0; i < nroute; i++ ) {
new->routemap[ i ] = astClone( routemaps[ i ] );
new->routeinv[ i ] = astGetInvert( routemaps[ i ] );
}
} else {
new->nroute = 0;
}
/* 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;
}
AstSwitchMap *astLoadSwitchMap_( void *mem, size_t size,
AstSwitchMapVtab *vtab, const char *name,
AstChannel *channel, int *status ) {
/*
*+
* Name:
* astLoadSwitchMap
* Purpose:
* Load a SwitchMap.
* Type:
* Protected function.
* Synopsis:
* #include "switchmap.h"
* AstSwitchMap *astLoadSwitchMap( void *mem, size_t size,
* AstSwitchMapVtab *vtab, const char *name,
* AstChannel *channel )
* Class Membership:
* SwitchMap loader.
* Description:
* This function is provided to load a new SwitchMap 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
* SwitchMap 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 a SwitchMap at the start of the memory
* passed via the "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory into which the SwitchMap is to be
* loaded. This must be of sufficient size to accommodate the
* SwitchMap data (sizeof(SwitchMap)) 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 SwitchMap (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 SwitchMap 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(AstSwitchMap) is used instead.
* vtab
* Pointer to the start of the virtual function table to be
* associated with the new SwitchMap. If this is NULL, a pointer to
* the (static) virtual function table for the SwitchMap 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 "SwitchMap" is used instead.
* Returned Value:
* A pointer to the new SwitchMap.
* 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 */
AstSwitchMap *new;
AstMapping *rmap;
int i;
char buf[ 20 ];
/* 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 SwitchMap. In this case the
SwitchMap belongs to this class, so supply appropriate values to be
passed to the parent class loader (and its parent, etc.). */
if ( !vtab ) {
size = sizeof( AstSwitchMap );
vtab = &class_vtab;
name = "SwitchMap";
/* If required, initialise the virtual function table for this class. */
if ( !class_init ) {
astInitSwitchMapVtab( 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 SwitchMap. */
new = astLoadMapping( mem, size, (AstMappingVtab *) 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, "SwitchMap" );
/* Now read each individual data item from this list and use it to
initialise the appropriate instance variable(s) for this class. */
/* In the case of attributes, we first read the "raw" input value,
supplying the "unset" value as the default. If a "set" value is
obtained, we then use the appropriate (private) Set... member
function to validate and set the value properly. */
/* Forward Selector Mapping and its Invert flag. */
/* --------------------------------------------- */
new->fsmap = astReadObject( channel, "fsmap", NULL );
new->fsinv = astReadInt( channel, "fsinv", 0 );
new->fsinv = ( new->fsinv != 0 );
/* Inverse Selector Mapping and its Invert flag. */
/* --------------------------------------------- */
new->ismap = astReadObject( channel, "ismap", NULL );
new->isinv = astReadInt( channel, "isinv", new->fsinv );
new->isinv = ( new->isinv != 0 );
/* Loop to load each route Mapping and its invert flag. */
/* ---------------------------------------------------- */
new->routemap = NULL;
new->routeinv = NULL;
i = 0;
while( astOK ) {
sprintf( buf, "rmap%d", i + 1 );
rmap = astReadObject( channel, buf, NULL );
if( rmap ) {
new->routemap = astGrow( new->routemap, i + 1, sizeof( AstMapping *) );
new->routeinv = astGrow( new->routeinv, i + 1, sizeof( int ) );
if( astOK ) {
new->routemap[ i ] = rmap;
sprintf( buf, "rinv%d", i + 1 );
new->routeinv[ i ] = astReadInt( channel, buf, 0 );
new->routeinv[ i ] = ( new->routeinv[ i ] != 0 );
i++;
}
} else {
break;
}
}
/* Number of route Mappings. */
new->nroute = i;
/* If an error occurred, clean up by deleting the new SwitchMap. */
if ( !astOK ) new = astDelete( new );
}
/* Return the new SwitchMap 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. */
/* None. */
ast-8.0.7/f77.h 0000664 0001750 0001750 00000117625 12610415024 010023 0000000 0000000 /*
*+
* Name:
* f77.h and cnf.h
* Purpose:
* C - FORTRAN interace macros and prototypes
* Language:
* C (part ANSI, part not)
* Type of Module:
* C include file
* Description:
* For historical reasons two files, F77.h and cnf.h are required
* but the have now been combined and for new code, only one is
* necessary.
*
* This file defines the macros needed to write C functions that are
* designed to be called from FORTRAN programs, and to do so in a
* portable way. Arguments are normally passed by reference from a
* FORTRAN program, and so the F77 macros arrange for a pointer to
* all arguments to be available. This requires no work on most
* machines, but will actually generate the pointers on a machine
* that passes FORTRAN arguments by value.
* Notes:
* - Macros are provided to handle the conversion of logical data
* values between the way that FORTRAN represents a value and the
* way that C represents it.
* - Macros are provided to convert variables between the FORTRAN and
* C method of representing them. In most cases there is no
* conversion required, the macros just arrange for a pointer to
* the FORTRAN variable to be set appropriately. The possibility that
* FORTRAN and C might use different ways of representing integer
* and floating point values is considered remote, the macros are
* really only there for completeness and to assist in the automatic
* generation of C interfaces.
* - For character variables the macros convert between
* the FORTRAN method of representing them (fixed length, blank
* filled strings) and the C method (variable length, null
* terminated strings) using calls to the CNF functions.
* Implementation Deficiencies:
* - The macros support the K&R style of function definition, but
* this file may not work with all K&R compilers as it contains
* "#if defined" statements. These could be replaced with #ifdef's
* if necessary. This has not been done as is would make the code
* less clear and the need for support for K&R sytle definitions
* should disappear as ANSI compilers become the default.
* Copyright:
* Copyright (C) 1991, 1993 Science & Engineering Research Council.
* Copyright (C) 2006 Particle Physics and Astronomy Research Council.
* Copyright (C) 2007,2008 Science and 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 General Public License as
* published by the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street,Fifth Floor, Boston, MA
* 02110-1301, USA
* Authors:
* PMA: Peter Allan (Starlink, RAL)
* AJC: Alan Chipperfield (Starlink, RAL)
* TIMJ: Tim Jenness (JAC)
* PWD: Peter W. Draper (JAC, Durham University)
* {enter_new_authors_here}
* History:
* 23-MAY-1991 (PMA):
* Original version.
* 19-JUN-1991 (PMA):
* Removed VMS versions of IM(EX)PORT_LOGICAL macros that tried
* to convert data representations.
* 24-JUN-1991 (PMA):
* Changed the names of IMPORT macros to GENPTR.
* Removed the EXPORT macros.
* 27-JUN-1991 (PMA):
* Modified DECstation specific stuff to allow use of the c89
* compiler.
* 8-JUL-1991 (PMA):
* Added macros to call FORTRAN from C.
* 16-OCT-1991 (PMA):
* Remove type_ARRAY2 definitions.
* Remove the length argument from CHARACTER_ARRAY and the
* dimension specifier from GENPTR_type_ARRAY.
* Add extra brackets to F77_ISFALSE and F77_ISTRUE.
* 25-OCT-1991 (PMA):
* Changed "if defined(sun4)" to "if defined(sun)"
* 2-JUN-1992 (PMA):
* Changed "if defined(mips)" to "if defined(ultrix)" to prevent
* those definitions being used on a Silicon Graphics machine.
* 11-JUN-1992 (PMA):
* Changed "if defined(ultrix)" back to "if defined(mips)" so that
* it still works on OSF/1 on a DECstation.
* Add support for general non-ANSI compilers, but not basic K&R
* ones.
* 12-JUN-1992 (PMA):
* Change declaration of dummy scalar arguments to be const
* pointers. Change declaration of dummy array arguments to be
* const pointers.
* 5-JAN-1993 (PMA):
* Changed "if defined(mips)" so that it will recognise a
* DECstation running Ultrix or OSF/1, but not a Silicon Graphics
* workstation.
* Change the definition of F77_BYTE_TYPE to add "signed".
* Redefine this on VMS where signed is invalid syntax.
* Add new types of UBYTE and UWORD.
* 8-JAN-1993 (PMA):
* Fix bug in the definition of CHARACTER_RETURN_VALUE. There was
* an extraneous space.
* Add a macro F77_POINTER_TYPE and use it to define POINTER.
* 13-JAN-1993 (PMA):
* Start to add support for K&R function definitions. These are
* done on a per machine basis.
* 16-APR-1993 (PMA):
* Change the definition of F77_POINTER_TYPE from int to unsigned
* int.
* 7-MAY-1993 (PMA):
* Change from using a null comment as a token concatenation
* operator to using the internal macro _f77_x on non-ANSI
* systems.
* 10-MAY-1993 (PMA):
* Finish adding K&R support. This will form version 2.0 of F77.
* 10-MAY-1993 (PMA):
* Add support for Alpha OSF/1.
* 9-JUL-1993 (PMA):
* Add further POINTER macros: POINTER_ARRAY,
* GENPTR_POINTER_ARRAY, DECLARE_POINTER, DECLARE_POINTER_ARRAY,
* POINTER_ARG, POINTER_ARRAY_ARG, F77_POINTER_FUNCTION,
* KR_POINTER_ARRAY.
* 24-AUG-1993 (PMA):
* Add const to the VMS definitions of CHARACTER and CHARACTER_ARRAY.
* 3-NOV-1993 (PMA):
* Remove K&R stuff to a separate file.
* Released on Unix as version 2.0 of CNF.
* 11-NOV-1993 (PMA):
* Return to using the null comment to concatenate text on non-ANSI
* systems as _f77_x caused problems with the c89 -common flag on
* DECstations.
* 23-JAN-1996 (AJC):
* Add SUBROUTINE, type_FUNCTION, SUBROUTINE_ARG,
* type_FUNCTION_ARG, GENPTR_SUBROUTINE and GENPTR_type_FUNCTION
* required for passed subroutine and function name.
* 29-JAN-1996 (AJC):
* Add the dynamic CHARACTER_ macros
* and CHARACTER_ARG_TYPE
* 22-FEB-1996 (AJC):
* Add CHARACTER_RETURN_ARG
* 23-MAY-1996 (AJC):
* Add DECLARE_CHARACTER_ARRAY_DYN
* F77_CREATE_CHARACTER_ARRAY
* F77_CHARACTER_ARG_TYPE
* 14-JUN-1996 (AJC):
* Add DECLARE_LOGICAL_ARRAY_DYN
* F77_CREATE_LOGICAL_ARRAY
* 21-JUN-1996 (AJC):
* Add cast to _ARRAY_ARGs to allow multidimensional arrays
* 17-MAR-1998 (AJC):
* Add DECLARE, CREATE and FREE dynamic array macros for all types
* Changed CREATE_CHARACTER_ARRAY and CREATE_LOGICAL_ARRAY to use
* number of elements rather than dimensions.
* Add IMPORT, EXPORT and ASSOC macros
* 22-JUL-1998 (AJC):
* Combined F77.h and cnf.h
* 23-SEP-1998 (AJC):
* Input strings for cnf -> const char *
* Input int arrays for cnf -> const int *
* 4-NOV-1998 (AJC):
* Bring cnf prototypes in line with .c routines
* 8-FEB-1999 (AJC):
* Added cnf_mem stuff
* 9-FEB-1999 (AJC):
* Use cnf_cptr/fptr for IMPORT/EXPORT_POINTER
* 16-FEB-1999 (AJC):
* Added missing cnf_fptr prototype
* 23-JUN-1999 (AJC):
* Change cnf_name to cnfName
* and add macros for cnf_name
* 1-DEC-1999 (AJC):
* Add define cnf_free
* 7-JAN-2000 (AJC):
* Correct omission of F77_ASSOC_UBYTE_ARRAY
* Correct F77_EXPORT_UWORD_ARRAY
* 25-AUG-2005 (TIMJ):
* Add cnfInitRTL
* 23-FEB-2006 (TIMJ):
* Add cnfRealloc
* Use starMalloc rather than malloc in F77_CREATE_POINTER_ARRAY
* (since it needs to match what goes on in cnfFree)
* 21-JUN-2006 (PWD):
* Changed to use a different return type for REAL functions. This
* effects g77 under 64-bit, when the f2c bindings expect the return
* value of a REAL function to be a double, not a float.
* 25-SEP-2006 (PWD):
* Introduced F77_CREATE_IMPORT_CHARACTER. Match length of
* F77_CREATE_CHARACTER to result from cnfCref.
* 13-JUL-2007 (PWD):
* Parameterise the type of Fortran character string lengths. Can
* be long.
* 7-OCT-2008 (TIMJ):
* Initialise pointers.
* 11-MAY-2011 (DSB):
* Added F77_LOCK
* {enter_further_changes_here}
*
* Bugs:
* {note_any_bugs_here}
*-
------------------------------------------------------------------------------
*/
#if !defined(CNF_MACROS)
#define CNF_MACROS
#include
#include
/* This initial sections defines values for all macros. These are the */
/* values that are generally appropriate to an ANSI C compiler on Unix. */
/* For macros that have different values on other systems, the macros */
/* should be undefined and then redefined in the system specific sections. */
/* At the end of this section, some macros are redefined if the compiler */
/* is non-ANSI. */
#if defined(__STDC__) || defined(VMS)
#define CNF_CONST const
#else
#define CNF_CONST
#endif
/* ----- Macros common to calling C from FORTRAN and FORTRAN from C ---- */
/* --- External Names --- */
/* Macro to define the name of a Fortran routine or common block. This */
/* ends in an underscore on many Unix systems. */
#define F77_EXTERNAL_NAME(X) X ## _
/* --- Logical Values --- */
/* Define the values that are used to represent the logical values TRUE */
/* and FALSE in Fortran. */
#define F77_TRUE 1
#define F77_FALSE 0
/* Define macros that evaluate to C logical values, given a FORTRAN */
/* logical value. */
#define F77_ISTRUE(X) ( X )
#define F77_ISFALSE(X) ( !( X ) )
/* --- Common Blocks --- */
/* Macros used in referring to FORTRAN common blocks. */
#define F77_BLANK_COMMON @BLANK_COMMON_SYMBOL@
#define F77_NAMED_COMMON(B) F77_EXTERNAL_NAME(B)
/* ------------------ Calling C from FORTRAN --------------------------- */
/* --- Data Types --- */
/* Define macros for all the Fortran data types (except COMPLEX, which is */
/* not handled by this package). */
#define F77_INTEGER_TYPE int
#define F77_REAL_TYPE float
#define F77_REAL_FUNCTION_TYPE float
#define F77_DOUBLE_TYPE double
#define F77_LOGICAL_TYPE int
#define F77_CHARACTER_TYPE char
#define F77_BYTE_TYPE signed char
#define F77_WORD_TYPE short int
#define F77_UBYTE_TYPE unsigned char
#define F77_UWORD_TYPE unsigned short int
/* Define macros for the type of a CHARACTER and CHARACTER_ARRAY argument */
#define F77_CHARACTER_ARG_TYPE char
#define F77_CHARACTER_ARRAY_ARG_TYPE char
/* Define a macro to use when passing arguments that STARLINK FORTRAN */
/* treats as a pointer. From the point of view of C, this type should be */
/* (void *), but it is declared as type unsigned int as we actually pass */
/* an INTEGER from the FORTRAN routine. The distinction is important for */
/* architectures where the size of an INTEGER is not the same as the size */
/* of a pointer. */
#define F77_POINTER_TYPE unsigned int
/* --- Subroutine Names --- */
/* This declares that the C function returns a value of void. */
#define F77_SUBROUTINE(X) void F77_EXTERNAL_NAME(X)
/* --- Function Names --- */
/* Macros to define the types and names of functions that return values. */
/* Due the the different ways that function return values could be */
/* implemented, it is better not to use functions, but to stick to using */
/* subroutines. */
/* Character functions are implemented, but in a way that cannot be */
/* guaranteed to be portable although it will work on VMS, SunOS, Ultrix */
/* and DEC OSF/1. It would be better to return the character value as a */
/* subroutine argument where possible, rather than use a character */
/* function. */
#define F77_INTEGER_FUNCTION(X) F77_INTEGER_TYPE F77_EXTERNAL_NAME(X)
#define F77_REAL_FUNCTION(X) F77_REAL_FUNCTION_TYPE F77_EXTERNAL_NAME(X)
#define F77_DOUBLE_FUNCTION(X) F77_DOUBLE_TYPE F77_EXTERNAL_NAME(X)
#define F77_LOGICAL_FUNCTION(X) F77_LOGICAL_TYPE F77_EXTERNAL_NAME(X)
#define F77_CHARACTER_FUNCTION(X) void F77_EXTERNAL_NAME(X)
#define F77_BYTE_FUNCTION(X) F77_BYTE_TYPE F77_EXTERNAL_NAME(X)
#define F77_WORD_FUNCTION(X) F77_WORD_TYPE F77_EXTERNAL_NAME(X)
#define F77_UBYTE_FUNCTION(X) F77_UBYTE_TYPE F77_EXTERNAL_NAME(X)
#define F77_UWORD_FUNCTION(X) F77_UWORD_TYPE F77_EXTERNAL_NAME(X)
#define F77_POINTER_FUNCTION(X) F77_POINTER_TYPE F77_EXTERNAL_NAME(X)
/* --- Character return value for a function --- */
#define CHARACTER_RETURN_VALUE(X) CHARACTER(X) TRAIL(X)
#define CHARACTER_RETURN_ARG(X) CHARACTER_ARG(X) TRAIL_ARG(X)
/* --- Dummy Arguments --- */
/* Macros for defining subroutine arguments. All these macros take a */
/* single argument; the name of the parameter. On most systems, a numeric */
/* argument is passed as a pointer. */
#define INTEGER(X) F77_INTEGER_TYPE *CNF_CONST X
#define REAL(X) F77_REAL_TYPE *CNF_CONST X
#define DOUBLE(X) F77_DOUBLE_TYPE *CNF_CONST X
#define LOGICAL(X) F77_LOGICAL_TYPE *CNF_CONST X
#define BYTE(X) F77_BYTE_TYPE *CNF_CONST X
#define WORD(X) F77_WORD_TYPE *CNF_CONST X
#define UBYTE(X) F77_UBYTE_TYPE *CNF_CONST X
#define UWORD(X) F77_UWORD_TYPE *CNF_CONST X
/* Pointer arguments. Define a pointer type for passing pointer values */
/* between subroutines. */
#define POINTER(X) F77_POINTER_TYPE *CNF_CONST X
/* EXTERNAL arguments. Define a passed subroutine or function name */
#define SUBROUTINE(X) void (*X)()
#define INTEGER_FUNCTION(X) F77_INTEGER_TYPE (*X)()
#define REAL_FUNCTION(X) F77_REAL_TYPE (*X)()
#define DOUBLE_FUNCTION(X) F77_DOUBLE_TYPE (*X)()
#define LOGICAL_FUNCTION(X) F77_LOGICAL_TYPE (*X)()
#define CHARACTER_FUNCTION(X) F77_CHARACTER_TYPE (*X)()
#define BYTE_FUNCTION(X) F77_BYTE_TYPE (*X)()
#define WORD_FUNCTION(X) F77_WORD_TYPE (*X)()
#define UBYTE_FUNCTION(X) F77_UBYTE_TYPE (*X)()
#define UWORD_FUNCTION(X) F77_UWORD_TYPE (*X)()
#define POINTER_FUNCTION(X) F77_POINTER_TYPE (*X)()
/* Array arguments. */
#define INTEGER_ARRAY(X) F77_INTEGER_TYPE *CNF_CONST X
#define REAL_ARRAY(X) F77_REAL_TYPE *CNF_CONST X
#define DOUBLE_ARRAY(X) F77_DOUBLE_TYPE *CNF_CONST X
#define LOGICAL_ARRAY(X) F77_LOGICAL_TYPE *CNF_CONST X
#define BYTE_ARRAY(X) F77_BYTE_TYPE *CNF_CONST X
#define WORD_ARRAY(X) F77_WORD_TYPE *CNF_CONST X
#define UBYTE_ARRAY(X) F77_UBYTE_TYPE *CNF_CONST X
#define UWORD_ARRAY(X) F77_UWORD_TYPE *CNF_CONST X
#define POINTER_ARRAY(X) F77_POINTER_TYPE *CNF_CONST X
/* Macros to handle character arguments. */
/* Character arguments can be passed in many ways. The purpose of these */
/* macros and the GENPTR_CHARACTER macro (defined in the next section) is */
/* to generate a pointer to a character variable called ARG and an integer */
/* ARG_length containing the length of ARG. If these two variables are */
/* available directly from the argument list of the routine, then the */
/* GENPTR_CHARACTER macro is null, otherwise it works on intermediate */
/* variables. */
#define CHARACTER(X) F77_CHARACTER_TYPE *CNF_CONST X
#define TRAIL(X) ,int X ## _length
#define CHARACTER_ARRAY(X) F77_CHARACTER_TYPE *CNF_CONST X
/* --- Getting Pointers to Arguments --- */
/* Macros that ensure that a pointer to each argument is available for the */
/* programmer to use. Usually this means that these macros are null. On */
/* VMS, a pointer to a character variable has to be generated. If a */
/* particular machine were to pass arguments by reference, rather than by */
/* value, then these macros would construct the appropriate pointers. */
#define GENPTR_INTEGER(X)
#define GENPTR_REAL(X)
#define GENPTR_DOUBLE(X)
#define GENPTR_CHARACTER(X)
#define GENPTR_LOGICAL(X)
#define GENPTR_BYTE(X)
#define GENPTR_WORD(X)
#define GENPTR_UBYTE(X)
#define GENPTR_UWORD(X)
#define GENPTR_POINTER(X)
#define GENPTR_INTEGER_ARRAY(X)
#define GENPTR_REAL_ARRAY(X)
#define GENPTR_DOUBLE_ARRAY(X)
#define GENPTR_CHARACTER_ARRAY(X)
#define GENPTR_LOGICAL_ARRAY(X)
#define GENPTR_BYTE_ARRAY(X)
#define GENPTR_WORD_ARRAY(X)
#define GENPTR_UBYTE_ARRAY(X)
#define GENPTR_UWORD_ARRAY(X)
#define GENPTR_POINTER_ARRAY(X)
#define GENPTR_SUBROUTINE(X)
#define GENPTR_INTEGER_FUNCTION(X)
#define GENPTR_REAL_FUNCTION(X)
#define GENPTR_DOUBLE_FUNCTION(X)
#define GENPTR_CHARACTER_FUNCTION(X)
#define GENPTR_LOGICAL_FUNCTION(X)
#define GENPTR_BYTE_FUNCTION(X)
#define GENPTR_WORD_FUNCTION(X)
#define GENPTR_UBYTE_FUNCTION(X)
#define GENPTR_UWORD_FUNCTION(X)
#define GENPTR_POINTER_FUNCTION(X)
/* ------------------ Calling FORTRAN from C --------------------------- */
/* --- Declare variables --- */
#define DECLARE_INTEGER(X) F77_INTEGER_TYPE X
#define DECLARE_REAL(X) F77_REAL_TYPE X
#define DECLARE_DOUBLE(X) F77_DOUBLE_TYPE X
#define DECLARE_LOGICAL(X) F77_LOGICAL_TYPE X
#define DECLARE_BYTE(X) F77_BYTE_TYPE X
#define DECLARE_WORD(X) F77_WORD_TYPE X
#define DECLARE_UBYTE(X) F77_UBYTE_TYPE X
#define DECLARE_UWORD(X) F77_UWORD_TYPE X
#define DECLARE_POINTER(X) F77_POINTER_TYPE X
#define DECLARE_CHARACTER(X,L) F77_CHARACTER_TYPE X[L]; \
const int X##_length = L
/* --- Declare arrays --- */
#define DECLARE_INTEGER_ARRAY(X,D) F77_INTEGER_TYPE X[D]
#define DECLARE_REAL_ARRAY(X,D) F77_REAL_TYPE X[D]
#define DECLARE_DOUBLE_ARRAY(X,D) F77_DOUBLE_TYPE X[D]
#define DECLARE_LOGICAL_ARRAY(X,D) F77_LOGICAL_TYPE X[D]
#define DECLARE_BYTE_ARRAY(X,D) F77_BYTE_TYPE X[D]
#define DECLARE_WORD_ARRAY(X,D) F77_WORD_TYPE X[D]
#define DECLARE_UBYTE_ARRAY(X,D) F77_UBYTE_TYPE X[D]
#define DECLARE_UWORD_ARRAY(X,D) F77_UWORD_TYPE X[D]
#define DECLARE_POINTER_ARRAY(X,D) F77_POINTER_TYPE X[D]
#define DECLARE_CHARACTER_ARRAY(X,L,D) F77_CHARACTER_TYPE X[D][L]; \
const int X##_length = L
/* --- Declare and construct dynamic CHARACTER arguments --- */
#define DECLARE_CHARACTER_DYN(X) F77_CHARACTER_TYPE *X = NULL;\
int X##_length = 0
#define F77_CREATE_CHARACTER(X,L) X=cnfCref(L);\
X##_length = (L>0?L:1)
/* Declare Dynamic Fortran arrays */
#define DECLARE_INTEGER_ARRAY_DYN(X) F77_INTEGER_TYPE *X = NULL
#define DECLARE_REAL_ARRAY_DYN(X) F77_REAL_TYPE *X = NULL
#define DECLARE_DOUBLE_ARRAY_DYN(X) F77_DOUBLE_TYPE *X = NULL
#define DECLARE_LOGICAL_ARRAY_DYN(X) F77_LOGICAL_TYPE *X = NULL
#define DECLARE_BYTE_ARRAY_DYN(X) F77_BYTE_TYPE *X = NULL
#define DECLARE_WORD_ARRAY_DYN(X) F77_WORD_TYPE *X = NULL
#define DECLARE_UBYTE_ARRAY_DYN(X) F77_UBYTE_TYPE *X = NULL
#define DECLARE_UWORD_ARRAY_DYN(X) F77_UWORD_TYPE *X = NULL
#define DECLARE_POINTER_ARRAY_DYN(X) F77_POINTER_TYPE *X = NULL
#define DECLARE_CHARACTER_ARRAY_DYN(X) F77_CHARACTER_TYPE *X = NULL;\
int X##_length = 0
/* Create arrays dynamic Fortran arrays for those types which require */
/* Separate space for Fortran and C arrays */
/* Character and logical are already defined */
/* For most types there is nothing to do */
#define F77_CREATE_CHARACTER_ARRAY(X,L,N) \
{int f77dims[1];f77dims[0]=N;X=cnfCrefa(L,1,f77dims);X##_length=L;}
#define F77_CREATE_CHARACTER_ARRAY_M(X,L,N,D) X=cnfCrefa(L,N,D);\
X##_length = L
#define F77_CREATE_LOGICAL_ARRAY(X,N) \
{int f77dims[1];f77dims[0]=N;X=cnfCrela(1,f77dims);}
#define F77_CREATE_LOGICAL_ARRAY_M(X,N,D) X=cnfCrela(N,D)
#define F77_CREATE_INTEGER_ARRAY(X,N)
#define F77_CREATE_REAL_ARRAY(X,N)
#define F77_CREATE_DOUBLE_ARRAY(X,N)
#define F77_CREATE_BYTE_ARRAY(X,N)
#define F77_CREATE_UBYTE_ARRAY(X,N)
#define F77_CREATE_WORD_ARRAY(X,N)
#define F77_CREATE_UWORD_ARRAY(X,N)
#define F77_CREATE_POINTER_ARRAY(X,N)\
X=(F77_POINTER_TYPE *) malloc(N*sizeof(F77_POINTER_TYPE))
/* Associate Fortran arrays with C arrays */
/* These macros ensure that there is space somewhere for the Fortran */
/* array. They are complemetary to the CREATE_type_ARRAY macros */
#define F77_ASSOC_CHARACTER_ARRAY(F,C)
#define F77_ASSOC_LOGICAL_ARRAY(F,C)
#define F77_ASSOC_INTEGER_ARRAY(F,C) F=C
#define F77_ASSOC_REAL_ARRAY(F,C) F=C
#define F77_ASSOC_DOUBLE_ARRAY(F,C) F=C
#define F77_ASSOC_BYTE_ARRAY(F,C) F=C
#define F77_ASSOC_UBYTE_ARRAY(F,C) F=C
#define F77_ASSOC_WORD_ARRAY(F,C) F=C
#define F77_ASSOC_UWORD_ARRAY(F,C) F=C
#define F77_ASSOC_POINTER_ARRAY(F,C)
/* Free created dynamic arrays */
/* Character and logical are already defined */
/* For most types there is nothing to do */
#define F77_FREE_INTEGER(X)
#define F77_FREE_REAL(X)
#define F77_FREE_DOUBLE(X)
#define F77_FREE_BYTE(X)
#define F77_FREE_UBYTE(X)
#define F77_FREE_WORD(X)
#define F77_FREE_UWORD(X)
#define F77_FREE_POINTER(X) cnfFree((void *)X);
#define F77_FREE_CHARACTER(X) cnfFreef( X )
#define F77_FREE_LOGICAL(X) cnfFree( (char *)X )
/* --- IMPORT and EXPORT of values --- */
/* Export C variables to Fortran variables */
#define F77_EXPORT_CHARACTER(C,F,L) cnfExprt(C,F,L)
#define F77_EXPORT_DOUBLE(C,F) F=C
#define F77_EXPORT_INTEGER(C,F) F=C
#define F77_EXPORT_LOGICAL(C,F) F=C?F77_TRUE:F77_FALSE
#define F77_EXPORT_REAL(C,F) F=C
#define F77_EXPORT_BYTE(C,F) F=C
#define F77_EXPORT_WORD(C,F) F=C
#define F77_EXPORT_UBYTE(C,F) F=C
#define F77_EXPORT_UWORD(C,F) F=C
#define F77_EXPORT_POINTER(C,F) F=cnfFptr(C)
#define F77_EXPORT_LOCATOR(C,F) cnfExpch(C,F,DAT__SZLOC)
/* Allow for character strings to be NULL, protects strlen. Note this
* does not allow lengths to differ. */
#define F77_CREATE_EXPORT_CHARACTER(C,F) \
if (C) { \
F77_CREATE_CHARACTER(F,strlen(C)); \
F77_EXPORT_CHARACTER(C,F,F##_length); \
} else { \
F77_CREATE_CHARACTER(F,1); \
F77_EXPORT_CHARACTER(" ",F,F##_length); \
}
/* Export C arrays to Fortran */
/* Arrays are assumed to be 1-d so just the number of elements is given */
/* This may be OK for n-d arrays also */
/* CHARACTER arrays may be represented in C as arrays of arrays of char or */
/* as arrays of pointers to char (the _P variant) */
#define F77_EXPORT_CHARACTER_ARRAY(C,LC,F,LF,N) \
{int f77dims[1];f77dims[0]=N;cnfExprta(C,LC,F,LF,1,f77dims);}
#define F77_EXPORT_CHARACTER_ARRAY_P(C,F,LF,N) \
{int f77dims[1];f77dims[0]=N;cnfExprtap(C,F,LF,1,f77dims);}
#define F77_EXPORT_DOUBLE_ARRAY(C,F,N) F=(F77_DOUBLE_TYPE *)C
#define F77_EXPORT_INTEGER_ARRAY(C,F,N) F=(F77_INTEGER_TYPE *)C
#define F77_EXPORT_LOGICAL_ARRAY(C,F,N) \
{int f77dims[1];f77dims[0]=N;cnfExpla(C,F,1,f77dims);}
#define F77_EXPORT_REAL_ARRAY(C,F,N) F=(F77_REAL_TYPE *)C
#define F77_EXPORT_BYTE_ARRAY(C,F,N) F=(F77_BYTE_TYPE *)C
#define F77_EXPORT_WORD_ARRAY(C,F,N) F=(F77_WORD_TYPE *)C
#define F77_EXPORT_UBYTE_ARRAY(C,F,N) F=(F77_UBYTE_TYPE *)C
#define F77_EXPORT_UWORD_ARRAY(C,F,N) F=(F77_UWORD_TYPE * )C
#define F77_EXPORT_POINTER_ARRAY(C,F,N) \
{int f77i;for (f77i=0;f77i
#endif
#undef F77_CHARACTER_ARG_TYPE
#define F77_CHARACTER_ARG_TYPE struct dsc$descriptor_s
#undef F77_CHARACTER_ARRAY_ARG_TYPE
#define F77_CHARACTER_ARRAY_ARG_TYPE struct dsc$descriptor_a
#undef CHARACTER
#define CHARACTER(X) F77_CHARACTER_ARG_TYPE *CNF_CONST X/**/_arg
#undef TRAIL
#define TRAIL(X)
#undef CHARACTER_ARRAY
#define CHARACTER_ARRAY(X) F77_CHARACTER_ARRAY_ARG_TYPE *CNF_CONST X/**/_arg
#undef GENPTR_CHARACTER
#define GENPTR_CHARACTER(X) \
F77_CHARACTER_TYPE *X = X/**/_arg->dsc$a_pointer; \
int X/**/_length = X/**/_arg->dsc$w_length;
#undef GENPTR_CHARACTER_ARRAY
#define GENPTR_CHARACTER_ARRAY(X) GENPTR_CHARACTER(X)
/* --- Logical Values --- */
#undef F77_TRUE
#define F77_TRUE -1
#undef F77_ISTRUE
#define F77_ISTRUE(X) ( (X)&1 )
#undef F77_ISFALSE
#define F77_ISFALSE(X) ( ! ( (X)&1 ) )
/* --- Common Blocks --- */
#undef F77_BLANK_COMMON
#define F77_BLANK_COMMON $BLANK
/* --- Declare Variables --- */
#undef DECLARE_CHARACTER
#define DECLARE_CHARACTER(X,L) \
F77_CHARACTER_TYPE X[L]; const int X/**/_length = L; \
F77_CHARACTER_ARG_TYPE X/**/_descr = \
{ L, DSC$K_DTYPE_T, DSC$K_CLASS_S, X }; \
F77_CHARACTER_ARG_TYPE *X/**/_arg = &X/**/_descr
#undef DECLARE_CHARACTER_ARRAY
#define DECLARE_CHARACTER_ARRAY(X,L,D) \
F77_CHARACTER_TYPE X[D][L]; const int X/**/_length = L; \
F77_CHARACTER_ARRAY_ARG_TYPE X/**/_descr = \
{ L, DSC$K_DTYPE_T, DSC$K_CLASS_S, X }; \
F77_CHARACTER_ARRAY_ARG_TYPE *X/**/_arg = &X/**/_descr
/* --- The dynamic allocation of character arguments --- */
#undef DECLARE_CHARACTER_DYN
#define DECLARE_CHARACTER_DYN(X) int X/**/_length;\
F77_CHARACTER_ARG_TYPE *X/**/_arg;\
F77_CHARACTER_TYPE *X
#undef DECLARE_CHARACTER_ARRAY_DYN
#define DECLARE_CHARACTER_ARRAY_DYN(X) int X/**/_length;\
F77_CHARACTER_ARRAY_ARG_TYPE *X/**/_arg;\
F77_CHARACTER_TYPE *X
#undef F77_CREATE_CHARACTER
#define F77_CREATE_CHARACTER(X,L) X/**/_arg = cnfCref(L);\
X = X/**/_arg->dsc$a_pointer; \
X/**/_length = X/**/_arg->dsc$w_length
#undef F77_CREATE_CHARACTER_ARRAY
#define F77_CREATE_CHARACTER_ARRAY(X,L,N) \
{int f77dims[1];f77dims[0]=N;X/**/_arg=cnfCrefa(L,1,f77dims);X/**/_length=L;}
#define F77_CREATE_CHARACTER_ARRAY_M(X,L,N,D) X/**/_arg = cnfCrefa(L,N,D);\
X = X/**/_arg->dsc$a_pointer; \
X/**/_length = X/**/_arg->dsc$w_length
#undef F77_FREE_CHARACTER
#define F77_FREE_CHARACTER(X) cnfFreef( X/**/_arg )
/* --- Pass arguments to a FORTRAN routine --- */
#undef CHARACTER_ARG
#define CHARACTER_ARG(X) X/**/_arg
#undef CHARACTER_ARRAY_ARG
#define CHARACTER_ARRAY_ARG(X) X/**/_arg
#undef TRAIL_ARG
#define TRAIL_ARG(X)
#endif /* VMS */
/* ----------------------------------------------------------------------- */
/*--------------------------
| DECstation Ultrix (cc) |
| DECstation Ultrix (c89) |
| DECstation OSF/1 |
| Alpha OSF/1 |
--------------------------*/
/* Do this complicated set of definitions as a single #if cannot be */
/* continued across multiple lines. */
#if defined(mips) && defined(ultrix)
#define _dec_unix 1
#endif
#if defined(__mips) && defined(__ultrix)
#define _dec_unix 1
#endif
#if defined(__mips__) && defined(__osf__)
#define _dec_unix 1
#endif
#if defined(__alpha) && defined(__osf__)
#define _dec_unix 1
#endif
#if _dec_unix
/* The macros for Ultrix are the same as the standard ones except for ones */
/* dealing with logical values. The ANSI definitions work with the c89 */
/* compiler, and the non ANSI definitions work with the cc compiler. */
/* The same applies to DEC OSF/1, except that its cc compiler is ANSI */
/* compliant. */
/* --- Logical Values --- */
/* Redefine macros that evaluate to a C logical value, given a FORTRAN */
/* logical value. These definitions are only valid when used with the DEC */
/* FORTRAN for RISC compiler. If you are using the earlier FORTRAN for */
/* RISC compiler from MIPS, then these macros should be deleted. */
#undef F77_TRUE
#define F77_TRUE -1
#undef F77_ISTRUE
#define F77_ISTRUE(X) ( (X)&1 )
#undef F77_ISFALSE
#define F77_ISFALSE(X) ( ! ( (X)&1 ) )
#endif /* DEC Unix */
/*
*+
* Name:
* cnf.h
* Purpose:
* Function prototypes for cnf routines
* Language:
* ANSI C
* Type of Module:
* C include file
* Description:
* These are the prototype definitions for the functions in the CNF
* library. They are used used in mixing C and FORTRAN programs.
* Copyright:
* Copyright (C) 1991 Science & Engineering Research Council
* Authors:
* PMA: Peter Allan (Starlink, RAL)
* AJC: Alan Chipperfield (Starlink, RAL)
* {enter_new_authors_here}
* History:
* 23-MAY-1991 (PMA):
* Original version.
* 12-JAN-1996 (AJC):
* Add cnf_cref and cnf_freef
* 14-JUN-1996 (AJC):
* Add cnf_crefa, imprta, exprta
* crela, impla, expla
* 18-JUL-1996 (AJC):
* Add impch and expch
* 17-MAR-1998 (AJC):
* Add imprtap and exprtap
* {enter_changes_here}
* Bugs:
* {note_any_bugs_here}
*-
------------------------------------------------------------------------------
*/
void cnfInitRTL( int, char** );
void *cnfCalloc( size_t, size_t );
void cnfCopyf( const char *source_f, int source_len, char *dest_f,
int dest_len );
void *cnfCptr( F77_POINTER_TYPE );
char *cnfCreat( int length );
F77_CHARACTER_ARG_TYPE *cnfCref( int length );
F77_CHARACTER_ARG_TYPE *cnfCrefa( int length, int ndims, const int *dims );
char *cnfCreib( const char *source_f, int source_len );
char *cnfCreim( const char *source_f, int source_len );
F77_LOGICAL_TYPE *cnfCrela( int ndims, const int *dims );
void cnfExpch( const char *source_c, char *dest_f, int nchars );
void cnfExpla( const int *source_c, F77_LOGICAL_TYPE *dest_f, int ndims,
const int *dims );
void cnfExpn( const char *source_c, int max, char *dest_f, int dest_len );
void cnfExprt( const char *source_c, char *dest_f, int dest_len );
void cnfExprta( const char *source_c, int source_len, char *dest_f,
int dest_len, int ndims, const int *dims );
void cnfExprtap( char *const *source_c, char *dest_f, int dest_len,
int ndims, const int *dims );
F77_POINTER_TYPE cnfFptr( void *cpointer );
void cnfFree( void * );
void cnfFreef( F77_CHARACTER_ARG_TYPE *temp );
void cnfImpb( const char *source_f, int source_len, char *dest_c );
void cnfImpbn( const char *source_f, int source_len, int max, char *dest_c );
void cnfImpch( const char *source_f, int nchars, char *dest_c );
void cnfImpla( const F77_LOGICAL_TYPE *source_f, int *dest_c,
int ndims, const int *dims );
void cnfImpn( const char *source_f, int source_len, int max, char *dest_c );
void cnfImprt( const char *source_f, int source_len, char *dest_c );
void cnfImprta( const char *source_f, int source_len, char *dest_c,
int dest_len, int ndims, const int *dims );
void cnfImprtap( const char *source_f, int source_len, char *const *dest_c,
int dest_len, int ndims, const int *dims );
int cnfLenc( const char *source_c );
int cnfLenf( const char *source_f, int source_len );
void *cnfMalloc( size_t );
void *cnfRealloc( void *, size_t );
int cnfRegp( void * );
void cnfUregp( void * );
void cnfLock( void );
void cnfUnlock( void );
#endif
#ifndef CNF_OLD_DEFINED
#define CNF_OLD_DEFINED
/* Define old names to be new names */
#define cnf_calloc cnfCalloc
#define cnf_copyf cnfCopyf
#define cnf_cptr cnfCptr
#define cnf_creat cnfCreat
#define cnf_cref cnfCref
#define cnf_crefa cnfCrefa
#define cnf_creib cnfCreib
#define cnf_creim cnfCreim
#define cnf_crela cnfCrela
#define cnf_expch cnfExpch
#define cnf_expla cnfExpla
#define cnf_expn cnfExpn
#define cnf_exprt cnfExprt
#define cnf_exprta cnfExprta
#define cnf_exprtap cnfExprtap
#define cnf_fptr cnfFptr
#define cnf_free cnfFree
#define cnf_freef cnfFreef
#define cnf_impb cnfImpb
#define cnf_impbn cnfImpbn
#define cnf_impch cnfImpch
#define cnf_impla cnfImpla
#define cnf_impn cnfImpn
#define cnf_imprt cnfImprt
#define cnf_imprta cnfImprta
#define cnf_imprtap cnfImprtap
#define cnf_lenc cnfLenc
#define cnf_lenf cnfLenf
#define cnf_malloc cnfMalloc
#define cnf_regp cnfRegp
#define cnf_uregp cnfUregp
#endif /* CNF_MACROS */
ast-8.0.7/fxmlchan.c 0000664 0001750 0001750 00000010070 12610415012 011172 0000000 0000000 /*
*+
* Name:
* fxmlchan.c
* Purpose:
* Define a FORTRAN 77 interface to the AST XmlChan class.
* Type of Module:
* C source file.
* Description:
* This file defines FORTRAN 77-callable C functions which provide
* a public FORTRAN 77 interface to the XmlChan class.
* Routines Defined:
* AST_XMLCHAN
* AST_ISAXMLCHAN
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* 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
* .
* Authors:
* DSB: David S. Berry (Starlink)
* History:
* 21-OCT-2003 (DSB):
* Original version.
*/
/* Define the astFORTRAN77 macro which prevents error messages from
AST C functions from reporting the file and line number where the
error occurred (since these would refer to this file, they would
not be useful). */
#define astFORTRAN77
/* Header files. */
/* ============= */
#include "f77.h" /* FORTRAN <-> C interface macros (SUN/209) */
#include "c2f77.h" /* F77 <-> C support functions/macros */
#include "error.h" /* Error reporting facilities */
#include "memory.h" /* Memory handling facilities */
#include "channel.h" /* Provides wrapper functions */
#include "xmlchan.h" /* C interface to the XmlChan class */
#include
/* Prototypes for external functions. */
/* ================================== */
/* This is the null function defined by the FORTRAN interface in fobject.c. */
F77_SUBROUTINE(ast_null)( void );
/* FORTRAN interface functions. */
/* ============================ */
/* These functions implement the remainder of the FORTRAN interface. */
F77_INTEGER_FUNCTION(ast_xmlchan)( void (* SOURCE)(),
void (* SINK)(),
CHARACTER(OPTIONS),
INTEGER(STATUS)
TRAIL(OPTIONS) ) {
GENPTR_CHARACTER(OPTIONS)
F77_INTEGER_TYPE(RESULT);
char *options;
const char *(* source)( void );
int i;
void (* sink)( const char * );
astAt( "AST_XMLCHAN", NULL, 0 );
astWatchSTATUS(
/* Set the source and sink function pointers to NULL if a pointer to
the null routine AST_NULL has been supplied. */
source = (const char *(*)( void )) SOURCE;
if ( source == (const char *(*)( void )) F77_EXTERNAL_NAME(ast_null) ) {
source = NULL;
}
sink = (void (*)( const char * )) SINK;
if ( sink == (void (*)( const char * )) F77_EXTERNAL_NAME(ast_null) ) {
sink = NULL;
}
options = astString( OPTIONS, OPTIONS_length );
/* Truncate the options string to exlucde any trailing spaces. */
astChrTrunc( options );
/* Change ',' to '\n' (see AST_SET in fobject.c for why). */
if ( astOK ) {
for ( i = 0; options[ i ]; i++ ) {
if ( options[ i ] == ',' ) options[ i ] = '\n';
}
}
RESULT = astP2I( astXmlChanFor( source, astSourceWrap, sink, astSinkWrap,
"%s", options ) );
astFree( options );
)
return RESULT;
}
F77_LOGICAL_FUNCTION(ast_isaxmlchan)( INTEGER(THIS),
INTEGER(STATUS) ) {
GENPTR_INTEGER(THIS)
F77_LOGICAL_TYPE(RESULT);
astAt( "AST_ISAXMLCHAN", NULL, 0 );
astWatchSTATUS(
RESULT = astIsAXmlChan( astI2P( *THIS ) ) ? F77_TRUE : F77_FALSE;
)
return RESULT;
}
ast-8.0.7/zoommap.c 0000664 0001750 0001750 00000213017 12610415012 011062 0000000 0000000 /*
*class++
* Name:
* ZoomMap
* Purpose:
* Zoom coordinates about the origin.
* Constructor Function:
c astZoomMap
f AST_ZOOMMAP
* Description:
* The ZoomMap class implements a Mapping which performs a "zoom"
* transformation by multiplying all coordinate values by the same
* scale factor (the inverse transformation is performed by
* dividing by this scale factor). The number of coordinate values
* representing each point is unchanged.
* Inheritance:
* The ZoomMap class inherits from the Mapping class.
* Attributes:
* In addition to those attributes common to all Mappings, every
* ZoomMap also has the following attributes:
*
* - Zoom: ZoomMap scale factor
* Functions:
c The ZoomMap class does not define any new functions beyond those
f The ZoomMap 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
* 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
* .
* Authors:
* RFWS: R.F. Warren-Smith (Starlink)
* DSB: David S. Berry (Starlink)
* History:
* 1-FEB-1996 (RFWS):
* Original version.
* 18-JUL-1996 (RFWS):
* Updated to support attributes and an external interface.
* 10-SEP-1996 (RFWS):
* Added I/O facilities.
* 4-JUN-1997 (RFWS):
* Over-ride the MapMerge method to provide ZoomMap
* simplification facilities.
* 8-JAN-2003 (DSB):
* Changed private InitVtab method to protected astInitZoomMapVtab
* method.
* 10-MAY-2006 (DSB):
* Override astEqual.
*class--
*/
/* Module Macros. */
/* ============== */
/* Set the name of the class we are implementing. This indicates to
the header files that define class interfaces that they should make
"protected" symbols available. */
#define astCLASS ZoomMap
/* 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 "globals.h" /* Thread-safe global data access */
#include "object.h" /* Base Object class */
#include "pointset.h" /* Sets of points/coordinates */
#include "mapping.h" /* Coordinate mappings (parent class) */
#include "channel.h" /* I/O channels */
#include "unitmap.h" /* Unit Mappings */
#include "matrixmap.h" /* Matrix Mappings */
#include "zoommap.h" /* Interface definition for this class */
/* Error code definitions. */
/* ----------------------- */
#include "ast_err.h" /* AST error codes */
/* C header files. */
/* --------------- */
#include
#include
#include
#include
#include
#include
/* 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 AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
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 * );
/* 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->GetAttrib_Buff[ 0 ] = 0;
/* Create the function that initialises global data for this module. */
astMAKE_INITGLOBALS(ZoomMap)
/* Define macros for accessing each item of thread specific global data. */
#define class_init astGLOBAL(ZoomMap,Class_Init)
#define class_vtab astGLOBAL(ZoomMap,Class_Vtab)
#define getattrib_buff astGLOBAL(ZoomMap,GetAttrib_Buff)
/* If thread safety is not needed, declare and initialise globals at static
variables. */
#else
static char getattrib_buff[ 101 ];
/* Define the class virtual function table and its initialisation flag
as static variables. */
static AstZoomMapVtab 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. */
AstZoomMap *astZoomMapId_( int, double, const char *, ... );
/* Prototypes for Private Member Functions. */
/* ======================================== */
static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
static const char *GetAttrib( AstObject *, const char *, int * );static double GetZoom( AstZoomMap *, int * );
static double Rate( AstMapping *, double *, int, int, int * );
static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * );
static int Equal( AstObject *, AstObject *, int * );
static int GetIsLinear( AstMapping *, int * );
static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * );
static int TestAttrib( AstObject *, const char *, int * );
static int TestZoom( AstZoomMap *, int * );
static void ClearAttrib( AstObject *, const char *, int * );
static void ClearZoom( AstZoomMap *, int * );
static void Dump( AstObject *, AstChannel *, int * );
static void SetAttrib( AstObject *, const char *, int * );
static void SetZoom( AstZoomMap *, double, int * );
/* Member functions. */
/* ================= */
static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
* Name:
* ClearAttrib
* Purpose:
* Clear an attribute value for a ZoomMap.
* Type:
* Private function.
* Synopsis:
* #include "zoommap.h"
* void ClearAttrib( AstObject *this, const char *attrib, int *status )
* Class Membership:
* ZoomMap member function (over-rides the astClearAttrib protected
* method inherited from the Mapping class).
* Description:
* This function clears the value of a specified attribute for a
* ZoomMap, so that the default value will subsequently be used.
* Parameters:
* this
* Pointer to the ZoomMap.
* 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: */
AstZoomMap *this; /* Pointer to the ZoomMap structure */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the ZoomMap structure. */
this = (AstZoomMap *) this_object;
/* Check the attribute name and clear the appropriate attribute. */
/* Zoom. */
/* ----- */
if ( !strcmp( attrib, "zoom" ) ) {
astClearZoom( 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 int Equal( AstObject *this_object, AstObject *that_object, int *status ) {
/*
* Name:
* Equal
* Purpose:
* Test if two ZoomMaps are equivalent.
* Type:
* Private function.
* Synopsis:
* #include "zoommap.h"
* int Equal( AstObject *this, AstObject *that, int *status )
* Class Membership:
* ZoomMap member function (over-rides the astEqual protected
* method inherited from the astMapping class).
* Description:
* This function returns a boolean result (0 or 1) to indicate whether
* two ZoomMaps are equivalent.
* Parameters:
* this
* Pointer to the first Object (a ZoomMap).
* that
* Pointer to the second Object.
* status
* Pointer to the inherited status variable.
* Returned Value:
* One if the ZoomMaps are equivalent, zero otherwise.
* 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: */
AstZoomMap *that;
AstZoomMap *this;
int nin;
int nout;
int result;
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain pointers to the two ZoomMap structures. */
this = (AstZoomMap *) this_object;
that = (AstZoomMap *) that_object;
/* Check the second object is a ZoomMap. We know the first is a
ZoomMap since we have arrived at this implementation of the virtual
function. */
if( astIsAZoomMap( that ) ) {
/* Get the number of inputs and outputs and check they are the same for both. */
nin = astGetNin( this );
nout = astGetNout( this );
if( astGetNin( that ) == nin && astGetNout( that ) == nout ) {
/* If the Invert flags for the two ZoomMaps differ, it may still be possible
for them to be equivalent. First compare the ZoomMaps if their Invert
flags are the same. In this case all the attributes of the two ZoomMaps
must be identical. */
if( astGetInvert( this ) == astGetInvert( that ) ) {
result = ( astEQUAL( this->zoom, that->zoom ) );
/* If the Invert flags for the two ZoomMaps differ, the attributes of the two
ZoomMaps must be inversely related to each other. */
} else {
result = ( astEQUAL( this->zoom, 1.0/that->zoom ) );
}
}
}
/* If an error occurred, clear the result value. */
if ( !astOK ) result = 0;
/* Return the result, */
return result;
}
static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
* Name:
* GetAttrib
* Purpose:
* Get the value of a specified attribute for a ZoomMap.
* Type:
* Private function.
* Synopsis:
* #include "zoommap.h"
* const char *GetAttrib( AstObject *this, const char *attrib, int *status )
* Class Membership:
* ZoomMap member function (over-rides the protected astGetAttrib
* method inherited from the Mapping class).
* Description:
* This function returns a pointer to the value of a specified
* attribute for a ZoomMap, formatted as a character string.
* Parameters:
* this
* Pointer to the ZoomMap.
* 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 ZoomMap, 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 ZoomMap. 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 /* Pointer to thread-specific global data */
AstZoomMap *this; /* Pointer to the ZoomMap structure */
const char *result; /* Pointer value to return */
double zoom; /* Zoom attribute value */
/* Initialise. */
result = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(this_object);
/* Obtain a pointer to the ZoomMap structure. */
this = (AstZoomMap *) 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. */
/* Zoom. */
/* ----- */
if ( !strcmp( attrib, "zoom" ) ) {
zoom = astGetZoom( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%.*g", DBL_DIG, zoom );
result = getattrib_buff;
}
/* If the attribute name was not recognised, pass it on to the parent
method for further interpretation. */
} else {
result = (*parent_getattrib)( this_object, attrib, status );
}
/* Return the result. */
return result;
}
static int GetIsLinear( AstMapping *this_mapping, int *status ){
/*
* Name:
* GetIsLinear
* Purpose:
* Return the value of the IsLinear attribute for a ZoomMap.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* void GetIsLinear( AstMapping *this, int *status )
* Class Membership:
* ZoomMap member function (over-rides the protected astGetIsLinear
* method inherited from the Mapping class).
* Description:
* This function returns the value of the IsLinear attribute for a
* Frame, which is always one.
* Parameters:
* this
* Pointer to the ZoomMap.
* status
* Pointer to the inherited status variable.
*/
return 1;
}
void astInitZoomMapVtab_( AstZoomMapVtab *vtab, const char *name, int *status ) {
/*
*+
* Name:
* astInitZoomMapVtab
* Purpose:
* Initialise a virtual function table for a ZoomMap.
* Type:
* Protected function.
* Synopsis:
* #include "zoommap.h"
* void astInitZoomMapVtab( AstZoomMapVtab *vtab, const char *name )
* Class Membership:
* ZoomMap vtab initialiser.
* Description:
* This function initialises the component of a virtual function
* table which is used by the ZoomMap 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 */
AstMappingVtab *mapping; /* Pointer to Mapping 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. */
astInitMappingVtab( (AstMappingVtab *) vtab, name );
/* Store a unique "magic" value in the virtual function table. This
will be used (by astIsAZoomMap) 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 = &(((AstMappingVtab *) vtab)->id);
/* Initialise member function pointers. */
/* ------------------------------------ */
/* Store pointers to the member functions (implemented here) that provide
virtual methods for this class. */
vtab->ClearZoom = ClearZoom;
vtab->GetZoom = GetZoom;
vtab->SetZoom = SetZoom;
vtab->TestZoom = TestZoom;
/* Save the inherited pointers to methods that will be extended, and
replace them with pointers to the new member functions. */
object = (AstObjectVtab *) vtab;
mapping = (AstMappingVtab *) vtab;
parent_clearattrib = object->ClearAttrib;
object->ClearAttrib = ClearAttrib;
parent_getattrib = object->GetAttrib;
object->GetAttrib = GetAttrib;
parent_setattrib = object->SetAttrib;
object->SetAttrib = SetAttrib;
parent_testattrib = object->TestAttrib;
object->TestAttrib = TestAttrib;
parent_transform = mapping->Transform;
mapping->Transform = Transform;
/* Store replacement pointers for methods which will be over-ridden by
new member functions implemented here. */
object->Equal = Equal;
mapping->MapMerge = MapMerge;
mapping->MapSplit = MapSplit;
mapping->Rate = Rate;
mapping->GetIsLinear = GetIsLinear;
/* Declare the class dump function. There is no copy constructor or
destructor. */
astSetDump( vtab, Dump, "ZoomMap", "Zoom about the origin" );
/* 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 int MapMerge( AstMapping *this, int where, int series, int *nmap,
AstMapping ***map_list, int **invert_list, int *status ) {
/*
* Name:
* MapMerge
* Purpose:
* Simplify a sequence of Mappings containing a ZoomMap.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* int MapMerge( AstMapping *this, int where, int series, int *nmap,
* AstMapping ***map_list, int **invert_list, int *status )
* Class Membership:
* ZoomMap method (over-rides the protected astMapMerge method
* inherited from the Mapping class).
* Description:
* This function attempts to simplify a sequence of Mappings by
* merging a nominated ZoomMap in the sequence with its neighbours,
* so as to shorten the sequence if possible.
*
* In many cases, simplification will not be possible and the
* function will return -1 to indicate this, without further
* action.
*
* In most cases of interest, however, this function will either
* attempt to replace the nominated ZoomMap with one which it
* considers simpler, or to merge it with the Mappings which
* immediately precede it or follow it in the sequence (both will
* normally be considered). This is sufficient to ensure the
* eventual simplification of most Mapping sequences by repeated
* application of this function.
*
* In some cases, the function may attempt more elaborate
* simplification, involving any number of other Mappings in the
* sequence. It is not restricted in the type or scope of
* simplification it may perform, but will normally only attempt
* elaborate simplification in cases where a more straightforward
* approach is not adequate.
* Parameters:
* this
* Pointer to the nominated ZoomMap which is to be merged with
* its neighbours. This should be a cloned copy of the ZoomMap
* pointer contained in the array element "(*map_list)[where]"
* (see below). This pointer will not be annulled, and the
* ZoomMap it identifies will not be modified by this function.
* where
* Index in the "*map_list" array (below) at which the pointer
* to the nominated ZoomMap resides.
* series
* A non-zero value indicates that the sequence of Mappings to
* be simplified will be applied in series (i.e. one after the
* other), whereas a zero value indicates that they will be
* applied in parallel (i.e. on successive sub-sets of the
* input/output coordinates).
* nmap
* Address of an int which counts the number of Mappings in the
* sequence. On entry this should be set to the initial number
* of Mappings. On exit it will be updated to record the number
* of Mappings remaining after simplification.
* map_list
* Address of a pointer to a dynamically allocated array of
* Mapping pointers (produced, for example, by the astMapList
* method) which identifies the sequence of Mappings. On entry,
* the initial sequence of Mappings to be simplified should be
* supplied.
*
* On exit, the contents of this array will be modified to
* reflect any simplification carried out. Any form of
* simplification may be performed. This may involve any of: (a)
* removing Mappings by annulling any of the pointers supplied,
* (b) replacing them with pointers to new Mappings, (c)
* inserting additional Mappings and (d) changing their order.
*
* The intention is to reduce the number of Mappings in the
* sequence, if possible, and any reduction will be reflected in
* the value of "*nmap" returned. However, simplifications which
* do not reduce the length of the sequence (but improve its
* execution time, for example) may also be performed, and the
* sequence might conceivably increase in length (but normally
* only in order to split up a Mapping into pieces that can be
* more easily merged with their neighbours on subsequent
* invocations of this function).
*
* If Mappings are removed from the sequence, any gaps that
* remain will be closed up, by moving subsequent Mapping
* pointers along in the array, so that vacated elements occur
* at the end. If the sequence increases in length, the array
* will be extended (and its pointer updated) if necessary to
* accommodate any new elements.
*
* Note that any (or all) of the Mapping pointers supplied in
* this array may be annulled by this function, but the Mappings
* to which they refer are not modified in any way (although
* they may, of course, be deleted if the annulled pointer is
* the final one).
* invert_list
* Address of a pointer to a dynamically allocated array which,
* on entry, should contain values to be assigned to the Invert
* attributes of the Mappings identified in the "*map_list"
* array before they are applied (this array might have been
* produced, for example, by the astMapList method). These
* values will be used by this function instead of the actual
* Invert attributes of the Mappings supplied, which are
* ignored.
*
* On exit, the contents of this array will be updated to
* correspond with the possibly modified contents of the
* "*map_list" array. If the Mapping sequence increases in
* length, the "*invert_list" array will be extended (and its
* pointer updated) if necessary to accommodate any new
* elements.
* status
* Pointer to the inherited status variable.
* Returned Value:
* If simplification was possible, the function returns the index
* in the "map_list" array of the first element which was
* modified. Otherwise, it returns -1 (and makes no changes to the
* arrays supplied).
* Notes:
* - A value of -1 will be returned if this function is invoked
* with the global error status set, or if it should fail for any
* reason.
*/
/* Local Variables: */
AstMapping *map; /* Pointer to Mapping */
AstMapping *new; /* Pointer to replacement Mapping */
const char *class; /* Pointer to Mapping class string */
double *zooms; /* Pointer to zoom factor array */
double maxzoom; /* Maximum zoom factor */
double minzoom; /* Minimum zoom factor */
double zoom; /* Zoom factor */
int coord; /* Loop counter for coordinates */
int imap1; /* Index of first ZoomMap */
int imap2; /* Index of last ZoomMap */
int imap; /* Loop counter for Mappings */
int ncoord; /* Number of input/output coordinates */
int ngone; /* Number of Mappings eliminated */
int nin; /* Total number of input coordinates */
int nzoom; /* Number of zoom factors */
int result; /* Result value to return */
int simpler; /* Mapping(s) simplified? */
int single; /* Replacement is a single ZoomMap? */
int unit; /* Replacement Mapping is a UnitMap? */
/* Initialise the returned result. */
result = -1;
/* Check the global error status. */
if ( !astOK ) return result;
/* Further initialisation. */
new = NULL;
ngone = 0;
simpler = 0;
/* In series. */
/* ---------- */
/* Handle the case where the Mappings are connected in series. */
if ( series ) {
/* Obtain the effective zoom factor of the nominated Mapping (which is
a ZoomMap) when its Invert attribute is set to the value given. */
zoom = astGetZoom( ( *map_list )[ where ] );
if ( ( *invert_list )[ where ] ) zoom = 1.0 / zoom;
/* Search adjacent lower-numbered Mappings and identify the class to
which they belong. */
imap1 = where;
while ( astOK && ( ( imap1 - 1 ) >= 0 ) ) {
map = ( *map_list )[ imap1 - 1 ];
class = astGetClass( map );
if ( astOK ) {
/* For each ZoomMap found, obtain the effective zoom factor (taking
account of the associated invert flag value), and accumulate the
overall zoom factor to be used for the simplified Mapping. */
if ( !strcmp( class, "ZoomMap" ) ) {
if ( ( *invert_list )[ imap1 - 1 ] ) {
zoom /= astGetZoom( map );
} else {
zoom *= astGetZoom( map );
}
/* UnitMaps have an effective zoom factor of unity, so we include them
but they so have no effect. Quit looping when the first Mapping is
found which is not a ZoomMap or a UnitMap. */
} else if ( strcmp( class, "UnitMap" ) ) {
break;
}
imap1--;
}
}
/* Similarly search adjacent higher-numbered ZoomMaps and UnitMaps and
accumulate their effective zoom factors. */
imap2 = where;
while ( astOK && ( ( imap2 + 1 ) < *nmap ) ) {
map = ( *map_list )[ imap2 + 1 ];
class = astGetClass( map );
if ( astOK ) {
if ( !strcmp( class, "ZoomMap" ) ) {
if ( ( *invert_list )[ imap2 + 1 ] ) {
zoom /= astGetZoom( map );
} else {
zoom *= astGetZoom( map );
}
} else if ( strcmp( class, "UnitMap" ) ) {
break;
}
imap2++;
}
}
/* Determine how many Mappings can be eliminated by condensing those
found above into a single Mapping. */
ngone = imap2 - imap1;
/* Determine if the replacement Mapping can be a UnitMap. This will be
the case only if the accumulated zoom factor is unity (within some
tolerable error). */
unit = ( fabs( zoom - 1.0 ) <= ( 8.0 * DBL_EPSILON ) );
/* Determine if simplification is possible. This will be so if (a)
Mappings can be eliminated ("ngone" is non-zero), or (b) the
replacement can be a UnitMap, or (c) where there was initially only
one ZoomMap, its invert flag was set (it will always be cleared in
the replacement Mapping). */
simpler = ngone || unit || ( *invert_list )[ where ];
/* Do nothing more unless simplification is possible. */
if ( simpler ) {
/* Obtain the number of input/output coordinates for the replacement
Mapping and create the appropriate replacement. */
nin = astGetNin( ( *map_list )[ where ] );
if ( unit ) {
new = (AstMapping *) astUnitMap( nin, "", status );
} else {
new = (AstMapping *) astZoomMap( nin, zoom, "", status );
}
}
/* In parallel. */
/* ------------ */
/* Handle the case where the Mappings are connected in parallel. */
} else {
/* Obtain the number of input/output coordinates for the nominated Mapping. */
nin = astGetNin( ( *map_list )[ where ] );
/* Search adjacent lower-numbered Mappings and identify the class to
which they belong. */
imap1 = where;
while ( astOK && ( ( imap1 - 1 ) >= 0 ) ) {
map = ( *map_list )[ imap1 - 1 ];
class = astGetClass( map );
if ( astOK ) {
/* Quit looping when the first Mapping is found which is not a ZoomMap
or a UnitMap. */
if ( strcmp( class, "ZoomMap" ) &&
strcmp( class, "UnitMap" ) ) break;
/* Accumulate the total number of input/output coordinates for each
adjacent ZoomMap and UnitMap found. */
nin += astGetNin( map );
imap1--;
}
}
/* Similarly search adjacent higher-numbered Mappings and accumulate
the number of input/output coordinates.*/
imap2 = where;
while ( astOK && ( ( imap2 + 1 ) < *nmap ) ) {
map = ( *map_list )[ imap2 + 1 ];
class = astGetClass( map );
if ( astOK ) {
if ( strcmp( class, "ZoomMap" ) &&
strcmp( class, "UnitMap" ) ) break;
nin += astGetNin( map );
imap2++;
}
}
/* Allocate memory for an array of zoom factors, one for each
input/output coordinate in the replacement Mapping. */
zooms = astMalloc( sizeof( double ) * (size_t) nin );
if ( astOK ) {
/* Initialise. */
nzoom = 0;
minzoom = DBL_MAX;
maxzoom = -DBL_MAX;
/* Loop through all the ZoomMaps and UnitMaps being merged and again
identify the class to which they belong. */
for ( imap = imap1; astOK && ( imap <= imap2 ); imap++ ) {
map = ( *map_list )[ imap ];
class = astGetClass( map );
if ( astOK ) {
/* Set a default zoom factor of unity (for UnitMaps). If the Mapping
is a ZoomMap, replace this with the effective zoom factor, taking
account of the associated invert flag. */
zoom = 1.0;
if ( !strcmp( class, "ZoomMap" ) ) {
if ( ( *invert_list )[ imap ] ) {
zoom = 1.0 / astGetZoom( map );
} else {
zoom = astGetZoom( map );
}
}
/* Replicate the zoom factor in the zoom factor array, once for each
input/output coordinate associated with the current Mapping. */
ncoord = astGetNin( map );
for ( coord = 0; coord < ncoord; coord++ ) {
zooms[ nzoom++ ] = zoom;
}
/* Store the minimum and maximum zoom factors encountered. */
if ( zoom < minzoom ) minzoom = zoom;
if ( zoom > maxzoom ) maxzoom = zoom;
}
}
/* Determine how many Mappings can be eliminated by condensing those
found above into a single Mapping. */
ngone = imap2 - imap1;
/* Determine whether the replacement Mapping can be a single
ZoomMap. This will be so if all the effective zoom factors were the
same. */
single = ( minzoom == maxzoom );
/* Determine if the replacement Mapping can be a UnitMap. This will be
so if all the effective zoom factors were equal to unity. */
unit = single && ( minzoom == 1.0 );
/* Determine if simplification is possible. This will be so if (a)
Mappings can be eliminated ("ngone" is non-zero), or (b) the
replacement can be a UnitMap, or (c) where there was initially only
one ZoomMap, its invert flag was set (it will always be cleared in
the replacement Mapping). */
simpler = ngone || unit || ( *invert_list )[ where ];
/* Do nothing more unless simplification is possible. */
if ( simpler ) {
/* Create a replacement UnitMap or ZoomMap if appropriate. */
if ( unit ) {
new = (AstMapping *) astUnitMap( nin, "", status );
} else if ( single ) {
new = (AstMapping *) astZoomMap( nin, minzoom, "", status );
/* Otherwise, replace the original ZoomMaps and UnitMaps with a
diagonal MatrixMap containing the zoom factors as its diagonal
elements. */
} else {
new = (AstMapping *) astMatrixMap( nin, nin, 1, zooms, "", status );
}
}
}
/* Free the array of zoom factors. */
zooms = astFree( zooms );
}
/* If OK, and simplification is possible, we now have a replacement
Mapping and can insert it into the Mapping list. */
if ( astOK ) {
if ( simpler ) {
/* Annul the pointers to all the ZoomMaps that are being replaced. */
for ( imap = imap1; imap <= imap2; imap++ ) {
( *map_list )[ imap ] = astAnnul( ( *map_list )[ imap ] );
}
/* Insert the new pointer and the associated invert flag. */
( *map_list )[ imap1 ] = new;
( *invert_list )[ imap1 ] = 0;
/* Loop to close the resulting gap by moving subsequent elements down
in the arrays. */
for ( imap = imap2 + 1; imap < *nmap; imap++ ) {
( *map_list )[ imap - ngone ] = ( *map_list )[ imap ];
( *invert_list )[ imap - ngone ] = ( *invert_list )[ imap ];
}
/* Clear the vacated elements at the end. */
for ( imap = *nmap - ngone; imap < *nmap; imap++ ) {
( *map_list )[ imap ] = NULL;
( *invert_list )[ imap ] = 0;
}
/* Decrement the Mapping count and return the index of the first
modified element. */
( *nmap ) -= ngone;
result = imap1;
}
/* If an error occurred, but a new Mapping has been created, then
annul it. */
} else {
if ( new ) new = astAnnul( new );
}
/* If an error occurred, clear the returned result. */
if ( !astOK ) result = -1;
/* Return the result. */
return result;
}
static int *MapSplit( AstMapping *this_map, int nin, const int *in, AstMapping **map, int *status ){
/*
* Name:
* MapSplit
* Purpose:
* Create a Mapping representing a subset of the inputs of an existing
* ZoomMap.
* Type:
* Private function.
* Synopsis:
* #include "zoommap.h"
* int *MapSplit( AstMapping *this, int nin, const int *in, AstMapping **map, int *status )
* Class Membership:
* ZoomMap method (over-rides the protected astMapSplit method
* inherited from the Mapping class).
* Description:
* This function creates a new Mapping by picking specified inputs from
* an existing ZoomMap. This is only possible if the specified inputs
* correspond to some subset of the ZoomMap outputs. That is, there
* must exist a subset of the ZoomMap outputs for which each output
* depends only on the selected ZoomMap inputs, and not on any of the
* inputs which have not been selected. If this condition is not met
* by the supplied ZoomMap, then a NULL Mapping is returned.
* Parameters:
* this
* Pointer to the ZoomMap to be split (the ZoomMap is not actually
* modified by this function).
* nin
* The number of inputs to pick from "this".
* in
* Pointer to an array of indices (zero based) for the inputs which
* are to be picked. This array should have "nin" elements. If "Nin"
* is the number of inputs of the supplied ZoomMap, then each element
* should have a value in the range zero to Nin-1.
* map
* Address of a location at which to return a pointer to the new
* Mapping. This Mapping will have "nin" inputs (the number of
* outputs may be different to "nin"). A NULL pointer will be
* returned if the supplied ZoomMap has no subset of outputs which
* depend only on the selected inputs.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to a dynamically allocated array of ints. The number of
* elements in this array will equal the number of outputs for the
* returned Mapping. Each element will hold the index of the
* corresponding output in the supplied ZoomMap. The array should be
* freed using astFree when no longer needed. A NULL pointer will
* be returned if no output Mapping can be created.
* Notes:
* - If this function is invoked with the global error status set,
* or if it should fail for any reason, then NULL values will be
* returned as the function value and for the "map" pointer.
*/
/* Local Variables: */
AstZoomMap *this; /* Pointer to ZoomMap structure */
int *result; /* Pointer to returned array */
int i; /* Loop count */
int iin; /* Mapping input index */
int mnin; /* No. of Mapping inputs */
int ok; /* Are input indices OK? */
/* Initialise */
result = NULL;
*map = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Get a pointer to the ZoomMap structure. */
this = (AstZoomMap *) this_map;
/* Allocate memory for the returned array and create a ZoomMap with the
required number of axes. */
result = astMalloc( sizeof( int )*(size_t) nin );
*map = (AstMapping *) astZoomMap( nin, astGetZoom( this ), "", status );
/* Set its Invert attribute to be like the supplied ZoomMap. */
astSetInvert( *map, astGetInvert( this ) );
/* Check pointers can be used safely. */
if( astOK ) {
/* Store the required output axis indices. At the same time check that each
axis is valid. */
mnin = astGetNin( this );
ok = 1;
for( i = 0; i < nin; i++ ) {
iin = in[ i ];
if( iin >= 0 && iin < mnin ) {
result[ i ] = iin;
} else {
ok = 0;
break;
}
}
/* If the "in" array contained any invalid values, free the returned
resources. */
if( !ok ) {
result = astFree( result );
*map = astAnnul( *map );
}
}
/* Free returned resources if an error has occurred. */
if( !astOK ) {
result = astFree( result );
*map = astAnnul( *map );
}
/* Return the list of output indices. */
return result;
}
static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){
/*
* Name:
* Rate
* Purpose:
* Calculate the rate of change of a Mapping output.
* Type:
* Private function.
* Synopsis:
* #include "zoommap.h"
* result = Rate( AstMapping *this, double *at, int ax1, int ax2, int *status )
* Class Membership:
* ZoomMap member function (overrides the astRate method inherited
* from the Mapping class ).
* Description:
* This function returns the rate of change of a specified output of
* the supplied Mapping with respect to a specified input, at a
* specified input position.
* Parameters:
* this
* Pointer to the Mapping to be applied.
* at
* The address of an array holding the axis values at the position
* at which the rate of change is to be evaluated. The number of
* elements in this array should equal the number of inputs to the
* Mapping.
* ax1
* The index of the Mapping output for which the rate of change is to
* be found (output numbering starts at 0 for the first output).
* ax2
* The index of the Mapping input which is to be varied in order to
* find the rate of change (input numbering starts at 0 for the first
* input).
* status
* Pointer to the inherited status variable.
* Returned Value:
* The rate of change of Mapping output "ax1" with respect to input
* "ax2", evaluated at "at", or AST__BAD if the value cannot be
* calculated.
*/
/* Local Variables: */
double result;
/* Check inherited status */
if( !astOK ) return AST__BAD;
/* Result is zero if the axes differ */
if( ax1 != ax2 ) {
result = 0.0;
/* Otherwise, get the zoom factor. */
} else {
result = astGetZoom( (AstZoomMap *) this );
/* If the ZoomMap has been inverted, return the reciprocal of the zoom
factor. */
if ( astGetInvert( this ) ) {
if( result != AST__BAD && result != 0.0 ) {
result = 1.0/result;
} else {
result = AST__BAD;
}
}
}
/* Return the result. */
return result;
}
static void SetAttrib( AstObject *this_object, const char *setting, int *status ) {
/*
* Name:
* SetAttrib
* Purpose:
* Set an attribute value for a ZoomMap.
* Type:
* Private function.
* Synopsis:
* #include "zoommap.h"
* void SetAttrib( AstObject *this, const char *setting )
* Class Membership:
* ZoomMap member function (over-rides the astSetAttrib protected
* method inherited from the Mapping class).
* Description:
* This function assigns an attribute value for a ZoomMap, 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 ZoomMap.
* setting
* Pointer to a null-terminated string specifying the new attribute
* value.
*/
/* Local Variables: */
AstZoomMap *this; /* Pointer to the ZoomMap structure */
double zoom; /* Zoom attribute value */
int len; /* Length of setting string */
int nc; /* Number of characters read by astSscanf */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the ZoomMap structure. */
this = (AstZoomMap *) 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. */
/* Zoom. */
/* ----- */
if ( nc = 0,
( 1 == astSscanf( setting, "zoom= %lf %n", &zoom, &nc ) )
&& ( nc >= len ) ) {
astSetZoom( this, zoom );
/* 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 int TestAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
* Name:
* TestAttrib
* Purpose:
* Test if a specified attribute value is set for a ZoomMap.
* Type:
* Private function.
* Synopsis:
* #include "zoommap.h"
* int TestAttrib( AstObject *this, const char *attrib, int *status )
* Class Membership:
* ZoomMap member function (over-rides the astTestAttrib protected
* method inherited from the Mapping class).
* Description:
* This function returns a boolean result (0 or 1) to indicate whether
* a value has been set for one of a ZoomMap's attributes.
* Parameters:
* this
* Pointer to the ZoomMap.
* 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: */
AstZoomMap *this; /* Pointer to the ZoomMap structure */
int result; /* Result value to return */
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain a pointer to the ZoomMap structure. */
this = (AstZoomMap *) this_object;
/* Check the attribute name and test the appropriate attribute. */
/* Zoom. */
/* ----- */
if ( !strcmp( attrib, "zoom" ) ) {
result = astTestZoom( 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 AstPointSet *Transform( AstMapping *this, AstPointSet *in,
int forward, AstPointSet *out, int *status ) {
/*
* Name:
* Transform
* Purpose:
* Apply a ZoomMap to transform a set of points.
* Type:
* Private function.
* Synopsis:
* #include "zoommap.h"
* AstPointSet *Transform( AstMapping *this, AstPointSet *in,
* int forward, AstPointSet *out, int *status )
* Class Membership:
* ZoomMap member function (over-rides the astTransform protected
* method inherited from the Mapping class).
* Description:
* This function takes a ZoomMap and a set of points encapsulated in a
* PointSet and transforms the points so as to apply the required zoom
* factor.
* Parameters:
* this
* Pointer to the ZoomMap.
* in
* Pointer to the PointSet holding the input coordinate data.
* forward
* A non-zero value indicates that the forward coordinate transformation
* should be applied, while a zero value requests the inverse
* transformation.
* out
* Pointer to a PointSet which will hold the transformed (output)
* coordinate values. A NULL value may also be given, in which case a
* new PointSet will be created by this function.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the output (possibly new) PointSet.
* 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.
* - The number of coordinate values per point in the input PointSet must
* match the number of coordinates for the ZoomMap being applied.
* - If an output PointSet is supplied, it must have space for sufficient
* number of points and coordinate values per point to accommodate the
* result. Any excess space will be ignored.
*/
/* Local Variables: */
AstPointSet *result; /* Pointer to output PointSet */
AstZoomMap *map; /* Pointer to ZoomMap to be applied */
double **ptr_in; /* Pointer to input coordinate data */
double **ptr_out; /* Pointer to output coordinate data */
double scale; /* Scale factor to implement zoom */
int coord; /* Loop counter for coordinates */
int ncoord_in; /* Number of coordinates per input point */
int npoint; /* Number of points */
int point; /* Loop counter for points */
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Obtain a pointer to the ZoomMap. */
map = (AstZoomMap *) this;
/* Apply the parent mapping using the stored pointer to the Transform member
function inherited from the parent Mapping class. This function validates
all arguments and generates an output PointSet if necessary, but does not
actually transform any coordinate values. */
result = (*parent_transform)( this, in, forward, out, status );
/* We will now extend the parent astTransform method by performing the
calculations needed to generate the output coordinate values. */
/* Determine the numbers of points and coordinates per point from the input
PointSet and obtain pointers for accessing the input and output coordinate
values. */
ncoord_in = astGetNcoord( in );
npoint = astGetNpoint( in );
ptr_in = astGetPoints( in );
ptr_out = astGetPoints( result );
/* Determine whether to apply the forward or inverse mapping, according to the
direction specified and whether the mapping has been inverted. */
if ( astGetInvert( map ) ) forward = !forward;
/* Perform coordinate arithmetic. */
/* ------------------------------ */
/* Generate the coordinate scale factor, according to the direction
of mapping required. */
scale = astGetZoom( map );
if ( !forward && astOK ) scale = 1.0 / scale;
/* Loop to apply the scale factor to all the coordinate values, checking for
(and propagating) bad values in the process. */
if ( astOK ) {
for ( coord = 0; coord < ncoord_in; coord++ ) {
for ( point = 0; point < npoint; point++ ) {
if ( ptr_in[ coord ][ point ] == AST__BAD ) {
ptr_out[ coord ][ point ] = AST__BAD;
} else {
ptr_out[ coord ][ point ] = ptr_in[ coord ][ point ] * scale;
}
}
}
}
/* Return a pointer to the output PointSet. */
return result;
}
/* Functions which access class attributes. */
/* ---------------------------------------- */
/* Implement member functions to access the attributes associated with
this class using the macros defined for this purpose in the
"object.h" file. For a description of each attribute, see the class
interface (in the associated .h file). */
/*
*att++
* Name:
* Zoom
* Purpose:
* ZoomMap scale factor.
* Type:
* Public attribute.
* Synopsis:
* Double precision.
* Description:
* This attribute holds the ZoomMap scale factor, by which
* coordinate values are multiplied (by the forward transformation)
* or divided (by the inverse transformation). This factor is set
* when a ZoomMap is created, but may later be modified. The
* default value is unity.
*
c Note that if a ZoomMap is inverted (e.g. by using astInvert),
f Note that if a ZoomMap is inverted (e.g. by using AST_INVERT),
* then the reciprocal of this zoom factor will, in effect, be
* used.
* Applicability:
* ZoomMap
* All ZoomMaps have this attribute.
* Notes:
* - The Zoom attribute may not be set to zero.
*att--
*/
/* This ia a double value with a value of 0.0 when undefined but
yielding a default of 1.0. Setting it explicitly to 0.0 is not
permitted except via astClearZoom. */
astMAKE_CLEAR(ZoomMap,Zoom,zoom,0.0)
astMAKE_GET(ZoomMap,Zoom,double,1.0,( ( this->zoom == 0.0 ) ?
1.0 : this->zoom ))
/* Check for an attempt to set a value of zero and report an error if
necessary (leaving the Zoom value unchanged). */
astMAKE_SET(ZoomMap,Zoom,double,zoom,(
( value != 0.0 ) ?
value :
( astError( AST__ZOOMI,
"astSetZoom(%s): A zoom factor of zero is not allowed.", status,
astGetClass( this ) ),
this->zoom ) ))
astMAKE_TEST(ZoomMap,Zoom,( this->zoom != 0.0 ))
/* Copy constructor. */
/* ----------------- */
/* No copy constructor is needed, as a byte-by-byte copy suffices. */
/* Destructor. */
/* ----------- */
/* No destructor is needed as no memory, etc. needs freeing. */
/* Dump function. */
/* -------------- */
static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
/*
* Name:
* Dump
* Purpose:
* Dump function for ZoomMap 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 ZoomMap class to an output Channel.
* Parameters:
* this
* Pointer to the ZoomMap 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: */
AstZoomMap *this; /* Pointer to the ZoomMap structure */
double dval; /* Double value */
int set; /* Attribute value set? */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the ZoomMap structure. */
this = (AstZoomMap *) this_object;
/* Write out values representing the instance variables for the
ZoomMap class. Accompany these with appropriate comment strings,
possibly depending on the values being written.*/
/* In the case of attributes, we first use the appropriate (private)
Test... member function to see if they are set. If so, we then use
the (private) Get... function to obtain the value to be written
out.
For attributes which are not set, we use the astGet... method to
obtain the value instead. This will supply a default value
(possibly provided by a derived class which over-rides this method)
which is more useful to a human reader as it corresponds to the
actual default attribute value. Since "set" will be zero, these
values are for information only and will not be read back. */
/* Zoom. */
/* ----- */
set = TestZoom( this, status );
dval = set ? GetZoom( this, status ) : astGetZoom( this );
astWriteDouble( channel, "Zoom", set, 1, dval, "Zoom factor" );
}
/* Standard class functions. */
/* ========================= */
/* Implement the astIsAZoomMap and astCheckZoomMap functions using the macros
defined for this purpose in the "object.h" header file. */
astMAKE_ISA(ZoomMap,Mapping)
astMAKE_CHECK(ZoomMap)
AstZoomMap *astZoomMap_( int ncoord, double zoom, const char *options, int *status, ...) {
/*
*++
* Name:
c astZoomMap
f AST_ZOOMMAP
* Purpose:
* Create a ZoomMap.
* Type:
* Public function.
* Synopsis:
c #include "zoommap.h"
c AstZoomMap *astZoomMap( int ncoord, double zoom,
c const char *options, ... )
f RESULT = AST_ZOOMMAP( NCOORD, ZOOM, OPTIONS, STATUS )
* Class Membership:
* ZoomMap constructor.
* Description:
* This function creates a new ZoomMap and optionally initialises its
* attributes.
*
* A ZoomMap is a Mapping which "zooms" a set of points about the
* origin by multiplying all coordinate values by the same scale
* factor (the inverse transformation is performed by dividing by
* this scale factor).
* Parameters:
c ncoord
f NCOORD = INTEGER (Given)
* The number of coordinate values for each point to be
* transformed (i.e. the number of dimensions of the space in
* which the points will reside). The same number is applicable
* to both input and output points.
c zoom
f ZOOM = DOUBLE PRECISION (Given)
* Initial scale factor by which coordinate values should be
* multiplied (by the forward transformation) or divided (by the
* inverse transformation). This factor may subsequently be
* changed via the ZoomMap's Zoom attribute. It may be positive
* or negative, but should not be zero.
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 ZoomMap. 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 ZoomMap. 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 astZoomMap()
f AST_ZOOMMAP = INTEGER
* A pointer to the new ZoomMap.
* Notes:
* - A null Object pointer (AST__NULL) will be returned if this
c function is invoked with the AST error status set, or if it
f function is invoked with STATUS set to an error value, or if it
* should fail for any reason.
* Status Handling:
* The protected interface to this function includes an extra
* parameter at the end of the parameter list descirbed above. This
* parameter is a pointer to the integer inherited status
* variable: "int *status".
*--
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstZoomMap *new; /* Pointer to new ZoomMap */
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 ZoomMap, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitZoomMap( NULL, sizeof( AstZoomMap ), !class_init, &class_vtab,
"ZoomMap", ncoord, zoom );
/* 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 ZoomMap'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 ZoomMap. */
return new;
}
AstZoomMap *astZoomMapId_( int ncoord, double zoom,
const char *options, ... ) {
/*
* Name:
* astZoomMapId_
* Purpose:
* Create a ZoomMap.
* Type:
* Private function.
* Synopsis:
* #include "zoommap.h"
* AstZoomMap *astZoomMapId_( int ncoord, double zoom,
* const char *options, ... )
* Class Membership:
* ZoomMap constructor.
* Description:
* This function implements the external (public) interface to the
* astZoomMap constructor function. It returns an ID value (instead
* of a true C pointer) to external users, and must be provided
* because astZoomMap_ 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 astZoomMap_ 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 astZoomMap_.
* Returned Value:
* The ID value associated with the new ZoomMap.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstZoomMap *new; /* Pointer to new ZoomMap */
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 ZoomMap, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitZoomMap( NULL, sizeof( AstZoomMap ), !class_init, &class_vtab,
"ZoomMap", ncoord, zoom );
/* 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 ZoomMap'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 ZoomMap. */
return astMakeId( new );
}
AstZoomMap *astInitZoomMap_( void *mem, size_t size, int init,
AstZoomMapVtab *vtab, const char *name,
int ncoord, double zoom, int *status ) {
/*
*+
* Name:
* astInitZoomMap
* Purpose:
* Initialise a ZoomMap.
* Type:
* Protected function.
* Synopsis:
* #include "zoommap.h"
* AstZoomMap *astInitZoomMap( void *mem, size_t size, int init,
* AstZoomMapVtab *vtab, const char *name,
* int ncoord, double zoom )
* Class Membership:
* ZoomMap initialiser.
* Description:
* This function is provided for use by class implementations to initialise
* a new ZoomMap object. It allocates memory (if necessary) to accommodate
* the ZoomMap plus any additional data associated with the derived class.
* It then initialises a ZoomMap structure at the start of this memory. If
* the "init" flag is set, it also initialises the contents of a virtual
* function table for a ZoomMap at the start of the memory passed via the
* "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory in which the ZoomMap is to be initialised.
* This must be of sufficient size to accommodate the ZoomMap data
* (sizeof(ZoomMap)) 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 ZoomMap (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 ZoomMap
* structure, so a valid value must be supplied even if not required for
* allocating memory.
* init
* A logical flag indicating if the ZoomMap'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 ZoomMap.
* 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).
* ncoord
* The number of coordinate values per point.
* zoom
* The scale factor by which coordinate values are multiplied (by the
* forward mapping) or divided (by the inverse mapping). This factor may
* not be zero.
* Returned Value:
* A pointer to the new ZoomMap.
* 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: */
AstZoomMap *new; /* Pointer to new ZoomMap */
/* Check the global status. */
if ( !astOK ) return NULL;
/* If necessary, initialise the virtual function table. */
if ( init ) astInitZoomMapVtab( vtab, name );
/* Initialise. */
new = NULL;
/* Check the initialisation value(s) for validity, reporting an error if
necessary. Prefix the error report with the name of the function and the
class of object being processed. */
if ( zoom == 0.0 ) {
astError( AST__ZOOMI, "astInitZoomMap(%s): A zoom factor of zero is not "
"allowed.", status, name );
} else {
/* Initialise a Mapping structure (the parent class) as the first component
within the ZoomMap structure, allocating memory if necessary. Specify that
the Mapping should be defined in both the forward and inverse directions. */
new = (AstZoomMap *) astInitMapping( mem, size, 0,
(AstMappingVtab *) vtab, name,
ncoord, ncoord, 1, 1 );
if ( astOK ) {
/* Initialise the ZoomMap data. */
/* ---------------------------- */
/* Store the zoom factor. */
new->zoom = zoom;
/* If an error occurred, clean up by deleting the new ZoomMap. */
if ( !astOK ) new = astDelete( new );
}
}
/* Return a pointer to the new ZoomMap. */
return new;
}
AstZoomMap *astLoadZoomMap_( void *mem, size_t size,
AstZoomMapVtab *vtab, const char *name,
AstChannel *channel, int *status ) {
/*
*+
* Name:
* astLoadZoomMap
* Purpose:
* Load a ZoomMap.
* Type:
* Protected function.
* Synopsis:
* #include "zoommap.h"
* AstZoomMap *astLoadZoomMap( void *mem, size_t size,
* AstZoomMapVtab *vtab, const char *name,
* AstChannel *channel )
* Class Membership:
* ZoomMap loader.
* Description:
* This function is provided to load a new ZoomMap 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
* ZoomMap 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 a ZoomMap at the start of the memory
* passed via the "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory into which the ZoomMap is to be
* loaded. This must be of sufficient size to accommodate the
* ZoomMap data (sizeof(ZoomMap)) 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 ZoomMap (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 ZoomMap 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(AstZoomMap) is used instead.
* vtab
* Pointer to the start of the virtual function table to be
* associated with the new ZoomMap. If this is NULL, a pointer
* to the (static) virtual function table for the ZoomMap 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 "ZoomMap" is used instead.
* Returned Value:
* A pointer to the new ZoomMap.
* 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 */
AstZoomMap *new; /* Pointer to the new ZoomMap */
/* 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 ZoomMap. In this case the
ZoomMap belongs to this class, so supply appropriate values to be
passed to the parent class loader (and its parent, etc.). */
if ( !vtab ) {
size = sizeof( AstZoomMap );
vtab = &class_vtab;
name = "ZoomMap";
/* If required, initialise the virtual function table for this class. */
if ( !class_init ) {
astInitZoomMapVtab( 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 ZoomMap. */
new = astLoadMapping( mem, size, (AstMappingVtab *) 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, "ZoomMap" );
/* Now read each individual data item from this list and use it to
initialise the appropriate instance variable(s) for this class. */
/* In the case of attributes, we first read the "raw" input value,
supplying the "unset" value as the default. If a "set" value is
obtained, we then use the appropriate (private) Set... member
function to validate and set the value properly. */
/* Zoom. */
/* ----- */
new->zoom = astReadDouble( channel, "zoom", 0.0 );
if ( TestZoom( new, status ) ) SetZoom( new, new->zoom, status );
/* If an error occurred, clean up by deleting the new ZoomMap. */
if ( !astOK ) new = astDelete( new );
}
/* Return the new ZoomMap 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. */
ast-8.0.7/ast_err.h 0000664 0001750 0001750 00000037177 12610415024 011062 0000000 0000000 /*
* C error define file for facility AST (1521)
* Generated by the MESSGEN utility
*/
#ifndef AST_ERROR_DEFINED
#define AST_ERROR_DEFINED
/* attribute getting error */
enum { AST__ATGER = 233933154 }; /* messid=300 */
/* attribute setting error */
enum { AST__ATSER = 233933162 }; /* messid=301 */
/* attribute value invalid */
enum { AST__ATTIN = 233933170 }; /* messid=302 */
/* axis index invalid */
enum { AST__AXIIN = 233933178 }; /* messid=303 */
/* bad attribute name */
enum { AST__BADAT = 233933186 }; /* messid=304 */
/* zero-sized box given */
enum { AST__BADBX = 233933194 }; /* messid=305 */
/* bad input data */
enum { AST__BADIN = 233933202 }; /* messid=306 */
/* bad number of input coordinates */
enum { AST__BADNI = 233933210 }; /* messid=307 */
/* bad number of output coordinates */
enum { AST__BADNO = 233933218 }; /* messid=308 */
/* PolyMap contains illegal power value */
enum { AST__BADPW = 233933226 }; /* messid=309 */
/* ShiftMap contains no shift information */
enum { AST__BADSM = 233933234 }; /* messid=310 */
/* WinMap contains no bounds information */
enum { AST__BADWM = 233933242 }; /* messid=311 */
/* bad break index */
enum { AST__BDBRK = 233933250 }; /* messid=312 */
/* bad field specifier */
enum { AST__BDFMT = 233933258 }; /* messid=313 */
/* invalid FITS keyword value found */
enum { AST__BDFTS = 233933266 }; /* messid=314 */
/* inappropriate Object supplied */
enum { AST__BDOBJ = 233933274 }; /* messid=315 */
/* wrong number of clipping axes */
enum { AST__CLPAX = 233933282 }; /* messid=316 */
/* range of coordinates invalid */
enum { AST__CORNG = 233933290 }; /* messid=317 */
/* too many breaks in a curve */
enum { AST__CVBRK = 233933298 }; /* messid=318 */
/* array dimensions invalid */
enum { AST__DIMIN = 233933306 }; /* messid=319 */
/* date/time error */
enum { AST__DTERR = 233933314 }; /* messid=320 */
/* invalid use of astEnd */
enum { AST__ENDIN = 233933322 }; /* messid=321 */
/* end of input Channel encountered */
enum { AST__EOCHN = 233933330 }; /* messid=322 */
/* attempt to export Object pointer from level zero */
enum { AST__EXPIN = 233933338 }; /* messid=323 */
/* corrupted FitsChan supplied */
enum { AST__FCRPT = 233933346 }; /* messid=324 */
/* error while formatting coordinate value */
enum { AST__FMTER = 233933354 }; /* messid=325 */
/* Frame index invalid */
enum { AST__FRMIN = 233933362 }; /* messid=326 */
/* FrameSet invalid */
enum { AST__FRSIN = 233933370 }; /* messid=327 */
/* cannot convert FITS data value type */
enum { AST__FTCNV = 233933378 }; /* messid=328 */
/* low level graphics error */
enum { AST__GRFER = 233933386 }; /* messid=329 */
/* invalid Handle */
enum { AST__INHAN = 233933394 }; /* messid=330 */
/* incompatible numbers of coordinates */
enum { AST__INNCO = 233933402 }; /* messid=331 */
/* internal programming error */
enum { AST__INTER = 233933410 }; /* messid=332 */
/* incompatible transformation directions */
enum { AST__INTRD = 233933418 }; /* messid=333 */
/* circular dependency between KeyMaps */
enum { AST__KYCIR = 233933426 }; /* messid=334 */
/* class loader error */
enum { AST__LDERR = 233933434 }; /* messid=335 */
/* invalid lookup table increment */
enum { AST__LUTII = 233933442 }; /* messid=336 */
/* invalid number of lookup table elements */
enum { AST__LUTIN = 233933450 }; /* messid=337 */
/* requested memory size invalid */
enum { AST__MEMIN = 233933458 }; /* messid=338 */
/* not a 2d or 3d MatrixMap */
enum { AST__MTR23 = 233933466 }; /* messid=339 */
/* null rotation axis supplied */
enum { AST__MTRAX = 233933474 }; /* messid=340 */
/* bad matrix shapes for multiplication */
enum { AST__MTRML = 233933482 }; /* messid=341 */
/* null matrix supplied */
enum { AST__MTRMT = 233933490 }; /* messid=342 */
/* number of axes invalid */
enum { AST__NAXIN = 233933498 }; /* messid=343 */
/* number of characters invalid */
enum { AST__NCHIN = 233933506 }; /* messid=344 */
/* number of coordinates invalid */
enum { AST__NCOIN = 233933514 }; /* messid=345 */
/* number of coordinates per point invalid */
enum { AST__NCPIN = 233933522 }; /* messid=346 */
/* number of array elements invalid */
enum { AST__NELIN = 233933530 }; /* messid=347 */
/* number of output coordinates too small */
enum { AST__NOCTS = 233933538 }; /* messid=348 */
/* transformation not defined */
enum { AST__NODEF = 233933546 }; /* messid=349 */
/* required FITS keywords missing */
enum { AST__NOFTS = 233933554 }; /* messid=350 */
/* unable to allocate memory */
enum { AST__NOMEM = 233933562 }; /* messid=351 */
/* number of output points too small */
enum { AST__NOPTS = 233933570 }; /* messid=352 */
/* attribute is read-only */
enum { AST__NOWRT = 233933578 }; /* messid=353 */
/* number of points invalid */
enum { AST__NPTIN = 233933586 }; /* messid=354 */
/* Object invalid */
enum { AST__OBJIN = 233933594 }; /* messid=355 */
/* invalid Plot option */
enum { AST__OPT = 233933602 }; /* messid=356 */
/* points data structure invalid */
enum { AST__PDSIN = 233933610 }; /* messid=357 */
/* no numerical labels can be produced */
enum { AST__PLFMT = 233933618 }; /* messid=358 */
/* permutation invalid */
enum { AST__PRMIN = 233933626 }; /* messid=359 */
/* pointer invalid */
enum { AST__PTRIN = 233933634 }; /* messid=360 */
/* range of points invalid */
enum { AST__PTRNG = 233933642 }; /* messid=361 */
/* read error */
enum { AST__RDERR = 233933650 }; /* messid=362 */
/* invalid or corrupted Region structure supplied */
enum { AST__REGIN = 233933658 }; /* messid=363 */
/* invalid attempt to remove last Frame */
enum { AST__REMIN = 233933666 }; /* messid=364 */
/* sky coordinate system invalid */
enum { AST__SCSIN = 233933674 }; /* messid=365 */
/* axis selection invalid */
enum { AST__SELIN = 233933682 }; /* messid=366 */
/* bad SLALIB transformation type */
enum { AST__SLAIN = 233933690 }; /* messid=367 */
/* coordinate transformation not defined */
enum { AST__TRNND = 233933698 }; /* messid=368 */
/* unmatched quotes */
enum { AST__UNMQT = 233933706 }; /* messid=369 */
/* valid area too small */
enum { AST__VSMAL = 233933714 }; /* messid=370 */
/* non-existent longitude or latitude axis */
enum { AST__WCSAX = 233933722 }; /* messid=371 */
/* too few mapping coordinates */
enum { AST__WCSNC = 233933730 }; /* messid=372 */
/* invalid projection parameters */
enum { AST__WCSPA = 233933738 }; /* messid=373 */
/* unknown projection type */
enum { AST__WCSTY = 233933746 }; /* messid=374 */
/* too many Objects in use at once */
enum { AST__XSOBJ = 233933754 }; /* messid=375 */
/* zoom factor invalid */
enum { AST__ZOOMI = 233933762 }; /* messid=376 */
/* bad coordinate index */
enum { AST__BADCI = 233933770 }; /* messid=377 */
/* FrameSet integrity lost */
enum { AST__ILOST = 233933778 }; /* messid=378 */
/* error in IntraMap transformation function */
enum { AST__ITFER = 233933786 }; /* messid=379 */
/* IntraMap transformation function name invalid */
enum { AST__ITFNI = 233933794 }; /* messid=380 */
/* Mapping bounding box not found */
enum { AST__MBBNF = 233933802 }; /* messid=381 */
/* multiple registration of IntraMap transformation function */
enum { AST__MRITF = 233933810 }; /* messid=382 */
/* Object class unknown */
enum { AST__OCLUK = 233933818 }; /* messid=383 */
/* error while unformatting a coordinate value */
enum { AST__UNFER = 233933826 }; /* messid=384 */
/* unregistered IntraMap transformation function */
enum { AST__URITF = 233933834 }; /* messid=385 */
/* grid bounds invalid */
enum { AST__GBDIN = 233933842 }; /* messid=386 */
/* number of grid dimensions invalid */
enum { AST__NGDIN = 233933850 }; /* messid=387 */
/* positional accuracy tolerance invalid */
enum { AST__PATIN = 233933858 }; /* messid=388 */
/* sub-pixel interpolation scheme invalid */
enum { AST__SISIN = 233933866 }; /* messid=389 */
/* scale size in pixels invalid */
enum { AST__SSPIN = 233933874 }; /* messid=390 */
/* error in user-supplied sub-pixel interpolation function */
enum { AST__UINER = 233933882 }; /* messid=391 */
/* error in user-supplied 1-d sub-pixel interpolation kernel */
enum { AST__UK1ER = 233933890 }; /* messid=392 */
/* invalid comma in expression */
enum { AST__COMIN = 233933898 }; /* messid=393 */
/* invalid constant in expression */
enum { AST__CONIN = 233933906 }; /* messid=394 */
/* duplicate variable name */
enum { AST__DUVAR = 233933914 }; /* messid=395 */
/* invalid number of transformation functions */
enum { AST__INNTF = 233933922 }; /* messid=396 */
/* missing or invalid operand in expression */
enum { AST__MIOPA = 233933930 }; /* messid=397 */
/* missing or invalid operator in expression */
enum { AST__MIOPR = 233933938 }; /* messid=398 */
/* missing variable name */
enum { AST__MISVN = 233933946 }; /* messid=399 */
/* missing left parenthesis in expression */
enum { AST__MLPAR = 233933954 }; /* messid=400 */
/* missing right parenthesis in expression */
enum { AST__MRPAR = 233933962 }; /* messid=401 */
/* missing right hand side in function */
enum { AST__NORHS = 233933970 }; /* messid=402 */
/* undefined variable or function in expression */
enum { AST__UDVOF = 233933978 }; /* messid=403 */
/* variable name invalid */
enum { AST__VARIN = 233933986 }; /* messid=404 */
/* wrong number of function arguments in expression */
enum { AST__WRNFA = 233933994 }; /* messid=405 */
/* invalid units specification */
enum { AST__BADUN = 233934002 }; /* messid=406 */
/* no rest frequency is defined */
enum { AST__NORSF = 233934010 }; /* messid=407 */
/* no standard of rest is defined */
enum { AST__NOSOR = 233934018 }; /* messid=408 */
/* invalid SpecMap */
enum { AST__SPCIN = 233934026 }; /* messid=409 */
/* invalid XML name or prefix */
enum { AST__XMLNM = 233934034 }; /* messid=410 */
/* invalid XML comment text */
enum { AST__XMLCM = 233934042 }; /* messid=411 */
/* invalid XML processing instruction target text */
enum { AST__XMLPT = 233934050 }; /* messid=412 */
/* invalid XML content item index */
enum { AST__XMLIT = 233934058 }; /* messid=413 */
/* supplied XML document is not well formed */
enum { AST__XMLWF = 233934066 }; /* messid=414 */
/* Range of log axis scale includes zero */
enum { AST__ZERAX = 233934074 }; /* messid=415 */
/* Invalid parameters for offset sky coordinate system */
enum { AST__BADOC = 233934082 }; /* messid=416 */
/* error getting a named value from a KeyMap */
enum { AST__MPGER = 233934090 }; /* messid=417 */
/* invalid integer index supplied for a KeyMap entry */
enum { AST__MPIND = 233934098 }; /* messid=418 */
/* region cannot be re-centred */
enum { AST__REGCN = 233934106 }; /* messid=419 */
/* attribute has no usable value */
enum { AST__NOVAL = 233934114 }; /* messid=420 */
/* incompatible time scales */
enum { AST__INCTS = 233934122 }; /* messid=421 */
/* invalid TimeMap */
enum { AST__TIMIN = 233934130 }; /* messid=422 */
/* cannot use supplied AstroCoords info */
enum { AST__STCKEY = 233934138 }; /* messid=423 */
/* invalid AstroCoords index */
enum { AST__STCIND = 233934146 }; /* messid=424 */
/* cannot conserve flux whilst resampling an array of data */
enum { AST__CNFLX = 233934154 }; /* messid=425 */
/* Unknown AST tuning parameter name supplied */
enum { AST__TUNAM = 233934162 }; /* messid=426 */
/* Bad value supplied for a public function parameter */
enum { AST__BDPAR = 233934170 }; /* messid=427 */
/* Supplied FrameSet does not contain any independent axes */
enum { AST__3DFSET = 233934178 }; /* messid=428 */
/* Attempt to delete original Plot3D base Frame */
enum { AST__PXFRRM = 233934186 }; /* messid=429 */
/* Illegal syntax for string substitution template */
enum { AST__BADSUB = 233934194 }; /* messid=430 */
/* Incompatible flags for re-sampling or re-binning */
enum { AST__BADFLG = 233934202 }; /* messid=431 */
/* Error locking or unlocking an AST Object */
enum { AST__LCKERR = 233934210 }; /* messid=432 */
/* FITS keyword had undefined value */
enum { AST__FUNDEF = 233934218 }; /* messid=433 */
/* invalid integer index supplied for a KeyMap vector element */
enum { AST__MPVIN = 233934226 }; /* messid=434 */
/* operation specifier invalid */
enum { AST__OPRIN = 233934234 }; /* messid=435 */
/* no inside point found */
enum { AST__NONIN = 233934242 }; /* messid=436 */
/* requested key not found in KeyMap */
enum { AST__MPKER = 233934250 }; /* messid=437 */
/* error putting a named value into a KeyMap */
enum { AST__MPPER = 233934258 }; /* messid=438 */
/* Attempt made to add an entry to a locked KeyMap */
enum { AST__BADKEY = 233934266 }; /* messid=439 */
/* Bad data type */
enum { AST__BADTYP = 233934274 }; /* messid=440 */
/* Column already exists with different properties */
enum { AST__OLDCOL = 233934282 }; /* messid=441 */
/* Bad null value for a FITS table column */
enum { AST__BADNULL = 233934290 }; /* messid=442 */
/* Key string is too long */
enum { AST__BIGKEY = 233934298 }; /* messid=443 */
/* No such column exists in the table */
enum { AST__BADCOL = 233934306 }; /* messid=444 */
/* Table is too large */
enum { AST__BIGTAB = 233934314 }; /* messid=445 */
/* Invalid array size */
enum { AST__BADSIZ = 233934322 }; /* messid=446 */
/* Error reading WCS from FITS binary table */
enum { AST__BADTAB = 233934330 }; /* messid=447 */
/* Cannot access FITS binary table */
enum { AST__NOTAB = 233934338 }; /* messid=448 */
/* Error in levmar Levenberg-Marquardt code */
enum { AST__LEVMAR = 233934346 }; /* messid=449 */
/* Fit failed */
enum { AST__NOFIT = 233934354 }; /* messid=450 */
/* A transformation generated one or more NaN values */
enum { AST__ISNAN = 233934362 }; /* messid=451 */
/* write error */
enum { AST__WRERR = 233934370 }; /* messid=452 */
/* Bad variant Mapping name */
enum { AST__BDVNM = 233934378 }; /* messid=453 */
/* Attempt to add a variant Mapping to a mirror Frame */
enum { AST__MIRRO = 233934386 }; /* messid=454 */
/* Error in cminpack Levenberg-Marquardt code */
enum { AST__MNPCK = 233934394 }; /* messid=455 */
/* Supplied array has too many pixels */
enum { AST__EXSPIX = 233934402 }; /* messid=456 */
/* No mapping found between coordinate systems */
enum { AST__NOCNV = 233934410 }; /* messid=457 */
#endif /* AST_ERROR_DEFINED */
ast-8.0.7/interval.c 0000664 0001750 0001750 00000516767 12610415012 011246 0000000 0000000 /*
*class++
* Name:
* Interval
* Purpose:
* A region representing an interval on one or more axes of a Frame.
* Constructor Function:
c astInterval
f AST_INTERVAL
* Description:
* The Interval class implements a Region which represents upper
* and/or lower limits on one or more axes of a Frame. For a point to
* be within the region represented by the Interval, the point must
* satisfy all the restrictions placed on all the axes. The point is
* outside the region if it fails to satisfy any one of the restrictions.
* Each axis may have either an upper limit, a lower limit, both or
* neither. If both limits are supplied but are in reverse order (so
* that the lower limit is greater than the upper limit), then the
* interval is an excluded interval, rather than an included interval.
*
* Note, The Interval class makes no allowances for cyclic nature of
* some coordinate systems (such as SkyFrame coordinates). A Box
* should usually be used in these cases since this requires the user
* to think about suitable upper and lower limits,
* Inheritance:
* The Interval class inherits from the Region class.
* Attributes:
* The Interval class does not define any new attributes beyond
* those which are applicable to all Regions.
* Functions:
c The Interval class does not define any new functions beyond those
f The Interval class does not define any new routines beyond those
* which are applicable to all Regions.
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* 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
* .
* Authors:
* DSB: David S. Berry (Starlink)
* History:
* 29-OCT-2004 (DSB):
* Original version.
* 19-APR-2006 (DSB):
* Negate the cached equivalent Box if the Interval has been negated.
* 28-MAY-2007 (DSB):
* Re-implemented BndBaseMesh.
* 20-JAN-2009 (DSB):
* Over-ride astRegBasePick.
* 26-JAN-2009 (DSB):
* Over-ride astMapMerge.
* 4-NOV42-2013 (DSB):
* - Change RegCentre so that it does not report an error for an unbounded
* Interval if the centre is merely being inquired rather than set. This is
* the documented behaviour of the astRegCentre method.
* - Modify RegPins so that it can handle uncertainty regions that straddle
* a discontinuity. Previously, such uncertainty Regions could have a huge
* bounding box resulting in matching region being far too big.
*class--
*/
/* Module Macros. */
/* ============== */
/* Set the name of the class we are implementing. This indicates to
the header files that define class interfaces that they should make
"protected" symbols available. */
#define astCLASS Interval
/* Macros which return the maximum and minimum of two values. */
#define MAX(aa,bb) ((aa)>(bb)?(aa):(bb))
#define MIN(aa,bb) ((aa)<(bb)?(aa):(bb))
/* Macro to check for equality of floating point values. We cannot
compare bad values directory because of the danger of floating point
exceptions, so bad values are dealt with explicitly. */
#define EQUAL(aa,bb) (((aa)==(bb))?1:(((aa)==AST__BAD||(bb)==AST__BAD)?0:(fabs((aa)-(bb))<=1.0E9*MAX((fabs(aa)+fabs(bb))*DBL_EPSILON,DBL_MIN))))
/* 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 "pointset.h" /* Sets of points/coordinates */
#include "region.h" /* Abstract coordinate regions (parent class) */
#include "channel.h" /* I/O channels */
#include "box.h" /* Box Regions */
#include "nullregion.h" /* Null Regions */
#include "wcsmap.h" /* Definitons of AST__DPI etc */
#include "interval.h" /* Interface definition for this class */
#include "ellipse.h" /* Interface definition for ellipse class */
#include "mapping.h" /* Position mappings */
#include "unitmap.h" /* Unit Mappings */
#include "cmpmap.h" /* Compound Mappings */
#include "cmpframe.h" /* Compound Frames */
#include "prism.h" /* Prism regions */
#include "pointlist.h" /* Lists of points in a Frame */
/* Error code definitions. */
/* ----------------------- */
#include "ast_err.h" /* AST error codes */
/* C header files. */
/* --------------- */
#include
#include
#include
#include
#include
#include
/* 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 AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
static AstMapping *(* parent_simplify)( AstMapping *, int * );
static int (* parent_overlap)( AstRegion *, AstRegion *, int * );
static void (* parent_setregfs)( AstRegion *, AstFrame *, int * );
static void (* parent_setunc)( AstRegion *, AstRegion *, int * );
static void (* parent_resetcache)( AstRegion *, int * );
#if defined(THREAD_SAFE)
static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * );
#endif
#ifdef THREAD_SAFE
/* Define how to initialise thread-specific globals. */
#define GLOBAL_inits \
globals->Class_Init = 0;
/* Create the function that initialises global data for this module. */
astMAKE_INITGLOBALS(Interval)
/* Define macros for accessing each item of thread specific global data. */
#define class_init astGLOBAL(Interval,Class_Init)
#define class_vtab astGLOBAL(Interval,Class_Vtab)
#include
#else
/* Define the class virtual function table and its initialisation flag
as static variables. */
static AstIntervalVtab 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. */
AstInterval *astIntervalId_( void *, const double[], const double[], void *, const char *, ... );
/* Prototypes for Private Member Functions. */
/* ======================================== */
static AstBox *Cache( AstInterval *, int * );
static AstMapping *Simplify( AstMapping *, int * );
static AstPointSet *BndBaseMesh( AstRegion *, double *, double *, int * );
static AstPointSet *RegBaseMesh( AstRegion *, int * );
static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
static AstRegion *RegBasePick( AstRegion *this, int, const int *, int * );
static AstRegion *GetDefUnc( AstRegion *, int * );
static AstRegion *MergeInterval( AstInterval *, AstRegion *, int, int * );
static double *RegCentre( AstRegion *this, double *, double **, int, int, int * );
static int *OneToOne( AstMapping *, int * );
static int GetBounded( AstRegion *, int * );
static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * );
static int Overlap( AstRegion *, AstRegion *, int * );
static int RegPins( AstRegion *, AstPointSet *, AstRegion *, int **, int * );
static int RegTrace( AstRegion *, int, double *, double **, int * );
static void Copy( const AstObject *, AstObject *, int * );
static void Delete( AstObject *, int * );
static void Dump( AstObject *, AstChannel *, int * );
static void IntervalPoints( AstInterval *, double *, double *, int *);
static void RegBaseBox( AstRegion *this, double *, double *, int * );
static void ResetCache( AstRegion *this, int * );
static void SetRegFS( AstRegion *, AstFrame *, int * );
static void SetUnc( AstRegion *, AstRegion *, int * );
#if defined(THREAD_SAFE)
static int ManageLock( AstObject *, int, int, AstObject **, int * );
#endif
/* Member functions. */
/* ================= */
static AstPointSet *BndBaseMesh( AstRegion *this, double *lbnd, double *ubnd, int *status ){
/*
* Name:
* BndBaseMesh
* Purpose:
* Return a PointSet containing points spread around part of the boundary
* of a Region.
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* AstPointSet *BndBaseMesh( AstRegion *this, double *lbnd, double *ubnd, int *status )
* Class Membership:
* Interval method (over-rides the astBndBaseMesh method inherited from
* the Region class).
* Description:
* This function returns a PointSet containing a set of points on the
* boundary of the intersection between the supplied Region and the
* supplied box. The points refer to the base Frame of the
* encapsulated FrameSet. If the boundary of the supplied Region does
* not intersect the supplied box, then a PointSet containing a single
* bad point is returned.
* Parameters:
* this
* Pointer to the Region.
* lbnd
* Pointer to an array holding the lower limits of the axis values
* within the required box.
* ubnd
* Pointer to an array holding the upper limits of the axis values
* within the required box.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the PointSet. The axis values in this PointSet will have
* associated accuracies derived from the uncertainties which were
* supplied when the Region was created.
*
* If the Region does not intersect the supplied box, the returned
* PointSet will contain a single point with a value of AST__BAD on
* every axis.
* Notes:
* - A NULL pointer is returned if an error has already occurred, or if
* this function should fail for any reason.
*/
/* Local Variables: */
AstBox *box;
AstFrame *bfrm;
AstInterval *this_interval;
AstMapping *map;
AstPointSet *result;
double *lbndb;
double *ubndb;
double **ptr;
int closed;
int i;
int nbase;
/* Initialise */
result = NULL;
/* Check the local error status. */
if ( !astOK ) return result;
/* Store a pointer to the interval. */
this_interval = (AstInterval *) this;
/* If the Interval is effectively a Box, invoke the astBndBaseMesh
function on the equivalent Box. A pointer to the equivalent Box will
be stored in the Interval structure. */
box = Cache( (AstInterval *) this, status );
if( box ) {
result = astBndBaseMesh( box, lbnd, ubnd );
/* If the Interval is not equivalent to a Box (i.e. if one or more bounds
are missing)... */
} else {
/* Find the base frame box that just encloses the supplied current Frame
box. */
map = astGetMapping( this->frameset, AST__CURRENT, AST__BASE );
nbase = astGetNout( map );
lbndb = astMalloc( sizeof(double)*nbase );
ubndb = astMalloc( sizeof(double)*nbase );
if( astOK ) {
for( i = 0; i < nbase; i++ ) {
astMapBox( map, lbnd, ubnd, 1, i, lbndb + i, ubndb + i,
NULL, NULL );
}
/* Create a Box that is like this Interval except that missing bounds are
inherited from the supplied limits. Check that the resulting box is
closed. */
closed = 1;
for( i = 0; i < nbase; i++ ) {
if( this_interval->ubnd[ i ] != DBL_MAX ) ubndb[ i ] = this_interval->ubnd[ i ];
if( this_interval->lbnd[ i ] != -DBL_MAX ) lbndb[ i ] = this_interval->lbnd[ i ];
if( lbndb[ i ] > ubndb[ i ] ) closed = 0;
}
/* Cannot create the required mesh if the box is not closed. */
if( closed ) {
/* Create the Box. */
bfrm = astGetFrame( this->frameset, AST__BASE );
box = astBox( bfrm, 1, lbndb, ubndb, NULL, "", status );
/* Create the required mesh. */
result = astRegBaseMesh( box );
/* Free resources */
bfrm = astAnnul( bfrm );
box = astAnnul( box );
/* If the boundary of the supplied Region does not intersect the box,
return a PointSet containing a single bad position. */
} else {
result = astPointSet( 1, nbase, "", status );
ptr = astGetPoints( result );
if( astOK ) {
for( i = 0; i < nbase; i++ ) ptr[ i ][ 0 ] = AST__BAD;
}
}
}
/* Free resources. */
map = astAnnul( map );
lbndb = astFree( lbndb );
ubndb = astFree( ubndb );
}
/* Return NULL if an error occurred. */
if( !astOK ) result = astAnnul( result );
/* Return the required pointer. */
return result;
}
static AstBox *Cache( AstInterval *this, int *status ){
/*
* Name:
* Cache
* Purpose:
* Calculate intermediate values and cache them in the Interval structure.
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* AstBox *Cache( AstInterval *this, int *status )
* Class Membership:
* Interval member function
* Description:
* This function uses the PointSet stored in the parent Region to calculate
* some intermediate values which are useful in other methods. These
* values are stored within the Interval structure.
* Parameters:
* this
* Pointer to the Interval.
* status
* Pointer to the inherited status variable.
* Returned Value:
* If the Interval is equivalent to a Box, then a pointer to the
* equivalent Box is returned. This is a copy of the pointer stored in
* the Interval structure and should not be annulled.
*/
/* Local Variables: */
AstBox *bbox; /* Equivalent base Box */
AstFrame *bfrm; /* Interval base Frame */
AstFrame *cfrm; /* Interval current Frame */
AstRegion *map; /* Interval base->current Mapping */
AstRegion *reg; /* Pointer to this Region structure */
AstRegion *unc; /* Pointer to uncertainty Region */
double **ptr; /* Pointer to data holding all axis limits */
double *lbnd; /* Pointer to array of lower axis limits */
double *ubnd; /* Pointer to array of upper axis limits */
int i; /* Axis index */
int isBox; /* Is this Interval equivalent to a Box? */
int nc; /* Number of base Frame axes */
int neg; /* Is the equivalent Box negated? */
/* Check the global error status. Also return if the cached information
is up to date (i.e. not stale). */
if( !this->stale || !astOK ) return this->box;
/* Get a pointer to the Region structure */
reg = (AstRegion *) this;
/* The Interval structure contains a pointer to an equivalent Box
structure. This Box structure is created below if the Interval is
equivalent to a Box. Annul any previous box. */
if( this->box ) this->box = astAnnul( this->box );
/* Get the number of axes in the base Frame of the FrameSet encapsulated
by the parent Region structure. */
nc = astGetNin( reg->frameset );
/* Get a pointer to the array holding the axis limits held in the PointSet
encapsulated in the parent Region structure. */
ptr = astGetPoints( reg->points );
/* Allocate memory to hold the limits organised per point rather than per
axis. */
lbnd = astMalloc( sizeof( double )*(size_t)nc );
ubnd = astMalloc( sizeof( double )*(size_t)nc );
/* Check these pointers can be used safely. */
if( ubnd ) {
/* See if the Interval is effectively a (possibly negated) Box. Assume it
is to begin with. */
isBox = 1;
/* Initialisation to prevent compiler warnings. */
neg = 0;
/* Check the limits on every axis. */
for( i = 0; i < nc; i++ ) {
/* Copy the axis limits into the allocated arrays (these are needed by the
Box constructor later on). */
lbnd[ i ] = ptr[ i ][ 0 ];
ubnd[ i ] = ptr[ i ][ 1 ];
/* The Interval is not a Box if any axis limit is missing. In this case
use -DBL_MAX or +DBL_MAX as the limit to be stored in the Interval
structure. */
if( lbnd[ i ] == AST__BAD ) lbnd[ i ] = -DBL_MAX;
if( fabs( lbnd[ i ] ) == DBL_MAX ) isBox = 0;
if( ubnd[ i ] == AST__BAD ) ubnd[ i ] = DBL_MAX;
if( fabs( ubnd[ i ] ) == DBL_MAX ) isBox = 0;
/* If this is the first axis, note if the axis interval is included or
excluded. This is determined by whether the "lower limit" is greater
than or less than the "upper limit". If the axis interval is excluded
(lower limit greater than upper limit), then any equivalent Box will be
a negated Box (i.e. will represent the outside of a box rather than
the inside). */
if( i == 0 ){
neg = ( lbnd[ i ] > ubnd[ i ] );
/* The Interval is not a Box if the limits for this axis are not the same
way round as those of the first axis. */
} else {
if( neg ) {
if( lbnd[ i ] < ubnd[ i ] ) isBox = 0;
} else {
if( lbnd[ i ] > ubnd[ i ] ) isBox = 0;
}
}
}
/* If the Interval is effectively an unnegated Box, create the equivalent Box,
and store a pointer to it in the Interval structure. */
if( isBox && !neg ) {
bfrm = astGetFrame( reg->frameset, AST__BASE );
cfrm = astGetFrame( reg->frameset, AST__CURRENT );
map = astGetMapping( reg->frameset, AST__BASE, AST__CURRENT );
unc = astTestUnc( reg ) ? astGetUncFrm( reg, AST__BASE ) : NULL;
bbox = astBox( bfrm, 1, lbnd, ubnd, unc, "", status );
if( astIsAUnitMap( map ) ){
this->box = astClone( bbox );
} else {
this->box = astMapRegion( bbox, map, cfrm );
}
if( unc ) unc = astAnnul( unc );
cfrm = astAnnul( cfrm );
bfrm = astAnnul( bfrm );
map = astAnnul( map );
bbox = astAnnul( bbox );
/* If the supplied Interval has been negated, negate the equivalent Box. */
if( astGetNegated( this ) ) astNegate( this->box );
/* If the supplied Interval is closed, ensure the equivalent Box is closed. */
astSetClosed( this->box, astGetClosed( this ) );
}
/* Store the axis limits in the Interval structure. */
if( this->lbnd ) astFree( this->lbnd );
if( this->ubnd ) astFree( this->ubnd );
this->lbnd = lbnd;
this->ubnd = ubnd;
}
/* Indicate the cached information is no longer stale, and return a
pointer to any equivalent Box. */
this->stale = 0;
return this->box;
}
static int GetBounded( AstRegion *this, int *status ) {
/*
* Name:
* GetBounded
* Purpose:
* Is the Region bounded?
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* int GetBounded( AstRegion *this, int *status )
* Class Membership:
* Interval method (over-rides the astGetBounded method inherited from
* the Region class).
* Description:
* This function returns a flag indicating if the Region is bounded.
* The implementation provided by the base Region class is suitable
* for Region sub-classes representing the inside of a single closed
* curve (e.g. Circle, Interval, Box, etc). Other sub-classes (such as
* CmpRegion, PointList, etc ) may need to provide their own
* implementations.
* Parameters:
* this
* Pointer to the Region.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Non-zero if the Region is bounded. Zero otherwise.
*/
/* Local Variables: */
int result; /* Returned result */
/* Initialise */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* The unnegated Interval is bounded only if there is an equivalent Box
structure stored in the Interval structure. */
if( Cache( (AstInterval *) this, status ) ) result = 1;
/* Return the required pointer. */
return result;
}
static AstRegion *GetDefUnc( AstRegion *this_region, int *status ) {
/*
* Name:
* GetDefUnc
* Purpose:
* Obtain a pointer to the default uncertainty Region for a given Region.
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* AstRegion *GetDefUnc( AstRegion *this, int *status )
* Class Membership:
* Interval member function (over-rides the astGetDefUnc protected
* method inherited from the Region class).
* Description:
* This function returns a pointer to a Region which represents the
* default uncertainty associated with a position on the boundary of the
* given Region. The returned Region refers to the base Frame within the
* FrameSet encapsulated by the supplied Region.
* Parameters:
* this
* Pointer to the Region.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the Region. This should be annulled (using astAnnul)
* when no longer needed.
* 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: */
AstBox *box; /* Pointer to equivalent Box */
AstFrame *bfrm; /* Base Frame of supplied Region */
AstInterval *this; /* Pointer to Interval structure */
AstRegion *result; /* Returned pointer */
double *lbnd; /* Ptr. to array holding axis lower bounds */
double *ubnd; /* Ptr. to array holding axis upper bounds */
double c; /* Central axis value */
double hw; /* Half width of uncertainty interval */
int i; /* Axis index */
int nax; /* Number of base Frame axes */
/* Initialise */
result = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Get a pointer to the Interval structure. */
this = (AstInterval *) this_region;
/* If this Interval is equivalent to a Box, get the default uncertainty
for the equivalent Box and return it. */
box = Cache( this, status );
if( box ) {
result = astGetDefUnc( box );
/* Otherwise, we use a box covering 1.0E-6 of each axis interval, centred on
the origin. */
} else {
/* Get a pointer to the base Frame. */
bfrm = astGetFrame( this_region->frameset, AST__BASE );
/* Get the number of base Frame axes. */
nax = astGetNaxes( bfrm );
/* Allocate arrays to hold the bounds of the uncertainty Box. */
lbnd = astMalloc( sizeof( double)*(size_t) nax );
ubnd = astMalloc( sizeof( double)*(size_t) nax );
if( astOK ) {
/* Ensure cached information (e.g.bounds) is up to date. */
Cache( this, status );
/* Do each axis in turn */
for( i = 0; i < nax; i++ ) {
/* If this axis has both limits, use 1.0E-6 of the difference between the
limits. */
if( this->lbnd[ i ] != -DBL_MAX &&
this->ubnd[ i ] != DBL_MAX ) {
hw = fabs( 0.5E-6*( this->ubnd[ i ] - this->lbnd[ i ] ) );
c = 0.5*( this->ubnd[ i ] + this->lbnd[ i ] );
if( hw == 0.0 ) hw = c*0.5E-6;
ubnd[ i ] = c + hw;
lbnd[ i ] = c - hw;
/* Otherwise use zero. */
} else {
ubnd[ i ] = 0.0;
lbnd[ i ] = 0.0;
}
}
/* Create the Box. */
result = (AstRegion *) astBox( bfrm, 1, lbnd, ubnd, NULL, "", status );
}
/* Free resources. */
lbnd = astFree( lbnd );
ubnd = astFree( ubnd );
bfrm = astAnnul( bfrm );
}
/* Return NULL if an error occurred. */
if( !astOK ) result = astAnnul( result );
/* Return the required pointer. */
return result;
}
void astInitIntervalVtab_( AstIntervalVtab *vtab, const char *name, int *status ) {
/*
*+
* Name:
* astInitIntervalVtab
* Purpose:
* Initialise a virtual function table for a Interval.
* Type:
* Protected function.
* Synopsis:
* #include "interval.h"
* void astInitIntervalVtab( AstIntervalVtab *vtab, const char *name )
* Class Membership:
* Interval vtab initialiser.
* Description:
* This function initialises the component of a virtual function
* table which is used by the Interval 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 */
AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */
AstRegionVtab *region; /* Pointer to Region 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. */
astInitRegionVtab( (AstRegionVtab *) vtab, name );
/* Store a unique "magic" value in the virtual function table. This
will be used (by astIsAInterval) 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 = &(((AstRegionVtab *) vtab)->id);
/* Initialise member function pointers. */
/* ------------------------------------ */
/* Store pointers to the member functions (implemented here) that provide
virtual methods for this class. */
vtab->IntervalPoints = IntervalPoints;
/* Save the inherited pointers to methods that will be extended, and
replace them with pointers to the new member functions. */
object = (AstObjectVtab *) vtab;
mapping = (AstMappingVtab *) vtab;
region = (AstRegionVtab *) vtab;
#if defined(THREAD_SAFE)
parent_managelock = object->ManageLock;
object->ManageLock = ManageLock;
#endif
parent_transform = mapping->Transform;
mapping->Transform = Transform;
parent_simplify = mapping->Simplify;
mapping->Simplify = Simplify;
parent_overlap = region->Overlap;
region->Overlap = Overlap;
parent_setregfs = region->SetRegFS;
region->SetRegFS = SetRegFS;
parent_resetcache = region->ResetCache;
region->ResetCache = ResetCache;
parent_setunc = region->SetUnc;
region->SetUnc = SetUnc;
/* Store replacement pointers for methods which will be over-ridden by
new member functions implemented here. */
mapping->MapMerge = MapMerge;
region->RegCentre = RegCentre;
region->GetBounded = GetBounded;
region->GetDefUnc = GetDefUnc;
region->RegPins = RegPins;
region->RegTrace = RegTrace;
region->RegBaseMesh = RegBaseMesh;
region->BndBaseMesh = BndBaseMesh;
region->RegBaseBox = RegBaseBox;
region->RegBasePick = RegBasePick;
/* Declare the copy constructor, destructor and class dump
functions. */
astSetDelete( vtab, Delete );
astSetCopy( vtab, Copy );
astSetDump( vtab, Dump, "Interval", "Axis intervals" );
/* 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) );
}
}
void IntervalPoints( AstInterval *this, double *lbnd, double *ubnd,
int *status) {
/*
*+
* Name:
* astIntervalPoints
* Purpose:
* Return the defining points of a Interval.
* Type:
* Protected function.
* Synopsis:
* #include "box.h"
* astIntervalPoints( AstInterval *this, double *lbnd, double *ubnd )
* Class Membership:
* Region virtual function.
* Description:
* This function returns the axis values at the points defining the
* supplied Interval.
* Parameters:
* this
* Pointer to the Interval.
* lbnd
* A pointer to an array in which to return the "lbnd" values
* supplied when the Interval was constructed. These are in the
* base Frame of the encapsilated FrameSet.
* ubnd
* A pointer to an array in which to return the "ubnd" values
* supplied when the Interval was constructed. These are in the
* base Frame of the encapsilated FrameSet.
* Notes:
* - It is assumed that the length of the supplied arrays is at least
* equal to the number of axes in the base frame of the encapsulated
* FrameSet.
*-
*/
/* Local Variables: */
AstPointSet *pset;
double **ptr;
int nc;
int i;
/* Check the inherited status. */
if( !astOK ) return;
/* Get a pointer to the PointSet holding the points defining the Interval. */
pset = ((AstRegion *) this)->points;
/* Get a pointer to the PointSet's data arrays. */
ptr = astGetPoints( pset );
/* See how many axes each point in the PointSet has. */
nc = astGetNcoord( pset );
/* Copy the axis values from the PointSet into the supplied arrays. */
for( i = 0; i < nc; i++ ) {
lbnd[ i ] = ptr[ i ] [ 0 ];
ubnd[ i ] = ptr[ i ] [ 1 ];
}
}
#if defined(THREAD_SAFE)
static int ManageLock( AstObject *this_object, int mode, int extra,
AstObject **fail, int *status ) {
/*
* Name:
* ManageLock
* Purpose:
* Manage the thread lock on an Object.
* Type:
* Private function.
* Synopsis:
* #include "object.h"
* AstObject *ManageLock( AstObject *this, int mode, int extra,
* AstObject **fail, int *status )
* Class Membership:
* Interval member function (over-rides the astManageLock protected
* method inherited from the parent class).
* Description:
* This function manages the thread lock on the supplied Object. The
* lock can be locked, unlocked or checked by this function as
* deteremined by parameter "mode". See astLock for details of the way
* these locks are used.
* Parameters:
* this
* Pointer to the Object.
* mode
* An integer flag indicating what the function should do:
*
* AST__LOCK: Lock the Object for exclusive use by the calling
* thread. The "extra" value indicates what should be done if the
* Object is already locked (wait or report an error - see astLock).
*
* AST__UNLOCK: Unlock the Object for use by other threads.
*
* AST__CHECKLOCK: Check that the object is locked for use by the
* calling thread (report an error if not).
* extra
* Extra mode-specific information.
* fail
* If a non-zero function value is returned, a pointer to the
* Object that caused the failure is returned at "*fail". This may
* be "this" or it may be an Object contained within "this". Note,
* the Object's reference count is not incremented, and so the
* returned pointer should not be annulled. A NULL pointer is
* returned if this function returns a value of zero.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A local status value:
* 0 - Success
* 1 - Could not lock or unlock the object because it was already
* locked by another thread.
* 2 - Failed to lock a POSIX mutex
* 3 - Failed to unlock a POSIX mutex
* 4 - Bad "mode" value supplied.
* Notes:
* - This function attempts to execute even if an error has already
* occurred.
*/
/* Local Variables: */
AstInterval *this; /* Pointer to Interval structure */
int result; /* Returned status value */
/* Initialise */
result = 0;
/* Check the supplied pointer is not NULL. */
if( !this_object ) return result;
/* Obtain a pointers to the Interval structure. */
this = (AstInterval *) this_object;
/* Invoke the ManageLock method inherited from the parent class. */
if( !result ) result = (*parent_managelock)( this_object, mode, extra,
fail, status );
/* Invoke the astManageLock method on any Objects contained within
the supplied Object. */
if( !result ) result = astManageLock( this->box, mode, extra, fail );
return result;
}
#endif
static int MapMerge( AstMapping *this, int where, int series, int *nmap,
AstMapping ***map_list, int **invert_list, int *status ) {
/*
* Name:
* MapMerge
* Purpose:
* Simplify a sequence of Mappings containing a Interval.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* int MapMerge( AstMapping *this, int where, int series, int *nmap,
* AstMapping ***map_list, int **invert_list, int *status )
* Class Membership:
* Interval method (over-rides the protected astMapMerge method
* inherited from the Region class).
* Description:
* This function attempts to simplify a sequence of Mappings by
* merging a nominated Interval in the sequence with its neighbours,
* so as to shorten the sequence if possible.
*
* In many cases, simplification will not be possible and the
* function will return -1 to indicate this, without further
* action.
*
* In most cases of interest, however, this function will either
* attempt to replace the nominated Interval with a Mapping which it
* considers simpler, or to merge it with the Mappings which
* immediately precede it or follow it in the sequence (both will
* normally be considered). This is sufficient to ensure the
* eventual simplification of most Mapping sequences by repeated
* application of this function.
*
* In some cases, the function may attempt more elaborate
* simplification, involving any number of other Mappings in the
* sequence. It is not restricted in the type or scope of
* simplification it may perform, but will normally only attempt
* elaborate simplification in cases where a more straightforward
* approach is not adequate.
* Parameters:
* this
* Pointer to the nominated Interval which is to be merged with
* its neighbours. This should be a cloned copy of the Interval
* pointer contained in the array element "(*map_list)[where]"
* (see below). This pointer will not be annulled, and the
* Interval it identifies will not be modified by this function.
* where
* Index in the "*map_list" array (below) at which the pointer
* to the nominated Interval resides.
* series
* A non-zero value indicates that the sequence of Mappings to
* be simplified will be applied in series (i.e. one after the
* other), whereas a zero value indicates that they will be
* applied in parallel (i.e. on successive sub-sets of the
* input/output coordinates).
* nmap
* Address of an int which counts the number of Mappings in the
* sequence. On entry this should be set to the initial number
* of Mappings. On exit it will be updated to record the number
* of Mappings remaining after simplification.
* map_list
* Address of a pointer to a dynamically allocated array of
* Mapping pointers (produced, for example, by the astMapList
* method) which identifies the sequence of Mappings. On entry,
* the initial sequence of Mappings to be simplified should be
* supplied.
*
* On exit, the contents of this array will be modified to
* reflect any simplification carried out. Any form of
* simplification may be performed. This may involve any of: (a)
* removing Mappings by annulling any of the pointers supplied,
* (b) replacing them with pointers to new Mappings, (c)
* inserting additional Mappings and (d) changing their order.
*
* The intention is to reduce the number of Mappings in the
* sequence, if possible, and any reduction will be reflected in
* the value of "*nmap" returned. However, simplifications which
* do not reduce the length of the sequence (but improve its
* execution time, for example) may also be performed, and the
* sequence might conceivably increase in length (but normally
* only in order to split up a Mapping into pieces that can be
* more easily merged with their neighbours on subsequent
* invocations of this function).
*
* If Mappings are removed from the sequence, any gaps that
* remain will be closed up, by moving subsequent Mapping
* pointers along in the array, so that vacated elements occur
* at the end. If the sequence increases in length, the array
* will be extended (and its pointer updated) if necessary to
* accommodate any new elements.
*
* Note that any (or all) of the Mapping pointers supplied in
* this array may be annulled by this function, but the Mappings
* to which they refer are not modified in any way (although
* they may, of course, be deleted if the annulled pointer is
* the final one).
* invert_list
* Address of a pointer to a dynamically allocated array which,
* on entry, should contain values to be assigned to the Invert
* attributes of the Mappings identified in the "*map_list"
* array before they are applied (this array might have been
* produced, for example, by the astMapList method). These
* values will be used by this function instead of the actual
* Invert attributes of the Mappings supplied, which are
* ignored.
*
* On exit, the contents of this array will be updated to
* correspond with the possibly modified contents of the
* "*map_list" array. If the Mapping sequence increases in
* length, the "*invert_list" array will be extended (and its
* pointer updated) if necessary to accommodate any new
* elements.
* status
* Pointer to the inherited status variable.
* Returned Value:
* If simplification was possible, the function returns the index
* in the "map_list" array of the first element which was
* modified. Otherwise, it returns -1 (and makes no changes to the
* arrays supplied).
* Notes:
* - A value of -1 will be returned if this function is invoked
* with the global error status set, or if it should fail for any
* reason.
*/
/* Local Variables: */
AstInterval *oldint; /* Pointer to supplied Interval */
AstMapping *map; /* Pointer to adjacent Mapping */
AstMapping *new; /* Simplified or merged Region */
int i1; /* Index of first Mapping merged */
int i; /* Loop counter */
int result; /* Result value to return */
/* Initialise. */
result = -1;
i1 = -1;
/* Check the global error status. */
if ( !astOK ) return result;
/* Get a pointer to the Interval. */
oldint = (AstInterval *) this;
/* First of all, see if the Interval can be replaced by a simpler Region,
without reference to the neighbouring Regions in the list. */
/* =====================================================================*/
/* Try to simplify the Interval. If the pointer value has changed, we assume
some simplification took place. */
new = astSimplify( oldint );
if( new != (AstMapping *) oldint ) {
/* Annul the Interval pointer in the list and replace it with the new Region
pointer, and indicate that the forward transformation of the returned
Region should be used (not really needed but keeps things clean). */
(void) astAnnul( ( *map_list )[ where ] );
( *map_list )[ where ] = new;
( *invert_list )[ where ] = 0;
/* Return the index of the first modified element. */
result = where;
/* If the Interval itself could not be simplified, see if it can be merged
with the Regions on either side of it in the list. We can only merge
in parallel. */
/* =====================================================================*/
} else if( ! series ){
new = astAnnul( new );
/* Attempt to merge the Interval with its lower neighbour (if any). */
if( where > 0 ) {
i1 = where - 1;
map = ( *map_list )[ where - 1 ];
if( astIsARegion( map ) ) {
new = (AstMapping *) MergeInterval( oldint, (AstRegion *) map,
0, status );
}
}
/* If this did not produced a merged Region, attempt to merge the Interval
with its upper neighbour (if any). */
if( !new && where < *nmap - 1 ) {
i1 = where;
map = ( *map_list )[ where + 1 ];
if( astIsARegion( map ) ) {
new = (AstMapping *) MergeInterval( oldint, (AstRegion *) map,
1, status );
}
}
/* If succesfull... */
if( new ){
/* Annul the first of the two Mappings, and replace it with the merged
Region. Also clear the invert flag. */
(void) astAnnul( ( *map_list )[ i1 ] );
( *map_list )[ i1 ] = new;
( *invert_list )[ i1 ] = 0;
/* Annul the second of the two Mappings, and shuffle down the rest of the
list to fill the gap. */
(void) astAnnul( ( *map_list )[ i1 + 1 ] );
for ( i = i1 + 2; i < *nmap; i++ ) {
( *map_list )[ i - 1 ] = ( *map_list )[ i ];
( *invert_list )[ i - 1 ] = ( *invert_list )[ i ];
}
/* Clear the vacated element at the end. */
( *map_list )[ *nmap - 1 ] = NULL;
( *invert_list )[ *nmap - 1 ] = 0;
/* Decrement the Mapping count and return the index of the first
modified element. */
( *nmap )--;
result = i1;
}
} else {
new = astAnnul( new );
}
/* Return the result. */
return result;
}
static AstRegion *MergeInterval( AstInterval *this, AstRegion *reg,
int intfirst, int *status ) {
/*
* Name:
* MergeInterval
* Purpose:
* Attempt to merge a Interval with another Region to form a Region of
* higher dimensionality.
* Type:
* Private function.
* Synopsis:
* #include "box.h"
* AstRegion *MergeInterval( AstInterval *this, AstRegion *reg,
* int intfirst, int *status )
* Class Membership:
* Interval member function.
* Description:
* This function attempts to combine the supplied Regions together
* into a Region of higher dimensionality.
* Parameters:
* this
* Pointer to a Interval.
* reg
* Pointer to another Region.
* intfirst
* If non-zero, then the Interval axes are put first in the new Region.
* Otherwise, the other Region's axes are put first.
* status
* Pointer to the inherited status value.
* Returned Value:
* A pointer to a new region, or NULL if the supplied Regions could
* not be merged.
*/
/* Local Variables: */
AstFrame *bfrm; /* Pointer to base Frame for "result" */
AstFrame *cfrm; /* Pointer to current Frame for "result" */
AstFrame *frm_reg; /* Pointer to Frame from "reg" */
AstFrame *frm_this; /* Pointer to Frame from "this" */
AstMapping *bcmap; /* Base->current Mapping for "result" */
AstMapping *map_reg; /* Base->current Mapping from "reg" */
AstMapping *map_this; /* Base->current Mapping from "this" */
AstMapping *sbunc; /* Simplified uncertainty */
AstPointSet *pset_new; /* PointSet holding PointList axis values for new */
AstPointSet *pset_reg; /* PointSet holding PointList axis values for reg */
AstRegion *bunc; /* Base Frame uncertainty Region */
AstRegion *new; /* Pointer to new Interval in base Frame */
AstRegion *result; /* Pointer to returned Interval in current Frame */
AstRegion *unc_reg; /* Current Frame uncertainty Region from "reg" */
AstRegion *unc_this; /* Current Frame uncertainty Region from "this" */
double **ptr_new; /* Pointers to arrays holding new axis values */
double **ptr_reg; /* Pointers to arrays holding reg axis values */
double *centre; /* Array to hold Interval centre axis values */
double *corner; /* Array to hold Interval corner axis values */
double *lbnd; /* Array to hold lower axis bounds */
double *lbnd_unc; /* Array to hold uncertainty lower bounds */
double *p; /* Pointer to next input value */
double *q; /* Pointer to next output value */
double *ubnd; /* Array to hold upper axis bounds */
double *ubnd_unc; /* Array to hold uncertainty upper bounds */
double fac_reg; /* Ratio of used to default MeshSize for "reg" */
double fac_this; /* Ratio of used to default MeshSize for "this" */
double temp; /* Temporary storage */
int i; /* Loop count */
int j; /* Loop count */
int msz_reg; /* Original MeshSize for "reg" */
int msz_reg_set; /* Was MeshSize originally set for "reg"? */
int msz_this; /* Original MeshSize for "this" */
int msz_this_set; /* Was MeshSize originally set for "this"? */
int nax; /* Number of axes in "result" */
int nax_reg; /* Number of axes in "reg" */
int nax_this; /* Number of axes in "this" */
int neg_reg; /* Negated attribute value for other supplied Region */
int neg_this; /* Negated attribute value for supplied Interval */
int npnt; /* Number of points in PointList */
int ok; /* Can supplied Regions be merged? */
/* Initialise */
result = NULL;
lbnd = NULL;
ubnd = NULL;
/* Check the local error status. */
if ( !astOK ) return result;
/* Get the Closed attributes of the two Regions. They must be the same in
each Region if we are to merge the Regions. In addition, in order to
merge, either both Regions must have a defined uncertainty, or neither
Region must have a defined Uncertainty. */
if( astGetClosed( this ) == astGetClosed( reg ) &&
astTestUnc( this ) == astTestUnc( reg ) ) {
/* Get the Nagated attributes of the two Regions. */
neg_this = astGetNegated( this );
neg_reg = astGetNegated( reg );
/* Get the number of axes in the two supplied Regions. */
nax_reg = astGetNaxes( reg );
nax_this = astGetNaxes( this );
/* If the Regions can be combined, get the number of axes the
combination will have. */
nax = nax_reg + nax_this;
/* Get the base Frames from the two Region FrameSets, and combine them
into a single CmpFrame that will be used to create any new Region. */
frm_this = astGetFrame( ((AstRegion *) this)->frameset, AST__BASE );
frm_reg = astGetFrame( reg->frameset, AST__BASE );
if( intfirst ) {
bfrm = (AstFrame *) astCmpFrame( frm_this, frm_reg, "", status );
} else {
bfrm = (AstFrame *) astCmpFrame( frm_reg, frm_this, "", status );
}
frm_this = astAnnul( frm_this );
frm_reg = astAnnul( frm_reg );
/* Indicate we do not yet have a merged Region. */
new = NULL;
/* First attempt to merge with another Interval. The result will be an
Interval. Both Intervals must be un-negated. */
if( astIsAInterval( reg ) && !neg_this && !neg_reg ) {
/* Allocate memory to store the bounds of the returned Interval. */
lbnd = astMalloc( sizeof( double )*(size_t) nax );
ubnd = astMalloc( sizeof( double )*(size_t) nax );
/* Copy the limits from the supplied Intervals into the above arrays,
in the requested order. */
if( intfirst ) {
astIntervalPoints( this, lbnd, ubnd );
astIntervalPoints( reg, lbnd + nax_this, ubnd + nax_this );
} else {
astIntervalPoints( reg, lbnd, ubnd );
astIntervalPoints( this, lbnd + nax_reg, ubnd + nax_reg );
}
/* Create the new Interval, initially with no uncertainty. */
new = (AstRegion *) astInterval( bfrm, lbnd, ubnd, NULL, "",
status );
/* Free resources .*/
lbnd = astFree( lbnd );
ubnd = astFree( ubnd );
/* Now attempt to merge with a Box. The result will be an Interval. Both
Regions must be un-negated. */
} else if( astIsABox( reg ) && !neg_this && !neg_reg ) {
/* Allocate memory to store the bounds of the returned Interval. */
lbnd = astMalloc( sizeof( double )*(size_t) nax );
ubnd = astMalloc( sizeof( double )*(size_t) nax );
/* Get the bounds from the Interval and add them into the above arrays. */
if( intfirst ) {
astIntervalPoints( this, lbnd, ubnd );
} else {
astIntervalPoints( this, lbnd + nax_reg, ubnd + nax_reg );
}
/* Copy the centre and corner from the supplied Box into the required part
of the above arrays. */
if( intfirst ) {
centre = lbnd + nax_this;
corner = ubnd + nax_this;
} else {
centre = lbnd;
corner = ubnd;
}
astBoxPoints( reg, centre, corner );
/* Convert these centre and corner positions into upper and lower bounds. */
if( astOK ) {
for( i = 0; i < nax_reg; i++ ) {
centre[ i ] = 2*centre[ i ] - corner[ i ];
if( centre[ i ] > corner[ i ] ) {
temp = centre[ i ];
centre[ i ] = corner[ i ];
corner[ i ] = temp;
}
}
}
/* Create the new Interval, initially with no uncertainty. */
new = (AstRegion *) astInterval( bfrm, lbnd, ubnd, NULL, "",
status );
/* Free resources .*/
lbnd = astFree( lbnd );
ubnd = astFree( ubnd );
/* Now attempt to merge with a NullRegion. The result will be an Interval.
The NullRegion must be negated and the Interval must not. */
} else if( astIsANullRegion( reg ) && !neg_this && neg_reg ) {
/* Allocate memory to store the bounds of the returned Interval. */
lbnd = astMalloc( sizeof( double )*(size_t) nax );
ubnd = astMalloc( sizeof( double )*(size_t) nax );
/* Copy the limits from the supplied Interval into the above arrays.
Store bad values for the other axes indicating they are unbounded. */
if( intfirst ) {
astIntervalPoints( this, lbnd, ubnd );
for( i = nax_this; i < nax; i++ ) {
lbnd[ i ] = AST__BAD;
ubnd[ i ] = AST__BAD;
}
} else {
for( i = 0; i < nax_reg; i++ ) {
lbnd[ i ] = AST__BAD;
ubnd[ i ] = AST__BAD;
}
astIntervalPoints( this, lbnd + nax_reg, ubnd + nax_reg );
}
/* Create the new Interval, initially with no uncertainty. */
new = (AstRegion *) astInterval( bfrm, lbnd, ubnd, NULL, "",
status );
/* Free resources .*/
lbnd = astFree( lbnd );
ubnd = astFree( ubnd );
/* Now attempt to merge with a PointList. The result will be a PointList.
Both Regions must be un-negated. */
} else if( astIsAPointList( reg ) && !neg_this && !neg_reg ) {
/* We can only do this if the Interval has zero width on each axis (i.e.
represents a point). Get the Interval bounds. */
lbnd = astMalloc( sizeof( double )*(size_t) nax_this );
ubnd = astMalloc( sizeof( double )*(size_t) nax_this );
astRegBaseBox( this, lbnd, ubnd );
/* Get the size of the Interval's uncertainty region. */
lbnd_unc = astMalloc( sizeof( double )*(size_t) nax_this );
ubnd_unc = astMalloc( sizeof( double )*(size_t) nax_this );
bunc = astGetUncFrm( this, AST__BASE );
astGetRegionBounds( bunc, lbnd, ubnd );
/* Set "ok" to zero if the Interval does not have zero width on any axis. Here
"zero width" means a width less than half the uncertainty on the axis.
We also replace the lower bound values in the "lbnd" array by the central
values in the Interval. */
if( astOK ) {
ok = 1;
for( i = 0; i < nax_this; i++ ) {
if( fabs( lbnd[ i ] - lbnd[ i ] ) >
0.25*fabs( ubnd_unc[ i ] - lbnd_unc[ i ] ) ) {
ok = 0;
break;
} else {
lbnd[ i ] = 0.5*( lbnd[ i ] + ubnd[ i ] );
}
}
/* If the Interval is a point, we go on to create a new PointList. */
if( ok ) {
/* Get a PointSet holding the axis values in the supplied PointList data.
Also get the number of points in the PointSet and pointers to the arrays
holding the axis values. */
astPointListPoints( reg, &pset_reg );
npnt = astGetNpoint( pset_reg );
ptr_reg = astGetPoints( pset_reg );
/* Create a new PointSet with room for the same number of points, but
with the extra required axes. Get pointers to its axis arrays. */
pset_new = astPointSet( npnt, nax, "", status );
ptr_new = astGetPoints( pset_new );
/* Copy the PointList axis values into the new PointSet, and then include
the extra axis values defined by the Interval to each point. */
if( astOK ) {
for( j = 0; j < nax_reg; j++ ) {
p = ptr_reg[ j ];
q = ptr_new[ intfirst ? nax_this + j : j ];
for( i = 0; i < npnt; i++ ) *(q++) = *(p++);
}
for( j = 0; j < nax_this; j++ ) {
p = lbnd + j;
q = ptr_new[ intfirst ? j : nax_reg + j ];
for( i = 0; i < npnt; i++ ) *(q++) = *p;
}
/* Create the new PointList, initially with no uncertainty. */
new = (AstRegion *) astPointList( bfrm, pset_new, NULL,
"", status );
}
/* Free resources .*/
pset_new = astAnnul( pset_new );
pset_reg = astAnnul( pset_reg );
}
}
lbnd = astFree( lbnd );
ubnd = astFree( ubnd );
lbnd_unc = astFree( lbnd_unc );
ubnd_unc = astFree( ubnd_unc );
bunc = astAnnul( bunc );
}
/* If a new Region was created above, propagate remaining attributes of
the supplied Region to it. */
if( new ) {
astRegOverlay( new, this, 1 );
/* The above Prism constructors create the Prism with the correct value
for the Nagated attribute (i.e. zero). Ensure the above call to
astRegOverlay has not changed this. */
astClearNegated( new );
/* If both the supplied Regions have uncertainty, assign the new Region an
uncertainty. */
if( astTestUnc( this ) && astTestUnc( reg ) ) {
/* Get the uncertainties from the two supplied Regions. */
unc_this = astGetUncFrm( this, AST__BASE );
unc_reg = astGetUncFrm( reg, AST__BASE );
/* Combine them into a single Region (a Prism), in the correct order. */
if( intfirst ) {
bunc = (AstRegion *) astPrism( unc_this, unc_reg, "", status );
} else {
bunc = (AstRegion *) astPrism( unc_reg, unc_this, "", status );
}
/* Attempt to simplify the Prism. */
sbunc = astSimplify( bunc );
/* Use the simplified Prism as the uncertainty for the returned Region. */
astSetUnc( new, sbunc );
/* Free resources. */
sbunc = astAnnul( sbunc );
bunc = astAnnul( bunc );
unc_reg = astAnnul( unc_reg );
unc_this = astAnnul( unc_this );
}
/* Get the current Frames from the two Region FrameSets, and combine them
into a single CmpFrame. */
frm_this = astGetFrame( ((AstRegion *) this)->frameset, AST__CURRENT );
frm_reg = astGetFrame( reg->frameset, AST__CURRENT );
if( intfirst ) {
cfrm = (AstFrame *) astCmpFrame( frm_this, frm_reg, "", status );
} else {
cfrm = (AstFrame *) astCmpFrame( frm_reg, frm_this, "", status );
}
/* Get the base -> current Mappings from the two Region FrameSets, and
combine them into a single parallel CmpMap that connects bfrm and cfrm. */
map_this = astGetMapping( ((AstRegion *) this)->frameset, AST__BASE,
AST__CURRENT );
map_reg = astGetMapping( reg->frameset, AST__BASE, AST__CURRENT );
if( intfirst ) {
bcmap = (AstMapping *) astCmpMap( map_this, map_reg, 0, "",
status );
} else {
bcmap = (AstMapping *) astCmpMap( map_reg, map_this, 0, "",
status );
}
/* Map the new Region into the new current Frame. */
result = astMapRegion( new, bcmap, cfrm );
/* The filling factor in the returned is the product of the filling
factors for the two supplied Regions. */
if( astTestFillFactor( reg ) || astTestFillFactor( this ) ) {
astSetFillFactor( result, astGetFillFactor( reg )*
astGetFillFactor( this ) );
}
/* If the MeshSize value is set in either supplied Region, set a value
for the returned Region which scales the default value by the
product of the scaling factors for the two supplied Regions. First see
if either MeshSize value is set. */
msz_this_set = astTestMeshSize( this );
msz_reg_set = astTestMeshSize( reg );
if( msz_this_set || msz_reg_set ) {
/* If so, get the two MeshSize values (one of which may be a default
value), and then clear them so that the default value will be returned
in future. */
msz_this = astGetMeshSize( this );
msz_reg = astGetMeshSize( reg );
astClearMeshSize( this );
astClearMeshSize( reg );
/* Get the ratio of the used MeshSize to the default MeshSize for both
Regions. */
fac_this = (double)msz_this/(double)astGetMeshSize( this );
fac_reg = (double)msz_reg/(double)astGetMeshSize( reg );
/* The MeshSize of the returned Returned is the default value scaled by
the product of the two ratios found above. */
astSetMeshSize( result, fac_this*fac_reg*astGetMeshSize( result ) );
/* Re-instate the original MeshSize values for the supplied Regions (if
set) */
if( msz_this_set ) astSetMeshSize( this, msz_this );
if( msz_reg_set ) astSetMeshSize( reg, msz_reg );
}
/* Free remaining resources */
frm_this = astAnnul( frm_this );
frm_reg = astAnnul( frm_reg );
map_this = astAnnul( map_this );
map_reg = astAnnul( map_reg );
bcmap = astAnnul( bcmap );
new = astAnnul( new );
cfrm = astAnnul( cfrm );
}
bfrm = astAnnul( bfrm );
}
/* If an error has occurred, annul the returned pointer. */
if( !astOK ) result = astAnnul( result );
/* Return the result. */
return result;
}
static int *OneToOne( AstMapping *map, int *status ){
/*
* Name:
* OneToOne
* Purpose:
* Does each output of the supplied Mapping depend on only one input?
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* int OneToOne( AstMapping *map, int *status )
* Class Membership:
* Interval method
* Description:
* This function returns a flag indicating if the Mapping is 1-to-1.
* That is, if each output depends only on one input.
* Parameters:
* map
* Pointer to the Mapping.
* status
* Pointer to the inherited status variable.
* Returned Value:
* If the Mapping is 1-to-1, a pointer to an array of ints is returned
* (NULL is returned otherwise). There is one int for each output of
* the supplied Mapping. The value of each int is the index of the
* corresponding input which feeds the output. The array should be
* freed using astFree when no longer needed.
*/
/* Local Variables: */
int *result;
const char *class;
int nout;
int i;
int *tt;
AstMapping *tmap;
/* Initialise */
result = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Get the number of outputs for the Mapping. */
nout = astGetNout( map );
/* The Mapping cannot be 1-to-1 if the number of inputs is different.*/
if( astGetNin( map ) == nout ) {
/* Allocate an output array on the assumption that the Mapping is 1-to-1. */
result = astMalloc( sizeof( int )*(size_t) nout );
if( result ) {
/* Check known specal cases for speed. */
class = astGetClass( map );
if( !strcmp( class, "WinMap" ) ||
!strcmp( class, "ZoomMap" ) ||
!strcmp( class, "UnitMap" ) ||
!strcmp( class, "ShiftMap" ) ){
/* Each output is fed by the corresponding input for these classes of
Mapping. */
for( i = 0; i < nout; i++ ) result[ i ] = i;
/* Now do the general case. */
} else {
/* Loop round each input axis. */
for( i = 0; i < nout; i++ ) {
/* Use astMapSplit to see if this input corresponds to a single output. */
tt = astMapSplit( map, 1, &i, &tmap );
/* If not, annul the returned array and break. */
if( !tmap ) {
result = astFree( result );
break;
/* If so, store the index of the corresponding input in the returned
array and free resources. */
} else {
result[ tt[ 0 ] ] = i;
tt = astFree( tt );
if( astGetNout( tmap ) != 1 ) result = astFree( result );
tmap = astAnnul( tmap );
if( !result ) break;
}
}
}
}
}
/* Return the result */
return result;
}
static int Overlap( AstRegion *this, AstRegion *that, int *status ){
/*
* Name:
* Overlap
* Purpose:
* Test if two regions overlap each other.
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* int Overlap( AstRegion *this, AstRegion *that, int *status )
* Class Membership:
* Interval member function (over-rides the astOverlap method inherited
* from the Region class).
* Description:
* This function returns an integer value indicating if the two
* supplied Regions overlap. The two Regions are converted to a commnon
* coordinate system before performing the check. If this conversion is
* not possible (for instance because the two Regions represent areas in
* different domains), then the check cannot be performed and a zero value
* is returned to indicate this.
* Parameters:
* this
* Pointer to the first Region.
* that
* Pointer to the second Region.
* status
* Pointer to the inherited status variable.
* Returned Value:
* astOverlap()
* A value indicating if there is any overlap between the two Regions.
* Possible values are:
*
* 0 - The check could not be performed because the second Region
* could not be mapped into the coordinate system of the first
* Region.
*
* 1 - There is no overlap between the two Regions.
*
* 2 - The first Region is completely inside the second Region.
*
* 3 - The second Region is completely inside the first Region.
*
* 4 - There is partial overlap between the two Regions.
*
* 5 - The Regions are identical.
*
* 6 - The second Region is the negation of the first Region.
* Notes:
* - The returned values 5 and 6 do not check the value of the Closed
* attribute in the two Regions.
* - A value of zero will be returned if this function is invoked with the
* AST error status set, or if it should fail for any reason.
*/
/* Local Variables: */
AstFrame *frm;
AstFrameSet *fs;
AstMapping *map;
AstMapping *map1;
AstMapping *map2;
AstMapping *map3;
AstMapping *smap;
AstMapping *tmap;
AstPointSet *pset_that;
AstRegion *unc_temp;
AstRegion *unc_that;
AstRegion *unc_this;
double **ptr_that;
double **ptr_thato;
double **ptr_this;
double *lbndu_that;
double *lbndu_this;
double *ubndu_that;
double *ubndu_this;
double err;
double err_that;
double err_this;
double lb_that;
double lb_this;
double tmp;
double ub_that;
double ub_this;
int *outperm;
int ic;
int inc_that;
int inc_this;
int lb_equal;
int nc;
int neg_that;
int neg_this;
int ov;
int result;
int ub_equal;
static int newResult[ 5 ][ 5 ] = { { 1, 1, 1, 1, 1},
{ 1, 2, 4, 4, 2},
{ 1, 4, 3, 4, 3},
{ 1, 4, 4, 4, 4},
{ 1, 2, 3, 4, 5} };
/* Initialise */
result = 0;
/* Check the inherited status. */
if ( !astOK ) return result;
/* If both Regions are Intervals, we provide a specialised implementation.
The implementation in the parent Region class assumes that at least one of
the two Regions can be represented using a finite mesh of points on the
boundary which is not the case with Intervals. The implementation in this
class sees if the Mapping between the base Frames of the Intervals allows
the axis limits to be transferred from one Frame ot the other. */
if( astIsAInterval( this ) && astIsAInterval( that ) ) {
/* Get a FrameSet which connects the Frame represented by the second Interval
to the Frame represented by the first Interval. Check that the conection is
defined. */
fs = astConvert( that, this, "" );
if( fs ) {
/* Get a pointer to the Mapping from base to current Frame in the second
Interval */
map1 = astGetMapping( that->frameset, AST__BASE, AST__CURRENT );
/* Get the Mapping from the current Frame of the second Interval to the
current Frame of the first Interval. */
map2 = astGetMapping( fs, AST__BASE, AST__CURRENT );
/* Get a pointer to the Mapping from current to base Frame in the first
Interval. */
map3 = astGetMapping( this->frameset, AST__CURRENT, AST__BASE );
/* Combine these Mappings to get the Mapping from the base Frame of the
second Interval to the base Frame of the first Interval. */
tmap = (AstMapping *) astCmpMap( map1, map2, 1, "", status );
map = (AstMapping *) astCmpMap( tmap, map3, 1, "", status );
/* Simplify this Mapping. */
smap = astSimplify( map );
/* We can only proceed if each output of the simplified Mapping depends
on only one input. Test this. */
outperm = OneToOne( smap, status );
if( outperm ){
/* Get the uncertainty Regions for both Intervals, expressed in the base
Frames of the Intervals. */
unc_this = astGetUncFrm( this, AST__BASE );
unc_temp = astGetUncFrm( that, AST__BASE );
/* Map the uncertainty Region for the second Interval from the base Frame
of the second Interval into the base Frame of the first Interval. */
frm = astGetFrame( this->frameset, AST__BASE );
unc_that = astMapRegion( unc_temp, smap, frm );
/* Get the bounding boxes of the two uncertainty Regions in the base
Frame of the first Interval. */
nc = astGetNaxes( frm );
lbndu_this = astMalloc( sizeof( double )*(size_t)nc );
ubndu_this = astMalloc( sizeof( double )*(size_t)nc );
astGetRegionBounds( unc_this, lbndu_this, ubndu_this );
lbndu_that = astMalloc( sizeof( double )*(size_t)nc );
ubndu_that = astMalloc( sizeof( double )*(size_t)nc );
astGetRegionBounds( unc_that, lbndu_that, ubndu_that );
/* Transform the PointSet holding the limits for the second Interval into
the Frame of the first Interval. */
pset_that = astTransform( smap, that->points, 1, NULL );
/* Get pointers for accesing the limits of the two Intervals, expressed
in a common Frame (the base Frame of the first Interval). */
ptr_that = astGetPoints( pset_that );
ptr_thato = astGetPoints( that->points );
ptr_this = astGetPoints( this->points );
if( astOK ) {
/* Check the limits on each base Frame axis in turn. */
for( ic = 0; ic < nc; ic++ ) {
/* Get the widths of the two uncertainty boxes on this axis. */
err_this = ubndu_this[ ic ] - lbndu_this[ ic ];
err_that = ubndu_that[ ic ] - lbndu_that[ ic ];
/* Add this together in quadrature to get the tolerance for two values on
the current axis to be considered equal. */
err = sqrt( err_that*err_that + err_this*err_this );
/* Get the limits on this axis from both Intervals. */
lb_this = ptr_this[ ic ][ 0 ];
ub_this = ptr_this[ ic ][ 1 ];
lb_that = ptr_that[ ic ][ 0 ];
ub_that = ptr_that[ ic ][ 1 ];
/* The limits for "that" have been mapped, which may have resulted in
them being swapped. We need to unswap them in this case to prevent the
swapping being used as an indication of a desire to use an excluded
interval rather than an included interval. */
if( lb_that != AST__BAD && ub_that != AST__BAD ) {
if( ptr_thato[ ic ][ 0 ] < ptr_thato[ ic ][ 1 ] ) {
if( lb_that > ub_that ) {
tmp = lb_that;
lb_that = ub_that;
ub_that = tmp;
}
} else {
if( lb_that < ub_that ) {
tmp = lb_that;
lb_that = ub_that;
ub_that = tmp;
}
}
}
/* If the regions are not closed, reduce the limits by the smallest
amount possible. */
if( !astGetClosed( that ) ) {
if( lb_that != AST__BAD && lb_that < DBL_MAX )
lb_that += DBL_EPSILON*fabs(lb_that);
if( ub_that != AST__BAD && ub_that > -DBL_MAX )
ub_that -= DBL_EPSILON*fabs(ub_that);
}
if( !astGetClosed( this ) ) {
if( lb_this != AST__BAD && lb_this < DBL_MAX )
lb_this += DBL_EPSILON*fabs(lb_this);
if( ub_this != AST__BAD && ub_this > -DBL_MAX )
ub_this -= DBL_EPSILON*fabs(ub_this);
}
/* Replace any missing limits with suitable extreme values */
if( lb_this == AST__BAD ) lb_this = -DBL_MAX;
if( ub_this == AST__BAD ) ub_this = DBL_MAX;
if( lb_that == AST__BAD ) lb_that = -DBL_MAX;
if( ub_that == AST__BAD ) ub_that = DBL_MAX;
/* If the bounds are the wrong way round (indicating an excluded rather
than an included axis range), swap them. Also set a flag indicating if
the limits define an included or excluded range. */
inc_this = ( lb_this <= ub_this );
if( !inc_this ) {
tmp = lb_this;
lb_this = ub_this;
ub_this = tmp;
}
inc_that = ( lb_that <= ub_that );
if( !inc_that ) {
tmp = lb_that;
lb_that = ub_that;
ub_that = tmp;
}
/* Are the lower limits from the two Intervals effectively equal? Take care
about DBL_MAX values causing overflow. */
lb_equal = EQUAL( lb_this, lb_that );
if( !lb_equal && fabs(lb_this) != DBL_MAX &&
fabs(lb_that) != DBL_MAX ) {
lb_equal = ( fabs( lb_this - lb_that) <= err );
}
/* Are the upper limits from the two Intervals effectively equal? */
ub_equal = EQUAL( ub_this, ub_that );
if( !ub_equal && fabs(ub_this) != DBL_MAX &&
fabs(ub_that) != DBL_MAX ) {
ub_equal = ( fabs( ub_this - ub_that) <= err );
}
/* If both the limits on this axis are effectively equal for the two Intervals,
set "ov" to 5 if both Interval ranges are inclusive or both are exclusive,
and set "ov" to 6 if one Interval range is exclusive and the other is
inclusive. */
if( lb_equal && ub_equal ) {
ov = ( inc_this == inc_that ) ? 5 : 6;
/* See if the limits on this axis indicate overlap for the two Intervals. "ov"
is set to 1 if there is no overlap, 2 if the first Interval range is
completely inside the second Interval range, 3 if the second Interval
range is completely inside the first Interval range, and 4 if there is
partial overlap between the Interval ranges. */
} else if( inc_this ) {
if( inc_that ) {
if( lb_that <= lb_this && ub_that >= ub_this ) {
ov = 2;
} else if( lb_that >= lb_this && ub_that <= ub_this ) {
ov = 3;
} else if( ub_that >= lb_this && lb_that <= ub_this ) {
ov = 4;
} else {
ov = 1;
}
} else {
if( lb_that <= lb_this && ub_that >= ub_this ) {
ov = 1;
} else if( lb_that >= ub_this || ub_that <= lb_this ) {
ov = 2;
} else if( lb_this == -DBL_MAX && ub_this == DBL_MAX ) {
ov = 3;
} else {
ov = 4;
}
}
} else {
if( inc_that ) {
if( lb_this <= lb_that && ub_this >= ub_that ) {
ov = 1;
} else if( lb_this >= ub_that || ub_this <= lb_that ) {
ov = 3;
} else if( lb_that == -DBL_MAX && ub_that == DBL_MAX ) {
ov = 2;
} else {
ov = 4;
}
} else {
ov = 4;
}
}
/* The returned value is initialised on the basis of the first axis
overlap. */
if( ic == 0 ) {
result = ov;
/* For subsequent axes, combine the old result value with the new ov value
to get the new result value. */
} else {
result = newResult[ result - 1 ][ ov - 1 ];
}
/* If we now know there is no overlap, there is no point in checking any
remaining axes. */
if( result == 1 ) break;
}
/* The above logic assumed that neither of the Intervals has been negated.
Decide on the value to return, taking into account whether either of
the Intervals has been negated. */
neg_this = astGetNegated( this );
neg_that = astGetNegated( that );
if( result == 1 ) {
if( neg_this ) {
result = neg_that ? 4 : 3;
} else if( neg_that ){
result = 2;
}
} else if( result == 2) {
if( neg_this ) {
result = neg_that ? 3 : 4;
} else if( neg_that ){
result = 1;
}
} else if( result == 3) {
if( neg_this ) {
result = neg_that ? 2 : 1;
} else if( neg_that ){
result = 4;
}
} else if( result == 4) {
result = 4;
} else if( result == 5) {
if( neg_this ) {
result = neg_that ? 5 : 6;
} else if( neg_that ){
result = 6;
}
}
}
/* Free resources. */
pset_that = astAnnul( pset_that );
unc_this = astAnnul( unc_this );
unc_that = astAnnul( unc_that );
unc_temp = astAnnul( unc_temp );
frm = astAnnul( frm );
lbndu_this = astFree( lbndu_this );
ubndu_this = astFree( ubndu_this );
lbndu_that = astFree( lbndu_that );
ubndu_that = astFree( ubndu_that );
outperm = astFree( outperm );
}
smap = astAnnul( smap );
map = astAnnul( map );
tmap = astAnnul( tmap );
map3 = astAnnul( map3 );
map2 = astAnnul( map2 );
map1 = astAnnul( map1 );
fs = astAnnul( fs );
}
}
/* If overlap could not be determined using the above implementation, try
using the implementation inherited from the parent Region class. */
if( !result ) result = (*parent_overlap)( this, that, status );
/* If not OK, return zero. */
if( !astOK ) result = 0;
/* Return the result. */
return result;
}
static void RegBaseBox( AstRegion *this_region, double *lbnd, double *ubnd, int *status ){
/*
* Name:
* RegBaseBox
* Purpose:
* Returns the bounding box of an un-negated Region in the base Frame of
* the encapsulated FrameSet.
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* void RegBaseBox( AstRegion *this, double *lbnd, double *ubnd, int *status )
* Class Membership:
* Interval member function (over-rides the astRegBaseBox protected
* method inherited from the Region class).
* Description:
* This function returns the upper and lower axis bounds of a Region in
* the base Frame of the encapsulated FrameSet, assuming the Region
* has not been negated. That is, the value of the Negated attribute
* is ignored.
* Parameters:
* this
* Pointer to the Region.
* lbnd
* Pointer to an array in which to return the lower axis bounds
* covered by the Region in the base Frame of the encapsulated
* FrameSet. It should have at least as many elements as there are
* axes in the base Frame.
* ubnd
* Pointer to an array in which to return the upper axis bounds
* covered by the Region in the base Frame of the encapsulated
* FrameSet. It should have at least as many elements as there are
* axes in the base Frame.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
AstInterval *this;
int nax;
int i;
/* Check the global error status. */
if ( !astOK ) return;
/* Get a pointer to the Interval structure */
this = (AstInterval *) this_region;
/* Ensure the cached bounds are up to date. */
Cache( this, status );
/* Copy the cached bounds into the supplied arrays. */
nax = astGetNin( this_region->frameset );
for( i = 0; i < nax; i++ ) {
lbnd[ i ] = this->lbnd[ i ];
ubnd[ i ] = this->ubnd[ i ];
}
}
static AstPointSet *RegBaseMesh( AstRegion *this_region, int *status ){
/*
* Name:
* RegBaseMesh
* Purpose:
* Return a PointSet containing a mesh of points on the boundary of a
* Region in its base Frame.
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* AstPointSet *astRegBaseMesh( AstRegion *this, int *status )
* Class Membership:
* Interval member function (over-rides the astRegBaseMesh protected
* method inherited from the Region class).
* Description:
* This function returns a PointSet containing a mesh of points on the
* boundary of the Region. The points refer to the base Frame of
* the encapsulated FrameSet.
* Parameters:
* this
* Pointer to the Region.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the PointSet. The axis values in this PointSet will have
* associated accuracies derived from the accuracies which were
* supplied when the Region was created.
* Notes:
* - A NULL pointer is returned if an error has already occurred, or if
* this function should fail for any reason.
*/
/* Local Variables: */
AstBox *box; /* The equivalent Box */
AstPointSet *result; /* Returned pointer */
/* Initialise */
result = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* If the Interval is effectively a Box, invoke the astRegBaseMesh
function on the equivalent Box. A pointer to the equivalent Box will
be stored in the Interval structure. */
box = Cache( (AstInterval *) this_region, status );
if( box ) {
result = astRegBaseMesh( box );
/* If the Interval is not equivalent to a Box, report an error. */
} else {
astError( AST__INTER, "astRegBaseMesh(%s): The %s given is "
"unbounded and therefore no boundary mesh can be "
"produced (internal AST programming error).", status,
astGetClass( this_region ), astGetClass( this_region ) );
}
/* Return a pointer to the output PointSet. */
return result;
}
static AstRegion *RegBasePick( AstRegion *this_region, int naxes,
const int *axes, int *status ){
/*
* Name:
* RegBasePick
* Purpose:
* Return a Region formed by picking selected base Frame axes from the
* supplied Region.
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* AstRegion *RegBasePick( AstRegion *this, int naxes, const int *axes,
* int *status )
* Class Membership:
* Interval member function (over-rides the astRegBasePick protected
* method inherited from the Region class).
* Description:
* This function attempts to return a Region that is spanned by selected
* axes from the base Frame of the encapsulated FrameSet of the supplied
* Region. This may or may not be possible, depending on the class of
* Region. If it is not possible a NULL pointer is returned.
* Parameters:
* this
* Pointer to the Region.
* naxes
* The number of base Frame axes to select.
* axes
* An array holding the zero-based indices of the base Frame axes
* that are to be selected.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the Region, or NULL if no region can be formed.
* Notes:
* - A NULL pointer is returned if an error has already occurred, or if
* this function should fail for any reason.
*/
/* Local Variables: */
AstFrame *bfrm; /* The base Frame in the supplied Region */
AstFrame *frm; /* The base Frame in the returned Region */
AstPointSet *pset; /* Holds axis values defining the supplied Region */
AstRegion *bunc; /* The uncertainty in the supplied Region */
AstRegion *result; /* Returned Region */
AstRegion *unc; /* The uncertainty in the returned Region */
double **ptr; /* Holds axis values defining the supplied Region */
double *lbnd; /* Base Frm lower bound axis values */
double *ubnd; /* Base Frm upper bound axis values */
int i; /* Index of axis within returned Region */
/* Initialise */
result = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Get a pointer to the base Frame of the encapsulated FrameSet. */
bfrm = astGetFrame( this_region->frameset, AST__BASE );
/* Create a Frame by picking the selected axes from the base Frame of the
encapsulated FrameSet. */
frm = astPickAxes( bfrm, naxes, axes, NULL );
/* Get the uncertainty Region (if any) within the base Frame of the supplied
Region, and select the required axes from it. If the resulting Object
is not a Region, annul it so that the returned Region will have no
uncertainty. */
if( astTestUnc( this_region ) ) {
bunc = astGetUncFrm( this_region, AST__BASE );
unc = astPickAxes( bunc, naxes, axes, NULL );
bunc = astAnnul( bunc );
if( ! astIsARegion( unc ) ) unc = astAnnul( unc );
} else {
unc = NULL;
}
/* Get pointers to the coordinate data in the parent Region structure. */
pset = this_region->points;
ptr = astGetPoints( pset );
/* Get space to hold the limits of the Interval in the new Frame. */
lbnd = astMalloc( sizeof( *lbnd )*naxes );
ubnd = astMalloc( sizeof( *ubnd )*naxes );
/* Check pointers can be used safely. */
if( astOK ) {
/* Copy the limits for the selected axes into the arrays allocated above. */
for( i = 0; i < naxes; i++ ) {
lbnd[ i ] = ptr[ axes[ i ] ][ 0 ];
ubnd[ i ] = ptr[ axes[ i ] ][ 1 ];
}
/* Create the new Interval. */
result = (AstRegion *) astInterval( frm, lbnd, ubnd, unc, "", status );
}
/* Free resources */
frm = astAnnul( frm );
bfrm = astAnnul( bfrm );
if( unc ) unc = astAnnul( unc );
lbnd = astFree( lbnd );
ubnd = astFree( ubnd );
/* Return a NULL pointer if an error has occurred. */
if( !astOK ) result = astAnnul( result );
/* Return the result. */
return result;
}
static double *RegCentre( AstRegion *this_region, double *cen, double **ptr,
int index, int ifrm, int *status ){
/*
* Name:
* RegCentre
* Purpose:
* Re-centre a Region.
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* double *RegCentre( AstRegion *this, double *cen, double **ptr,
* int index, int ifrm, int *status )
* Class Membership:
* Interval member function (over-rides the astRegCentre protected
* method inherited from the Region class).
* Description:
* This function shifts the centre of the supplied Region to a
* specified position, or returns the current centre of the Region.
* Parameters:
* this
* Pointer to the Region.
* cen
* Pointer to an array of axis values, giving the new centre.
* Supply a NULL value for this in order to use "ptr" and "index" to
* specify the new centre.
* ptr
* Pointer to an array of points, one for each axis in the Region.
* Each pointer locates an array of axis values. This is the format
* returned by the PointSet method astGetPoints. Only used if "cen"
* is NULL.
* index
* The index of the point within the arrays identified by "ptr" at
* which is stored the coords for the new centre position. Only used
* if "cen" is NULL.
* ifrm
* Should be AST__BASE or AST__CURRENT. Indicates whether the centre
* position is supplied and returned in the base or current Frame of
* the FrameSet encapsulated within "this".
* status
* Pointer to the inherited status variable.
* Returned Value:
* If both "cen" and "ptr" are NULL then a pointer to a newly
* allocated dynamic array is returned which contains the centre
* coords of the Region. This array should be freed using astFree when
* no longer needed. If either of "ptr" or "cen" is not NULL, then a
* NULL pointer is returned.
* Notes:
* - Some Region sub-classes do not have a centre. Such classes will report
* an AST__INTER error code if this method is called with either "ptr" or
* "cen" not NULL. If "ptr" and "cen" are both NULL, then no error is
* reported if this method is invoked on a Region of an unsuitable class,
* but NULL is always returned.
*/
/* Local Variables: */
AstInterval *this; /* Pointer to Interval structure */
AstBox *box; /* Pointer to equivalent Box structure */
double **bptr; /* Data pointers for Region PointSet */
double *lbnd; /* Pointer to new lower bound values */
double *ubnd; /* Pointer to new upper bound values */
double *result; /* Returned pointer */
int i; /* Coordinate index */
int nax; /* Number of axes */
/* Initialise */
result = NULL;
/* Check the local error status. */
if ( !astOK ) return result;
/* Get a pointer to the Interval structure. */
this = (AstInterval *) this_region;
/* The Interval can only be re-centred if it is effectively a Box. */
box = Cache( (AstInterval *) this_region, status );
if( box ) {
/* If the centre is being changed... */
if( cen || ptr ) {
/* Set the new centre in the equivalent box. */
astRegCentre( box, cen, ptr, index, ifrm );
/* Get the new base Frame bounds from the Box. */
nax = astGetNin( this_region->frameset );
lbnd = astMalloc( sizeof( double )*nax );
ubnd = astMalloc( sizeof( double )*nax );
astRegBaseBox( box, lbnd, ubnd );
/* Store these bounds in the Interval structure. */
bptr = astGetPoints( this_region->points );
if( astOK ) {
for( i = 0; i < nax; i++ ) {
bptr[ i ][ 0 ] = lbnd[ i ];
bptr[ i ][ 1 ] = ubnd[ i ];
}
}
/* Free resources. */
lbnd = astFree( lbnd );
ubnd = astFree( ubnd );
/* If the centre is not being changed, just invoke the method on the
equivalent box. */
} else {
result = astRegCentre( box, NULL, NULL, 0, AST__BASE );
}
/* If the Interval is not equivalent to a Box, report an error */
} else if( cen || ptr ) {
astError( AST__REGCN, "astRegCentre(%s): The supplied %s is not a "
"closed Interval and so cannot be re-centred.", status,
astGetClass( this ), astGetClass( this ) );
}
/* Return the result. */
return result;
}
static int RegPins( AstRegion *this_region, AstPointSet *pset, AstRegion *unc,
int **mask, int *status ){
/*
* Name:
* RegPins
* Purpose:
* Check if a set of points fall on the boundary of a given Interval.
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* int RegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc,
* int **mask, int *status )
* Class Membership:
* Interval member function (over-rides the astRegPins protected
* method inherited from the Region class).
* Description:
* This function returns a flag indicating if the supplied set of
* points all fall on the boundary of the given Interval.
*
* Some tolerance is allowed, as specified by the uncertainty Region
* stored in the supplied Interval "this", and the supplied uncertainty
* Region "unc" which describes the uncertainty of the supplied points.
* Parameters:
* this
* Pointer to the Interval.
* pset
* Pointer to the PointSet. The points are assumed to refer to the
* base Frame of the FrameSet encapsulated by "this".
* unc
* Pointer to a Region representing the uncertainties in the points
* given by "pset". The Region is assumed to represent the base Frame
* of the FrameSet encapsulated by "this". Zero uncertainity is assumed
* if NULL is supplied.
* mask
* Pointer to location at which to return a pointer to a newly
* allocated dynamic array of ints. The number of elements in this
* array is equal to the value of the Npoint attribute of "pset".
* Each element in the returned array is set to 1 if the
* corresponding position in "pset" is on the boundary of the Region
* and is set to zero otherwise. A NULL value may be supplied
* in which case no array is created. If created, the array should
* be freed using astFree when no longer needed.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Non-zero if the points all fall on the boundary of the given
* Region, to within the tolerance specified. Zero otherwise.
*/
/* Local variables: */
AstBox *box; /* The equivalent Box */
AstInterval *large_int; /* Interval slightly larger than "this" */
AstInterval *small_int; /* Interval slightly smaller than "this" */
AstInterval *this; /* Pointer to the Interval structure. */
AstFrame *frm; /* Base Frame in supplied Interval */
AstPointSet *ps1; /* Points masked by larger Interval */
AstPointSet *ps2; /* Points masked by larger and smaller Intervals */
AstRegion *tunc; /* Uncertainity Region from "this" */
double **ptr; /* Pointer to axis values in "ps2" */
double *large_lbnd; /* Lower bounds of larger interval */
double *large_ubnd; /* Upper bounds of larger interval */
double *lbnd_tunc; /* Lower bounds of "this" uncertainty Region */
double *lbnd_unc; /* Lower bounds of supplied uncertainty Region */
double *p; /* Pointer to next axis value */
double *safe; /* An interior point in "this" */
double *small_lbnd; /* Lower bounds of smaller interval */
double *small_ubnd; /* Upper bounds of smaller interval */
double *ubnd_tunc; /* Upper bounds of "this" uncertainty Region */
double *ubnd_unc; /* Upper bounds of supplied uncertainty Region */
double *wid; /* Widths of "this" border */
double lb; /* Lower bound */
double ub; /* Upper bound */
double t; /* Swap space */
double w; /* Width */
int i; /* Axis index */
int j; /* Point index */
int nc; /* No. of axes in Interval base frame */
int np; /* No. of supplied points */
int result; /* Returned flag */
/* Initialise */
result = 0;
if( mask ) *mask = NULL;
/* Check the inherited status. */
if( !astOK ) return result;
/* Get a pointer to the Interval structure. */
this = (AstInterval *) this_region;
/* If the Interval is effectively a Box, invoke the astRegPins function on
the equivalent Box. A pointer to the equivalent Box will be stored in the
Interval structure. */
box = Cache( this, status );
if( box ) return astRegPins( box, pset, unc, mask );
/* Arrive here only if the Interval is not equivalent to a box (i.e. has
at least one infinite boundary). Get the number of base Frame axes in the
Interval, and check the supplied PointSet has the same number of axis
values per point. */
frm = astGetFrame( this_region->frameset, AST__BASE );
nc = astGetNaxes( frm );
if( astGetNcoord( pset ) != nc && astOK ) {
astError( AST__INTER, "astRegPins(%s): Illegal number of axis "
"values per point (%d) in the supplied PointSet - should be "
"%d (internal AST programming error).", status, astGetClass( this ),
astGetNcoord( pset ), nc );
}
/* Get the number of axes in the uncertainty Region and check it is the
same as above. */
if( unc && astGetNaxes( unc ) != nc && astOK ) {
astError( AST__INTER, "astRegPins(%s): Illegal number of axes (%d) "
"in the supplied uncertainty Region - should be "
"%d (internal AST programming error).", status, astGetClass( this ),
astGetNaxes( unc ), nc );
}
/* Get the centre of the region in the base Frame. We use this as a "safe"
interior point within the region. */
safe = astRegCentre( this, NULL, NULL, 0, AST__BASE );
/* We now find the maximum distance on each axis that a point can be from
the boundary of the Interval for it still to be considered to be on the
boundary. First get the Region which defines the uncertainty within the
Interval being checked (in its base Frame), re-centre it on the interior
point found above (to avoid problems if the uncertainty region straddles
a discontinuity), and get its bounding box. */
tunc = astGetUncFrm( this, AST__BASE );
if( safe ) astRegCentre( tunc, safe, NULL, 0, AST__CURRENT );
lbnd_tunc = astMalloc( sizeof( double )*(size_t) nc );
ubnd_tunc = astMalloc( sizeof( double )*(size_t) nc );
astGetRegionBounds( tunc, lbnd_tunc, ubnd_tunc );
/* Also get the Region which defines the uncertainty of the supplied
points and get its bounding box. First re-centre the uncertainty at the
interior position to avoid problems from uncertainties that straddle a
discontinuity. */
if( unc ) {
if( safe ) astRegCentre( unc, safe, NULL, 0, AST__CURRENT );
lbnd_unc = astMalloc( sizeof( double )*(size_t) nc );
ubnd_unc = astMalloc( sizeof( double )*(size_t) nc );
astGetRegionBounds( unc, lbnd_unc, ubnd_unc );
} else {
lbnd_unc = NULL;
ubnd_unc = NULL;
}
/* The required border width for each axis is half of the total width of
the two bounding boxes. Use a zero sized box "unc" if no box was supplied. */
wid = astMalloc( sizeof( double )*(size_t) nc );
large_lbnd = astMalloc( sizeof( double )*(size_t) nc );
large_ubnd = astMalloc( sizeof( double )*(size_t) nc );
small_lbnd = astMalloc( sizeof( double )*(size_t) nc );
small_ubnd = astMalloc( sizeof( double )*(size_t) nc );
if( small_ubnd ) {
if( unc ) {
for( i = 0; i < nc; i++ ) {
wid[ i ] = 0.5*( fabs( astAxDistance( frm, i + 1, lbnd_tunc[ i ],
ubnd_tunc[ i ] ) )
+ fabs( astAxDistance( frm, i + 1, lbnd_unc[ i ],
ubnd_unc[ i ] ) ) );
}
} else {
for( i = 0; i < nc; i++ ) {
wid[ i ] = 0.5*fabs( astAxDistance( frm, i + 1, lbnd_tunc[ i ],
ubnd_tunc[ i ] ) );
}
}
/* Create two new Intervals, one of which is larger than "this" by the widths
found above, and the other of which is smaller than "this" by the widths
found above. */
for( i = 0; i < nc; i++ ) {
lb = this->lbnd[ i ];
ub = this->ubnd[ i ];
if( lb > ub ) {
t = ub;
ub = lb;
lb = t;
}
w = fabs( wid[ i ] );
if( lb != -DBL_MAX ){
large_lbnd[ i ] = lb - w;
small_lbnd[ i ] = lb + w;
} else {
large_lbnd[ i ] = AST__BAD;
small_lbnd[ i ] = AST__BAD;
}
if( ub != DBL_MAX ){
large_ubnd[ i ] = ub + w;
small_ubnd[ i ] = ub - w;
} else {
large_ubnd[ i ] = AST__BAD;
small_ubnd[ i ] = AST__BAD;
}
if( small_lbnd[ i ] > small_ubnd[ i ] ) {
small_lbnd[ i ] = small_ubnd[ i ];
}
}
large_int = astInterval( frm, large_lbnd, large_ubnd, NULL, "", status );
small_int = astInterval( frm, small_lbnd, small_ubnd, NULL, "", status );
/* Negate the smaller interval.*/
astNegate( small_int );
/* Points are on the boundary of "this" if they are inside both the large
interval and the negated small interval. First transform the supplied
PointSet using the large interval, then transform them using the negated
smaller Interval. */
ps1 = astTransform( large_int, pset, 1, NULL );
ps2 = astTransform( small_int, ps1, 1, NULL );
/* Get a point to the resulting axis values, and the number of axis
values per axis. */
ptr = astGetPoints( ps2 );
np = astGetNpoint( ps2 );
/* If a mask array is to be returned, create one. */
if( mask ) {
*mask = astMalloc( sizeof(int)*(size_t) np );
/* Check all the resulting points, setting mask values for all of them. */
if( astOK ) {
/* Initialise the mask elements on the basis of the first axis values */
result = 1;
p = ptr[ 0 ];
for( j = 0; j < np; j++ ) {
if( *(p++) == AST__BAD ) {
result = 0;
(*mask)[ j ] = 0;
} else {
(*mask)[ j ] = 1;
}
}
/* Now check for bad values on other axes. */
for( i = 1; i < nc; i++ ) {
p = ptr[ i ];
for( j = 0; j < np; j++ ) {
if( *(p++) == AST__BAD ) {
result = 0;
(*mask)[ j ] = 0;
}
}
}
}
/* If no output mask is to be made, we can break out of the check as soon
as the first bad value is found. */
} else if( astOK ) {
result = 1;
for( i = 0; i < nc && result; i++ ) {
p = ptr[ i ];
for( j = 0; j < np; j++ ) {
if( *(p++) == AST__BAD ) {
result = 0;
break;
}
}
}
}
/* Free resources. */
large_int = astAnnul( large_int );
small_int = astAnnul( small_int );
ps1 = astAnnul( ps1 );
ps2 = astAnnul( ps2 );
}
tunc = astAnnul( tunc );
frm = astAnnul( frm );
lbnd_tunc = astFree( lbnd_tunc );
ubnd_tunc = astFree( ubnd_tunc );
if( unc ) lbnd_unc = astFree( lbnd_unc );
if( unc ) ubnd_unc = astFree( ubnd_unc );
wid = astFree( wid );
large_lbnd = astFree( large_lbnd );
large_ubnd = astFree( large_ubnd );
small_lbnd = astFree( small_lbnd );
small_ubnd = astFree( small_ubnd );
safe = astFree( safe );
/* If an error has occurred, return zero. */
if( !astOK ) {
result = 0;
if( mask ) *mask = astAnnul( *mask );
}
/* Return the result. */
return result;
}
static int RegTrace( AstRegion *this_region, int n, double *dist, double **ptr,
int *status ){
/*
*+
* Name:
* RegTrace
* Purpose:
* Return requested positions on the boundary of a 2D Region.
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* int astTraceRegion( AstRegion *this, int n, double *dist, double **ptr );
* Class Membership:
* Interval member function (overrides the astTraceRegion method
* inherited from the parent Region class).
* Description:
* This function returns positions on the boundary of the supplied
* Region, if possible. The required positions are indicated by a
* supplied list of scalar parameter values in the range zero to one.
* Zero corresponds to some arbitrary starting point on the boundary,
* and one corresponds to the end (which for a closed region will be
* the same place as the start).
* Parameters:
* this
* Pointer to the Region.
* n
* The number of positions to return. If this is zero, the function
* returns without action (but the returned function value still
* indicates if the method is supported or not).
* dist
* Pointer to an array of "n" scalar parameter values in the range
* 0 to 1.0.
* ptr
* A pointer to an array of pointers. The number of elements in
* this array should equal tthe number of axes in the Frame spanned
* by the Region. Each element of the array should be a pointer to
* an array of "n" doubles, in which to return the "n" values for
* the corresponding axis. The contents of the arrays are unchanged
* if the supplied Region belongs to a class that does not
* implement this method.
* Returned Value:
* Non-zero if the astTraceRegion method is implemented by the class
* of Region supplied, and zero if not.
*-
*/
/* Local Variables; */
AstBox *box;
int result;
/* Initialise */
result = 0;
/* Check inherited status. */
if( ! astOK ) return result;
/* If the Interval is effectively a Box, invoke the astRegTrace function on
the equivalent Box. A pointer to the equivalent Box will be stored in the
Interval structure. */
box = Cache( (AstInterval *) this_region, status );
if( box ) result = astRegTrace( box, n, dist, ptr );
/* Return the result. */
return result;
}
static void ResetCache( AstRegion *this, int *status ){
/*
* Name:
* ResetCache
* Purpose:
* Clear cached information within the supplied Region.
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* void ResetCache( AstRegion *this, int *status )
* Class Membership:
* Region member function (overrides the astResetCache method
* inherited from the parent Region class).
* Description:
* This function clears cached information from the supplied Region
* structure.
* Parameters:
* this
* Pointer to the Region.
* status
* Pointer to the inherited status variable.
*/
if( this ) {
( (AstInterval *) this )->stale = 1;
(*parent_resetcache)( this, status );
}
}
static void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) {
/*
* Name:
* SetRegFS
* Purpose:
* Stores a new FrameSet in a Region
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status )
* Class Membership:
* Interval method (over-rides the astSetRegFS method inherited from
* the Region class).
* Description:
* This function creates a new FrameSet and stores it in the supplied
* Region. The new FrameSet contains two copies of the supplied
* Frame, connected by a UnitMap.
* Parameters:
* this
* Pointer to the Region.
* frm
* The Frame to use.
* status
* Pointer to the inherited status variable.
*/
/* Check the global error status. */
if ( !astOK ) return;
/* Invoke the parent method to store the FrameSet in the parent Region
structure. */
(* parent_setregfs)( this_region, frm, status );
/* Indicate that the cached intermediate information is now stale and
should be recreated when next needed. */
astResetCache( this_region );
}
static void SetUnc( AstRegion *this, AstRegion *unc, int *status ){
/*
* Name:
* SetUnc
* Purpose:
* Store uncertainty information in a Region.
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* void SetUnc( AstRegion *this, AstRegion *unc, int *status )
* Class Membership:
* Interval method (over-rides the astSetUnc method inherited from the
* Region class).
* Description:
* Each Region (of any class) can have an "uncertainty" which specifies
* the uncertainties associated with the boundary of the Region. This
* information is supplied in the form of a second Region. The uncertainty
* in any point on the boundary of a Region is found by shifting the
* associated "uncertainty" Region so that it is centred at the boundary
* point being considered. The area covered by the shifted uncertainty
* Region then represents the uncertainty in the boundary position.
* The uncertainty is assumed to be the same for all points.
*
* The uncertainty is usually specified when the Region is created, but
* this function allows it to be changed at any time.
* Parameters:
* this
* Pointer to the Region which is to be assigned a new uncertainty.
* unc
* Pointer to the new uncertainty Region. This must be either a Box,
* a Circle or an Ellipse. A deep copy of the supplied Region will be
* taken, so subsequent changes to the uncertainty Region using the
* supplied pointer will have no effect on the Region "this".
* status
* Pointer to the inherited status variable.
*/
/* Check the inherited status. */
if( !astOK ) return;
/* Invoke the astSetUnc method inherited from the parent Region class. */
(*parent_setunc)( this, unc, status );
/* Indicate that the cached intermediate information is now stale and
should be recreated when next needed. */
astResetCache( this );
}
static AstMapping *Simplify( AstMapping *this_mapping, int *status ) {
/*
* Name:
* Simplify
* Purpose:
* Simplify the Mapping represented by a Region.
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* AstMapping *Simplify( AstMapping *this, int *status )
* Class Membership:
* Interval method (over-rides the astSimplify method inherited
* from the Region class).
* Description:
* This function invokes the parent Region Simplify method, and then
* performs any further region-specific simplification.
*
* If the Mapping from base to current Frame is not a UnitMap, this
* will include attempting to fit a new Region to the boundary defined
* in the current Frame.
* Parameters:
* this
* Pointer to the original Region.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to the simplified Region. A cloned pointer to the
* supplied Region will be returned if no simplication could be
* performed.
* Notes:
* - A NULL pointer value will be returned if this function is
* invoked with the AST error status set, or if it should fail for
* any reason.
*/
/* Local Variables: */
AstBox *box2; /* Box used to determine 1-to-1 axis correspondance */
AstBox *box; /* Box used to determine 1-to-1 axis correspondance */
AstInterval *this_interval;/* Pointer to Interval structure */
AstMapping *bfrm; /* Pointer to base Frame in supplied Interval */
AstMapping *cfrm; /* Pointer to current Frame in supplied Interval */
AstMapping *map; /* Base -> current Mapping after parent simplification */
AstMapping *result; /* Result pointer to return */
AstPointSet *pset2; /* PointSet containing current Frame test points */
AstPointSet *pset3; /* PointSet containing base Frame test points */
AstPointSet *psetb; /* PointSet holding base positions */
AstPointSet *psetc; /* PointSet holding current positions */
AstRegion *new; /* Pointer to Region simplfied by parent class */
AstRegion *sreg; /* Pointer to simplified Box */
AstRegion *this; /* Pointer to supplied Region structure */
AstRegion *unc; /* Pointer to uncertainty Region */
double **ptr2; /* Pointer axis values in "pset2" */
double **ptr3; /* Pointer axis values in "pset3" */
double **ptr; /* Pointer to base Frame values defining Interval */
double **ptrb; /* Pointer to "psetb" axis values */
double **sptr; /* Pointer to simplified Interval bounds */
double *lbnd; /* Pointer to array of base Frame lower bounds */
double *slbnd; /* Pointer to array of current Frame lower bounds */
double *subnd; /* Pointer to array of current Frame upper bounds */
double *ubnd; /* Pointer to array of base Frame upper bounds */
double d; /* Distance between axis values */
double lb; /* Lower bound on axis values */
double lwid; /* Axis width below the Interval lower limit */
double maxd; /* Maximum currenrt Frame axis offset between test points */
double tmp; /* Temporary storage for swapping variable values */
double ub; /* Upperbound on axis values */
double uwid; /* Axis width above the Interval upper limit */
int bax; /* Base Frame axis index corresponding to "ic" */
int ic; /* Axis index */
int jc; /* Axis index */
int nc; /* No. of base Frame axis values per point */
int simpler; /* Has some simplication taken place? */
int snc; /* No. of current Frame axis values per point */
/* Initialise. */
result = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Get a pointer to the supplied Region structure. */
this = (AstRegion *) this_mapping;
/* Get a pointer to the supplied Interval structure. */
this_interval = (AstInterval *) this;
/* If this Interval is equivalent to a Box, use the astTransform method of
the equivalent Box. */
box = Cache( this_interval, status );
if( box ) {
result = astSimplify( box );
/* Otherwise, we use a new implementation appropriate for unbounded
intervals. */
} else {
/* Invoke the parent Simplify method inherited from the Region class. This
will simplify the encapsulated FrameSet and uncertainty Region. */
new = (AstRegion *) (*parent_simplify)( this_mapping, status );
if( new ) {
/* Note if any simplification took place. This is assumed to be the case
if the pointer returned by the above call is different to the supplied
pointer. */
simpler = ( new != this );
/* If the Mapping from base to current Frame is not a UnitMap, we attempt
to simplify the Interval by re-defining it within its current Frame. */
map = astGetMapping( new->frameset, AST__BASE, AST__CURRENT );
if( !astIsAUnitMap( map ) ){
/* Take a copy of the Interval bounds (defined in the base Frame of the
Intervals FrameSet) and replace any missing limits with arbitrary
non-BAD values. This will give us a complete set of bounds defining a
box within the base Frame of the Interval. */
ptr = astGetPoints( new->points );
nc = astGetNcoord( new->points );
lbnd = astMalloc( sizeof( double )*(size_t) nc );
ubnd = astMalloc( sizeof( double )*(size_t) nc );
if( astOK ) {
for( ic = 0; ic < nc; ic++ ) {
lbnd[ ic ] = ptr[ ic ][ 0 ];
ubnd[ ic ] = ptr[ ic ][ 1 ];
/* Ensure we have a good upper bound for this axis. */
if( ubnd[ ic ] == AST__BAD ) {
if( lbnd[ ic ] == AST__BAD ) {
ubnd[ ic ] = 1.0;
} else if( lbnd[ ic ] > 0.0 ) {
ubnd[ ic ] = lbnd[ ic ]*1.01;
} else if( lbnd[ ic ] < 0.0 ) {
ubnd[ ic ] = lbnd[ ic ]*0.99;
} else {
ubnd[ ic ] = 1.0;
}
}
/* Ensure we have a good lower bound for this axis. */
if( lbnd[ ic ] == AST__BAD ) {
if( ubnd[ ic ] > 0.0 ) {
lbnd[ ic ] = ubnd[ ic ]*0.99;
} else if( ubnd[ ic ] < 0.0 ) {
lbnd[ ic ] = ubnd[ ic ]*1.01;
} else {
lbnd[ ic ] = 1.0;
}
}
}
}
/* Transform the box corners found above into the current frame and then back
into the base Frame, and ensure that the box encloses both the original
and the new bounds. PermMaps with fewer outputs than inputs can cause the
resulting base Frame positions to differ significantly from the original. */
psetb =astPointSet( 2, nc,"", status );
ptrb =astGetPoints( psetb );
if( astOK ) {
for( ic = 0; ic < nc; ic++ ) {
ptrb[ ic ][ 0 ] = lbnd[ ic ];
ptrb[ ic ][ 1 ] = ubnd[ ic ];
}
}
psetc = astTransform( map, psetb, 1, NULL );
(void) astTransform( map, psetc, 0, psetb );
if( astOK ) {
for( ic = 0; ic < nc; ic++ ) {
lb = ptrb[ ic ][ 0 ];
if( lb != AST__BAD ) {
if( lb < lbnd[ ic ] ) lbnd[ ic ] = lb;
if( lb > ubnd[ ic ] ) ubnd[ ic ] = lb;
}
ub = ptrb[ ic ][ 1 ];
if( ub != AST__BAD ) {
if( ub < lbnd[ ic ] ) lbnd[ ic ] = ub;
if( ub > ubnd[ ic ] ) ubnd[ ic ] = ub;
}
}
}
psetb = astAnnul( psetb );
psetc = astAnnul( psetc );
/* Limit this box to not exceed the limits imposed by the Interval.*/
Cache( this_interval, status );
for( ic = 0; ic < nc; ic++ ) {
lb = this_interval->lbnd[ ic ] ;
ub = this_interval->ubnd[ ic ] ;
if( lb <= ub ) {
if( lbnd[ ic ] < lb ) {
lbnd[ ic ] = lb;
} else if( lbnd[ ic ] > ub ) {
lbnd[ ic ] = ub;
}
if( ubnd[ ic ] < lb ) {
ubnd[ ic ] = lb;
} else if( ubnd[ ic ] > ub ) {
ubnd[ ic ] = ub;
}
} else {
lwid = lb - lbnd[ ic ];
uwid = ubnd[ ic ] - ub;
if( lwid > uwid ) {
if( lbnd[ ic ] > lb ) lbnd[ ic ] = lb;
if( ubnd[ ic ] > lb ) ubnd[ ic ] = lb;
} else {
if( lbnd[ ic ] < ub ) lbnd[ ic ] = ub;
if( ubnd[ ic ] < ub ) ubnd[ ic ] = ub;
}
}
/* Ensure the bounds are not equal */
if( lbnd[ ic ] == 0.0 && ubnd[ ic ] == 0.0 ) {
ubnd[ ic ] = 1.0;
} else if( EQUAL( lbnd[ ic ], ubnd[ ic ] ) ) {
ubnd[ ic ] = MAX( ubnd[ ic ], lbnd[ ic ] )*( 1.0E6*DBL_EPSILON );
}
}
/* Create a new Box representing the box found above. */
bfrm = astGetFrame( new->frameset, AST__BASE );
unc = astTestUnc( new ) ? astGetUncFrm( new, AST__BASE ) : NULL;
box = astBox( bfrm, 1, lbnd, ubnd, unc, "", status );
if( unc ) unc = astAnnul( unc );
/* Modify this Box so that it has the same current Frame as this Interval. */
cfrm = astGetFrame( new->frameset, AST__CURRENT );
box2 = astMapRegion( box, map, cfrm );
/* Try simplifying the Box. */
sreg = (AstRegion *) astSimplify( box2 );
/* Only proceed if the Box was simplified */
if( sreg != (AstRegion *) box2 ) {
/* If the simplified Box is a NullRegion return it. */
if( astIsANullRegion( sreg ) ) {
(void) astAnnul( new );
new = astClone( sreg );
simpler = 1;
/* If the simplified Box is a Box or an Interval... */
} else if( astIsABox( sreg ) || astIsAInterval( sreg ) ) {
/* Get the bounds of the simplified Box. We assume that the base and
current Frames in the simplified Box are the same. */
snc = astGetNin( sreg->frameset );
slbnd = astMalloc( sizeof( double )*(size_t)snc );
subnd = astMalloc( sizeof( double )*(size_t)snc );
if( astIsAInterval( sreg ) ) {
sptr = astGetPoints( sreg->points );
if( astOK ) {
for( ic = 0; ic < snc; ic++ ) {
slbnd[ ic ] = sptr[ ic ][ 0 ];
subnd[ ic ] = sptr[ ic ][ 1 ];
}
}
} else {
astRegBaseBox( sreg, slbnd, subnd );
}
/* Now create a PointSet containing one point for each axis in the
current (or equivalently, base ) Frame of the simplified Box, plus an
extra point. */
pset2 = astPointSet( snc + 1, snc, "", status );
ptr2 = astGetPoints( pset2 );
/* Put the lower bounds of the simplified Box into the first point in
this PointSet. The remaining points are displaced from this first point
along each axis in turn. The length of each displacement is determined
by the length of the box on the axis. */
if( astOK ) {
for( ic = 0; ic < snc; ic++ ) {
for( jc = 0; jc < snc + 1; jc++ ) {
ptr2[ ic ][ jc ] = slbnd[ ic ];
}
ptr2[ ic ][ ic + 1 ] = subnd[ ic ];
}
}
/* Transform this PointSet into the base Frame of this Interval using the
inverse of the base->current Mapping. */
pset3 = astTransform( map, pset2, 0, NULL );
ptr3 = astGetPoints( pset3 );
if( astOK ) {
/* Now consider each axis of the Interval's current Frame (i.e. each base
Frame axis in the simplified Box). */
for( ic = 0; ic < snc; ic++ ) {
/* Given that the Box simplified succesfully, we know that there is a one
to one connection between the axes of the base and current Frame in this
Interval, but we do not yet know which base Frame axis corresponds to
which current Frame axis (and the number of base and current Frame axes
need not be equal). We have two points on a line parallel to current
Frame axis number "ic" (points zero and "ic+1" in "pset2"). Look at the
corresponding base Frame positions (in "pset3), and see which base Frame
axis they are parallel to. We look for the largest base Frame axis
increment (this allows small non-zero displacements to occur on the
other axes due to rounding errors). */
maxd = -DBL_MAX;
bax = -1;
for( jc = 0; jc < nc; jc++ ) {
d = fabs( astAxDistance( bfrm, jc + 1, ptr3[ jc ][ 0 ],
ptr3[ jc ][ ic + 1 ] ) );
if( d != AST__BAD && d > maxd ) {
maxd = d;
bax = jc;
}
}
/* If the largest base Frame axis increment is zero, it must mean that
the current Frame axis is not present in the base Frame. The only
plausable cause of this is if the base->current Mapping contains a
PermMap which introduces an extra axis, in which case the axis will
have a fixed value (any other Mapping arrangement would have prevented
the Box from simplifying). Therefore, set upper and lower limits for
this axis to the same value. */
if( maxd <= 0.0 ) {
if( slbnd[ ic ] == AST__BAD ||
subnd[ ic ] == AST__BAD ) {
slbnd[ ic ] = AST__BAD;
} else {
slbnd[ ic ] = 0.5*( slbnd[ ic ] + subnd[ ic ] );
}
subnd[ ic ] = slbnd[ ic ];
/* If we have found a base Frame axis which corresponds to the current
Frame axis "ic", then look to see which limits are specified for the
base Frame axis, and transfer missing limits to the current Frame. */
} else {
if( ptr[ bax ][ 0 ] == AST__BAD ) slbnd[ ic ] = AST__BAD;
if( ptr[ bax ][ 1 ] == AST__BAD ) subnd[ ic ] = AST__BAD;
/* If the original limits were equal, ensure the new limits are equal
(the code above modified the upper limit to ensure it was different to
the lower limit). */
if( ptr[ bax ][ 1 ] == ptr[ bax ][ 0 ] ) {
subnd[ ic ] = slbnd[ ic ];
/* If the original interval was an inclusion (ubnd > lbnd), ensure the new
interval is also an inclusion by swapping the limits if required. */
} else if( ptr[ bax ][ 1 ] > ptr[ bax ][ 0 ] ) {
if( subnd[ ic ] < slbnd[ ic ] ) {
tmp = subnd[ ic ];
subnd[ ic ] = slbnd[ ic ];
slbnd[ ic ] = tmp;
}
/* If the original interval was an exclusion (ubnd < lbnd), ensure the new
interval is also an exlusion by swapping the limits if required. */
} else if( ptr[ bax ][ 1 ] < ptr[ bax ][ 0 ] ) {
if( subnd[ ic ] > slbnd[ ic ] ) {
tmp = subnd[ ic ];
subnd[ ic ] = slbnd[ ic ];
slbnd[ ic ] = tmp;
}
}
}
}
/* Create the simplified Interval from the current Frame limits found
above, and use it in place of the original. */
unc = astTestUnc( new ) ? astGetUncFrm( new, AST__CURRENT ) : NULL;
(void) astAnnul( new );
new = (AstRegion *) astInterval( cfrm, slbnd, subnd, unc, "", status );
if( unc ) unc = astAnnul( unc );
simpler = 1;
}
/* Free resources */
pset2 = astAnnul( pset2 );
pset3 = astAnnul( pset3 );
slbnd = astFree( slbnd );
subnd = astFree( subnd );
}
}
/* Free resources */
bfrm = astAnnul( bfrm );
cfrm = astAnnul( cfrm );
box = astAnnul( box );
box2 = astAnnul( box2 );
sreg = astAnnul( sreg );
lbnd = astFree( lbnd );
ubnd = astFree( ubnd );
}
/* Free resources */
map = astAnnul( map );
/* If any simplification could be performed, copy Region attributes from
the supplied Region to the returned Region, and return a pointer to it.
If the supplied Region had no uncertainty, ensure the returned Region
has no uncertainty. Otherwise, return a clone of the supplied pointer. */
if( simpler ){
astRegOverlay( new, this, 1 );
result = (AstMapping *) new;
} else {
new = astAnnul( new );
result = astClone( this );
}
}
}
/* If an error occurred, annul the returned pointer. */
if ( !astOK ) result = astAnnul( result );
/* Return the result. */
return result;
}
static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in,
int forward, AstPointSet *out, int *status ) {
/*
* Name:
* Transform
* Purpose:
* Apply a Interval to transform a set of points.
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* AstPointSet *Transform( AstMapping *this, AstPointSet *in,
* int forward, AstPointSet *out, int *status )
* Class Membership:
* Interval member function (over-rides the astTransform protected
* method inherited from the Region class).
* Description:
* This function takes a Interval and a set of points encapsulated in a
* PointSet and transforms the points by setting axis values to
* AST__BAD for all points which are outside the region. Points inside
* the region are copied unchanged from input to output.
* Parameters:
* this
* Pointer to the Interval.
* in
* Pointer to the PointSet holding the input coordinate data.
* forward
* A non-zero value indicates that the forward coordinate transformation
* should be applied, while a zero value requests the inverse
* transformation.
* out
* Pointer to a PointSet which will hold the transformed (output)
* coordinate values. A NULL value may also be given, in which case a
* new PointSet will be created by this function.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the output (possibly new) PointSet.
* Notes:
* - The forward and inverse transformations are identical for a
* Region.
* - 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.
* - The number of coordinate values per point in the input PointSet must
* match the number of axes in the Frame represented by the Interval.
* - If an output PointSet is supplied, it must have space for sufficient
* number of points and coordinate values per point to accommodate the
* result. Any excess space will be ignored.
*/
/* Local Variables: */
AstBox *box; /* Pointer to equivalent Box */
AstInterval *this; /* Pointer to Interval structure */
AstPointSet *pset_tmp; /* Pointer to PointSet holding base Frame positions*/
AstPointSet *result; /* Pointer to output PointSet */
AstRegion *reg; /* Pointer to Region structure */
AstRegion *unc; /* Uncertainty Region */
double **ptr_lims; /* Pointer to limits array */
double **ptr_out; /* Pointer to output coordinate data */
double **ptr_tmp; /* Pointer to base Frame coordinate data */
double *lbnd_unc; /* Lower bounds of uncertainty Region */
double *ubnd_unc; /* Upper bounds of uncertainty Region */
double lb; /* Base Frame axis lower bound */
double p; /* Input base Frame axis value */
double ub; /* Base Frame axis upper bound */
double wid; /* Half width of uncertainy Region */
int coord; /* Zero-based index for coordinates */
int ncoord_out; /* No. of coordinates per output point */
int ncoord_tmp; /* No. of coordinates per base Frame point */
int neg; /* Has the Region been negated? */
int npoint; /* No. of points */
int pass; /* Does this point pass the axis test? */
int point; /* Loop counter for points */
int setbad; /* Set the output point bad? */
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Obtain pointers to the Region and to the Interval. */
reg = (AstRegion *) this_mapping;
this = (AstInterval *) this_mapping;
/* If this Interval is equivalent to a Box, use the astTransform method of
the equivalent Box. */
box = Cache( this, status );
if( box ) {
result = astTransform( box, in, forward, out );
/* Otherwise, we use a new implementation appropriate for unbounded
intervals. */
} else {
/* Apply the parent mapping using the stored pointer to the Transform member
function inherited from the parent Region class. This function validates
all arguments and generates an output PointSet if necessary,
containing a copy of the input PointSet. */
result = (*parent_transform)( this_mapping, in, forward, out, status );
/* We will now extend the parent astTransform method by performing the
calculations needed to generate the output coordinate values. */
/* First use the encapsulated FrameSet to transform the supplied positions
from the current Frame in the encapsulated FrameSet (the Frame
represented by the Region), to the base Frame (the Frame in which the
Region is defined). This call also returns a pointer to the base Frame
of the encapsulated FrameSet. Note, the returned pointer may be a
clone of the "in" pointer, and so we must be carefull not to modify the
contents of the returned PointSet. */
pset_tmp = astRegTransform( reg, in, 0, NULL, NULL );
/* Determine the numbers of points and coordinates per point from the base
Frame PointSet and obtain pointers for accessing the base Frame and output
coordinate values. */
npoint = astGetNpoint( pset_tmp );
ncoord_tmp = astGetNcoord( pset_tmp );
ptr_tmp = astGetPoints( pset_tmp );
ncoord_out = astGetNcoord( result );
ptr_out = astGetPoints( result );
/* Get a pointer to the array of axis limits */
ptr_lims = astGetPoints( reg->points );
/* See if the Region is negated. */
neg = astGetNegated( reg );
/* Indicate we have not yet got the bounding box of the uncertainty
Region. */
lbnd_unc = NULL;
ubnd_unc = NULL;
unc = NULL;
/* Perform coordinate arithmetic. */
if ( astOK ) {
/* First deal with closed unnegated Intervals. */
/* ------------------------------------------- */
if( astGetClosed( reg ) ) {
if( !neg ) {
/* Loop round each point. */
for ( point = 0; point < npoint; point++ ) {
/* Assume this point is inside the Region. We change this flag when we find
the first axis for which the point does not pass the axis test. */
setbad = 0;
/* Loop round each base Frame axis */
Cache( this, status );
for ( coord = 0; coord < ncoord_tmp; coord++ ) {
p = ptr_tmp[ coord ][ point ];
lb = (this->lbnd)[ coord ];
ub = (this->ubnd)[ coord ];
/* If the limits are equal separate them slightly to give some tolerance. */
if( lb == ub ) {
/* If not yet done so, get the bounding box of the uncertainty Region in the
base Frame of the Interval */
if( !unc ) {
unc = astGetUncFrm( reg, AST__BASE );
lbnd_unc = astMalloc( sizeof( double)*(size_t) ncoord_tmp );
ubnd_unc = astMalloc( sizeof( double)*(size_t) ncoord_tmp );
astGetRegionBounds( unc, lbnd_unc, ubnd_unc );
}
/* Set the gap between the limits to be equal to the uincertainty on this
axis. */
if( astOK ) {
wid = 0.5*( ubnd_unc[ coord ] - lbnd_unc[ coord ] );
lb -= wid;
ub += wid;
}
}
/* Bad input points should always be bad in the output. */
if( p == AST__BAD ) {
setbad = 1;
break;
/* Does the current axis value pass the limits test for this axis? */
} else if( lb <= ub ) {
pass = ( lb <= p && p <= ub );
} else {
pass = ( p <= ub || lb <= p );
}
/* If this point does not pass the test for this axis, then indicate that
we should set the resulting output point bad and break since we now have
a definite value for the inside/outside flag. */
if( !pass ) {
setbad = 1;
break;
}
}
/* Set the axis values bad for this output point if required. */
if( setbad ) {
for ( coord = 0; coord < ncoord_out; coord++ ) {
ptr_out[ coord ][ point ] = AST__BAD;
}
}
}
/* Now deal with closed negated Intervals. */
/* --------------------------------------- */
} else {
/* Loop round each point. */
for ( point = 0; point < npoint; point++ ) {
/* Assume this point is outside the negated Region (i.e. inside the
unnegated Region). We change this flag when we find the first axis for
which the point passes the axis test. */
setbad = 1;
/* Loop round each base Frame axis */
Cache( this, status );
for ( coord = 0; coord < ncoord_tmp; coord++ ) {
p = ptr_tmp[ coord ][ point ];
lb = (this->lbnd)[ coord ];
ub = (this->ubnd)[ coord ];
/* Bad input points should always be bad in the output. */
if( p == AST__BAD ) {
setbad = 1;
break;
/* Does the current axis value pass the limits test for this axis? */
} else if( lb <= ub ) {
pass = ( p <= lb || ub <= p );
} else {
pass = ( ub <= p && p <= lb );
}
/* If this point passes the test for this axis, then indicate that we should
not set the resulting output point bad and break since we now have a
definite value for the inside/outside flag. */
if( pass ) {
setbad = 0;
break;
}
}
/* Set the axis values bad for this output point if required. */
if( setbad ) {
for ( coord = 0; coord < ncoord_out; coord++ ) {
ptr_out[ coord ][ point ] = AST__BAD;
}
}
}
}
/* Now deal with open unnegated Intervals. */
/* --------------------------------------- */
} else {
if( !neg ) {
/* Loop round each point. */
for ( point = 0; point < npoint; point++ ) {
/* Assume this point is inside the Region. We change this flag when we find
the first axis for which the point does not pass the axis test. */
setbad = 0;
/* Loop round each base Frame axis */
Cache( this, status );
for ( coord = 0; coord < ncoord_tmp; coord++ ) {
p = ptr_tmp[ coord ][ point ];
lb = (this->lbnd)[ coord ];
ub = (this->ubnd)[ coord ];
/* Bad input points should always be bad in the output. */
if( p == AST__BAD ) {
setbad = 1;
break;
/* Does the current axis value pass the limits test for this axis? */
} else if( lb <= ub ) {
pass = ( lb < p && p < ub );
} else {
pass = ( p < ub || lb < p );
}
/* If this point does not pass the test for this axis, then indicate that
we should set the resulting output point bad and break since we now have
a definite value for the inside/outside flag. */
if( !pass ) {
setbad = 1;
break;
}
}
/* Set the axis values bad for this output point if required. */
if( setbad ) {
for ( coord = 0; coord < ncoord_out; coord++ ) {
ptr_out[ coord ][ point ] = AST__BAD;
}
}
}
/* Now deal with open negated Intervals. */
/* ------------------------------------- */
} else {
/* Loop round each point. */
for ( point = 0; point < npoint; point++ ) {
/* Assume this point is outside the negated Region (i.e. inside the
unnegated Region). We change this flag when we find the first axis for
which the point passes the axis test. */
setbad = 1;
/* Loop round each base Frame axis */
Cache( this, status );
for ( coord = 0; coord < ncoord_tmp; coord++ ) {
p = ptr_tmp[ coord ][ point ];
lb = (this->lbnd)[ coord ];
ub = (this->ubnd)[ coord ];
/* If the limits are equal separate them slightly to give some tolerance. */
if( lb == ub ) {
/* If not yet done so, get the bounding box of the uncertainty Region in the
base Frame of the Interval */
if( !unc ) {
unc = astGetUncFrm( reg, AST__BASE );
lbnd_unc = astMalloc( sizeof( double)*(size_t) ncoord_tmp );
ubnd_unc = astMalloc( sizeof( double)*(size_t) ncoord_tmp );
astGetRegionBounds( unc, lbnd_unc, ubnd_unc );
}
/* Set the gap between the limits to be equal to the uincertainty on this
axis. */
if( astOK ) {
wid = 0.5*( ubnd_unc[ coord ] - lbnd_unc[ coord ] );
lb -= wid;
ub += wid;
}
}
/* Bad input points should always be bad in the output. */
if( p == AST__BAD ) {
setbad = 1;
break;
/* Does the current axis value pass the limits test for this axis? */
} else if( lb <= ub ) {
pass = ( p < lb || ub < p );
} else {
pass = ( ub < p && p < lb );
}
/* If this point passes the test for this axis, then indicate that we should
not set the resulting output point bad and break since we now have a
definite value for the inside/outside flag. */
if( pass ) {
setbad = 0;
break;
}
}
/* Set the axis values bad for this output point if required. */
if( setbad ) {
for ( coord = 0; coord < ncoord_out; coord++ ) {
ptr_out[ coord ][ point ] = AST__BAD;
}
}
}
}
}
}
/* Free resources */
pset_tmp = astAnnul( pset_tmp );
if( lbnd_unc ) lbnd_unc = astFree( lbnd_unc );
if( ubnd_unc ) ubnd_unc = astFree( ubnd_unc );
if( unc ) unc = astAnnul( unc );
}
/* Annul the result if an error has occurred. */
if( !astOK ) result = astAnnul( result );
/* Return a pointer to the output PointSet. */
return result;
}
/* 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). */
/* Copy constructor. */
/* ----------------- */
static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
/*
* Name:
* Copy
* Purpose:
* Copy constructor for Interval objects.
* Type:
* Private function.
* Synopsis:
* void Copy( const AstObject *objin, AstObject *objout, int *status )
* Description:
* This function implements the copy constructor for Region 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: */
AstInterval *in; /* Pointer to input Interval */
AstInterval *out; /* Pointer to output Interval */
size_t nb; /* Number of bytes in limits array */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain pointers to the input and output Intervals. */
in = (AstInterval *) objin;
out = (AstInterval *) objout;
/* For safety, first clear any references to the input memory from
the output Interval. */
out->box = NULL;
out->lbnd = NULL;
out->ubnd = NULL;
/* Note the number of bytes in each limits array */
nb = sizeof( double )*(size_t) astGetNin( ((AstRegion *) in)->frameset );
/* Copy dynamic memory contents */
if( in->box ) out->box = astCopy( in->box );
out->lbnd = astStore( NULL, in->lbnd, nb );
out->ubnd = astStore( NULL, in->ubnd, nb );
}
/* Destructor. */
/* ----------- */
static void Delete( AstObject *obj, int *status ) {
/*
* Name:
* Delete
* Purpose:
* Destructor for Interval objects.
* Type:
* Private function.
* Synopsis:
* void Delete( AstObject *obj, int *status )
* Description:
* This function implements the destructor for Interval 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: */
AstInterval *this; /* Pointer to Interval */
/* Obtain a pointer to the Interval structure. */
this = (AstInterval *) obj;
/* Annul all resources. */
if( this->box ) this->box = astAnnul( this->box );
this->lbnd = astFree( this->lbnd );
this->ubnd = astFree( this->ubnd );
}
/* Dump function. */
/* -------------- */
static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
/*
* Name:
* Dump
* Purpose:
* Dump function for Interval 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 Interval class to an output Channel.
* Parameters:
* this
* Pointer to the Interval 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: */
AstInterval *this; /* Pointer to the Interval structure */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the Interval structure. */
this = (AstInterval *) this_object;
/* Write out values representing the instance variables for the
Interval class. Accompany these with appropriate comment strings,
possibly depending on the values being written.*/
/* In the case of attributes, we first use the appropriate (private)
Test... member function to see if they are set. If so, we then use
the (private) Get... function to obtain the value to be written
out.
For attributes which are not set, we use the astGet... method to
obtain the value instead. This will supply a default value
(possibly provided by a derived class which over-rides this method)
which is more useful to a human reader as it corresponds to the
actual default attribute value. Since "set" will be zero, these
values are for information only and will not be read back. */
/* There are no values to write, so return without further action. */
}
/* Standard class functions. */
/* ========================= */
/* Implement the astIsAInterval and astCheckInterval functions using the macros
defined for this purpose in the "object.h" header file. */
astMAKE_ISA(Interval,Region)
astMAKE_CHECK(Interval)
AstInterval *astInterval_( void *frame_void, const double lbnd[],
const double ubnd[], AstRegion *unc,
const char *options, int *status, ...) {
/*
*++
* Name:
c astInterval
f AST_INTERVAL
* Purpose:
* Create a Interval.
* Type:
* Public function.
* Synopsis:
c #include "interval.h"
c AstInterval *astInterval( AstFrame *frame, const double lbnd[],
c const double ubnd[], AstRegion *unc,
c const char *options, ... )
f RESULT = AST_INTERVAL( FRAME, LBND, UBND, UNC, OPTIONS, STATUS )
* Class Membership:
* Interval constructor.
* Description:
* This function creates a new Interval and optionally initialises its
* attributes.
*
* A Interval is a Region which represents upper and/or lower limits on
* one or more axes of a Frame. For a point to be within the region
* represented by the Interval, the point must satisfy all the
* restrictions placed on all the axes. The point is outside the region
* if it fails to satisfy any one of the restrictions. Each axis may have
* either an upper limit, a lower limit, both or neither. If both limits
* are supplied but are in reverse order (so that the lower limit is
* greater than the upper limit), then the interval is an excluded
* interval, rather than an included interval.
*
* At least one axis limit must be supplied.
*
* Note, The Interval class makes no allowances for cyclic nature of
* some coordinate systems (such as SkyFrame coordinates). A Box
* should usually be used in these cases since this requires the user
* to think about suitable upper and lower limits,
* Parameters:
c frame
f FRAME = INTEGER (Given)
* A pointer to the Frame in which the region is defined. A deep
* copy is taken of the supplied Frame. This means that any
* subsequent changes made to the Frame using the supplied pointer
* will have no effect the Region.
c lbnd
f LBND( * ) = DOUBLE PRECISION (Given)
c An array of double, with one element for each Frame axis
f An array with one element for each Frame axis
* (Naxes attribute) containing the lower limits on each axis.
* Set a value to AST__BAD to indicate that the axis has no lower
* limit.
c ubnd
f UBND( * ) = DOUBLE PRECISION (Given)
c An array of double, with one element for each Frame axis
f An array with one element for each Frame axis
* (Naxes attribute) containing the upper limits on each axis.
* Set a value to AST__BAD to indicate that the axis has no upper
* limit.
c unc
f UNC = INTEGER (Given)
* An optional pointer to an existing Region which specifies the
* uncertainties associated with the boundary of the Interval being created.
* The uncertainty in any point on the boundary of the Interval is found by
* shifting the supplied "uncertainty" Region so that it is centred at
* the boundary point being considered. The area covered by the
* shifted uncertainty Region then represents the uncertainty in the
* boundary position. The uncertainty is assumed to be the same for
* all points.
*
* If supplied, the uncertainty Region must be of a class for which
* all instances are centro-symetric (e.g. Box, Circle, Ellipse, etc.)
* or be a Prism containing centro-symetric component Regions. A deep
* copy of the supplied Region will be taken, so subsequent changes to
* the uncertainty Region using the supplied pointer will have no
* effect on the created Interval. Alternatively,
f a null Object pointer (AST__NULL)
c a NULL Object pointer
* may be supplied, in which case a default uncertainty is used
* equivalent to a box 1.0E-6 of the size of the Interval being created.
*
* The uncertainty Region has two uses: 1) when the
c astOverlap
f AST_OVERLAP
* function compares two Regions for equality the uncertainty
* Region is used to determine the tolerance on the comparison, and 2)
* when a Region is mapped into a different coordinate system and
* subsequently simplified (using
c astSimplify),
f AST_SIMPLIFY),
* the uncertainties are used to determine if the transformed boundary
* can be accurately represented by a specific shape of Region.
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 Interval. 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 Interval. 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 astInterval()
f AST_INTERVAL = INTEGER
* A pointer to the new Interval.
* Notes:
* - A null Object pointer (AST__NULL) will be returned if this
c function is invoked with the AST error status set, or if it
f function is invoked with STATUS set to an error value, or if it
* should fail for any reason.
* Status Handling:
* The protected interface to this function includes an extra
* parameter at the end of the parameter list descirbed above. This
* parameter is a pointer to the integer inherited status
* variable: "int *status".
*--
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstFrame *frame; /* Pointer to Frame structure */
AstInterval *new; /* Pointer to new Interval */
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;
/* Obtain and validate a pointer to the supplied Frame structure. */
frame = astCheckFrame( frame_void );
/* Initialise the Interval, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitInterval( NULL, sizeof( AstInterval ), !class_init,
&class_vtab, "Interval", frame, lbnd, ubnd, unc );
/* 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 Interval'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 Interval. */
return new;
}
AstInterval *astIntervalId_( void *frame_void, const double lbnd[],
const double ubnd[], void *unc_void,
const char *options, ... ) {
/*
* Name:
* astIntervalId_
* Purpose:
* Create a Interval.
* Type:
* Private function.
* Synopsis:
* #include "interval.h"
* AstInterval *astIntervalId_( AstFrame *frame, const double lbnd[],
* const double ubnd[], AstRegion *unc,
* const char *options, ... )
* Class Membership:
* Interval constructor.
* Description:
* This function implements the external (public) interface to the
* astInterval constructor function. It returns an ID value (instead
* of a true C pointer) to external users, and must be provided
* because astInterval_ 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 astInterval_ 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 astInterval_.
* Returned Value:
* The ID value associated with the new Interval.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstFrame *frame; /* Pointer to Frame structure */
AstInterval *new; /* Pointer to new Interval */
AstRegion *unc; /* Pointer to Region structure */
va_list args; /* Variable argument list */
int *status; /* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(NULL);
/* Pointer to inherited status value */
/* Get a pointer to the inherited status value. */
status = astGetStatusPtr;
/* Check the global status. */
if ( !astOK ) return NULL;
/* Obtain a Frame pointer from the supplied ID and validate the
pointer to ensure it identifies a valid Frame. */
frame = astVerifyFrame( astMakePointer( frame_void ) );
/* Obtain a Region pointer from the supplied "unc" ID and validate the
pointer to ensure it identifies a valid Region . */
unc = unc_void ? astCheckRegion( astMakePointer( unc_void ) ) : NULL;
/* Initialise the Interval, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitInterval( NULL, sizeof( AstInterval ), !class_init, &class_vtab,
"Interval", frame, lbnd, ubnd, unc );
/* 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 Interval'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 Interval. */
return astMakeId( new );
}
AstInterval *astInitInterval_( void *mem, size_t size, int init, AstIntervalVtab *vtab,
const char *name, AstFrame *frame,
const double lbnd[], const double ubnd[],
AstRegion *unc, int *status ) {
/*
*+
* Name:
* astInitInterval
* Purpose:
* Initialise a Interval.
* Type:
* Protected function.
* Synopsis:
* #include "interval.h"
* AstInterval *astInitInterval( void *mem, size_t size, int init, AstIntervalVtab *vtab,
* const char *name, AstFrame *frame,
* const double lbnd[], const double ubnd[],
* AstRegion *unc )
* Class Membership:
* Interval initialiser.
* Description:
* This function is provided for use by class implementations to initialise
* a new Interval object. It allocates memory (if necessary) to accommodate
* the Interval plus any additional data associated with the derived class.
* It then initialises a Interval structure at the start of this memory. If
* the "init" flag is set, it also initialises the contents of a virtual
* function table for a Interval at the start of the memory passed via the
* "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory in which the Interval is to be initialised.
* This must be of sufficient size to accommodate the Interval data
* (sizeof(Interval)) 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 Interval (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 Interval
* structure, so a valid value must be supplied even if not required for
* allocating memory.
* init
* A logical flag indicating if the Interval'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 Interval.
* 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).
* frame
* A pointer to the Frame in which the region is defined.
* lbnd
* An array of double, with one element for each Frame axis
* (Naxes attribute) containing the lower limits on each axis.
* Set a value to AST__BAD to indicate that the axis has no lower
* limit. Upper and ower limits can be reversed to create an
* excluded interval rather than an included interval.
* ubnd
* An array of double, with one element for each Frame axis
* (Naxes attribute) containing the upper limits on each axis.
* Set a value to AST__BAD to indicate that the axis has no upper
* limit.
* unc
* A pointer to a Region which specifies the uncertainty in the
* supplied positions (all points on the boundary of the new Interval
* being initialised are assumed to have the same uncertainty). A NULL
* pointer can be supplied, in which case default uncertainties equal to
* 1.0E-6 of the dimensions of the new Interval's bounding box are used.
* If an uncertainty Region is supplied, it must be either a Box, a
* Circle or an Ellipse, and its encapsulated Frame must be related
* to the Frame supplied for parameter "frame" (i.e. astConvert
* should be able to find a Mapping between them). Two positions
* the "frame" Frame are considered to be co-incident if their
* uncertainty Regions overlap. The centre of the supplied
* uncertainty Region is immaterial since it will be re-centred on the
* point being tested before use. A deep copy is taken of the supplied
* Region.
* Returned Value:
* A pointer to the new Interval.
* 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 */
AstInterval *new; /* Pointer to new Interval */
AstPointSet *pset; /* PointSet to pass to Region initialiser */
double **ptr; /* Pointer to coords data in pset */
int i; /* Axis index */
int nc; /* No. of axes */
/* Check the global status. */
if ( !astOK ) return NULL;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(NULL);
/* If necessary, initialise the virtual function table. */
if ( init ) astInitIntervalVtab( &class_vtab, name );
/* Initialise. */
new = NULL;
/* Get the number of axis values required for each position. */
nc = astGetNaxes( frame );
/* Create a PointSet to hold the upper and lower bounds, and get pointers to
the data arrays. */
pset = astPointSet( 2, nc, "", status );
ptr = astGetPoints( pset );
if( astOK ) {
/* Copy the limits into the PointSet. */
for( i = 0; i < nc; i++ ) {
ptr[ i ][ 0 ] = lbnd[ i ];
ptr[ i ][ 1 ] = ubnd[ i ];
}
/* Initialise a Region structure (the parent class) as the first component
within the Interval structure, allocating memory if necessary. */
new = (AstInterval *) astInitRegion( mem, size, 0, (AstRegionVtab *) vtab,
name, frame, pset, unc );
if ( astOK ) {
/* Initialise the Interval data. */
/* ----------------------------- */
new->lbnd = NULL;
new->ubnd = NULL;
new->box = NULL;
new->stale = 1;
/* If an error occurred, clean up by deleting the new Interval. */
if ( !astOK ) new = astDelete( new );
}
}
/* Free resources. */
pset = astAnnul( pset );
/* Return a pointer to the new Interval. */
return new;
}
AstInterval *astLoadInterval_( void *mem, size_t size, AstIntervalVtab *vtab,
const char *name, AstChannel *channel, int *status ) {
/*
*+
* Name:
* astLoadInterval
* Purpose:
* Load a Interval.
* Type:
* Protected function.
* Synopsis:
* #include "interval.h"
* AstInterval *astLoadInterval( void *mem, size_t size, AstIntervalVtab *vtab,
* const char *name, AstChannel *channel )
* Class Membership:
* Interval loader.
* Description:
* This function is provided to load a new Interval 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
* Interval 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 a Interval at the start of the memory
* passed via the "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory into which the Interval is to be
* loaded. This must be of sufficient size to accommodate the
* Interval data (sizeof(Interval)) 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 Interval (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 Interval 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(AstInterval) is used instead.
* vtab
* Pointer to the start of the virtual function table to be
* associated with the new Interval. If this is NULL, a pointer
* to the (static) virtual function table for the Interval 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 "Interval" is used instead.
* Returned Value:
* A pointer to the new Interval.
* 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 */
AstInterval *new; /* Pointer to the new Interval */
/* 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 Interval. In this case the
Interval belongs to this class, so supply appropriate values to be
passed to the parent class loader (and its parent, etc.). */
if ( !vtab ) {
size = sizeof( AstInterval );
vtab = &class_vtab;
name = "Interval";
/* If required, initialise the virtual function table for this class. */
if ( !class_init ) {
astInitIntervalVtab( 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 Interval. */
new = astLoadRegion( mem, size, (AstRegionVtab *) 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, "Interval" );
/* Now read each individual data item from this list and use it to
initialise the appropriate instance variable(s) for this class. */
/* In the case of attributes, we first read the "raw" input value,
supplying the "unset" value as the default. If a "set" value is
obtained, we then use the appropriate (private) Set... member
function to validate and set the value properly. */
/* There are no values to read. */
/* ---------------------------- */
new->lbnd = NULL;
new->ubnd = NULL;
new->box = NULL;
new->stale = 1;
/* If an error occurred, clean up by deleting the new Interval. */
if ( !astOK ) new = astDelete( new );
}
/* Return the new Interval 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. */
void astIntervalPoints_( AstInterval *this, double *lbnd, double *ubnd,
int *status) {
if ( !astOK ) return;
(**astMEMBER(this,Interval,IntervalPoints))( this, lbnd, ubnd, status );
return;
}
ast-8.0.7/plot3d.c 0000664 0001750 0001750 00001055175 12610415012 010617 0000000 0000000 /*
*class++
* Name:
* Plot3D
* Purpose:
* Provide facilities for 3D graphical output.
* Constructor Function:
c astPlot3D
f AST_PLOT3D
* Description:
* A Plot3D is a specialised form of Plot that provides facilities
* for producing 3D graphical output, including fully annotated 3D
* coordinate grids. The base Frame in a Plot3D describes a 3-dimensional
* "graphical" coordinate system. The axes of this coordinate system are
* assumed to be right-handed (that is, if X appears horizontally to the
* right and Y vertically upwards, then Z is out of the screen towards
* the viewer), and are assumed to be equally scaled (that is, the same
* units are used to measure positions on each of the 3 axes). The upper
* and lower bounds of a volume within this graphical coordinate system
* is specified when the Plot3D is created, and all subsequent graphics
* are "drawn" in this volume.
*
* The Plot3D class does not itself include any ability to draw on a
* graphics device. Instead it calls upon function in an externally
* supplied module (the "grf3d" module) to do the required drawing.
* A module should be written that implements the functions of the
* grf3d interface using the facilities of a specific graphics system
* This module should then be linked into the application so that the
* Plot3D class can use its functions (see the description of the
* ast_link commands for details of how to do this). The grf3d interface
* defines a few simple functions for drawing primitives such as straight
* lines, markers and character strings. These functions all accept
* positions in the 3D graphics coordinate system (the base Frame of the
* Plot3D), and so the grf3d module must also manage the projection of
* these 3D coordinates onto the 2D viewing surface, including the choice
* of "eye"/"camera" position, direction of viewing, etc. The AST
* library includes a sample implementation of the grf3d interface
* based on the PGPLOT graphics system (see file grf3d_pgplot.c). This
* implementation also serves to document the grf3d interface itself and
* should be consulted for details before writing a new implementation.
*
* The current Frame of a Plot3D describes a "physical" 3-dimensional
* coordinate system, which is the coordinate system in which plotting
* operations are specified when invoking the methods of the Plot3D
* class. The results of each plotting operation are automatically
* transformed into 3D graphical coordinates before being plotted
* using the facilities of the grf3d module linked into the application.
* Note, at least one of the three axes of the current Frame must be
* independent of the other two current Frame axes.
*
* You may select different physical coordinate systems in which to
* plot (including the native graphical coordinate system itself)
* by selecting different Frames as the current Frame of a Plot3D,
* using its Current attribute.
*
* Like any FrameSet, a Plot3D may also be used as a Frame. In this
* case, it behaves like its current Frame, which describes the
* physical coordinate system.
*
* When used as a Mapping, a Plot3D describes the inter-relation
* between 3D graphical coordinates (its base Frame) and 3D physical
* coordinates (its current Frame).
*
* Although the Plot3D class inherits from the Plot class, several of
* the facilities of the Plot class are not available in the Plot3D
* class, and an error will be reported if any attempt is made to use
* them. Specifically, the Plot3D class does not support clipping
* using the
* astClip function.
f AST_CLIP routine.
* Nor does it support the specification of graphics primitive functions
* at run-time using the
c astGrfSet, astGrfPop, astGrfPush and astGetGrfContext functions.
f AST_GRFSET, AST_GRFPOP, AST_GRFPUSH, and AST_GETGRFCONTEXT routines.
* Inheritance:
* The Plot3D class inherits from the Plot class.
* Attributes:
* In addition to those attributes common to all Plots, every
* Plot3D also has the following attributes:
*
* - Norm: Normal vector defining the 2D plane used for text and markers
* - RootCorner: Specifies which edges of the 3D box should be annotated.
*
* Some attributes of the Plot class refer to specific physical
* coordinate axes (e.g. Gap, LabelUp, DrawAxes, etc). For a basic
* Plot, the axis index must be 1 or 2, but for a Plot3D the axis index
* can be 1, 2 or 3.
*
* Certain Plot attributes are ignored by the Plot3D class (e.g. Edge,
* DrawTitle, TitleGap, etc). Consult the Plot attribute documentation
* for details. All other Plot attributes can be set for a specific
* plane of the 3-d plot by appending one of the strings "_XY", "_XZ"
* or "_YZ" to the end of the Plot attribute name. For instance,
* "Grid_YZ" refers to the "Grid" attribute for the plane spanning
* the second (Y) and third (Z) axes of the 3-d plot.
* Functions:
c The Plot3D class does not define any new functions beyond those
f The Plot3D class does not define any new routines beyond those
* which are applicable to all Plots. Note, however, that the
* following methods inherited from the Plot class cannot be used with
* a Plot3D and will report an error if called:
c - astBoundingBox, astClip, astCurve, astGenCurve,
c astGetGrfContext, astGrfPop, astGrfPush, astGrfSet, astGridLine,
c astPolyCurve.
f - AST_BOUNDINGBOX, AST_CLIP, AST_CURVE, AST_GENCURVE,
f AST_GETGRFCONTEXT, AST_GRFPOP, AST_GRFPUSH, AST_GRFSET,
f AST_GRIDLINE, AST_POLYCURVE.
* Copyright:
* Copyright (C) 2007 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
* .
* Authors:
* DSB: David S. Berry (Starlink)
* History:
* 6-JUN-2007 (DSB):
* Original version.
* 6-SEP-2007 (DSB):
* Re-code the astGrid function.
* 12-NOV-2007 (DSB):
* Clear up compiler warnings.
* 4-MAY-2012 (DSB):
* Avoid segvio in Grid if no ticks are drawn.
* 21-MAY-2012 (DSB):
* In astLoadPlot3D, do not call SetRootCorner as it requires an
* active graphics system to be present which may not yet have been
* established. Also establish the grf routines to be used.
* 5-OCT-2015 (DSB):
* Allow Plot attributes to be set for specific planes.
*class--
*/
/* Module Macros. */
/* ============== */
/* Set the name of the class we are implementing. This indicates to
the header files that define class interfaces that they should make
"protected" symbols available. */
#define astCLASS Plot3D
/* Macros which return the maximum and minimum of two values. */
#define MAX(aa,bb) ((aa)>(bb)?(aa):(bb))
#define MIN(aa,bb) ((aa)<(bb)?(aa):(bb))
/* Macro to check for equality of floating point values. We cannot
compare bad values directory because of the danger of floating point
exceptions, so bad values are dealt with explicitly. */
#define EQUAL(aa,bb) (((aa)==AST__BAD)?(((bb)==AST__BAD)?1:0):(((bb)==AST__BAD)?0:(fabs((aa)-(bb))<=1.0E5*MAX((fabs(aa)+fabs(bb))*DBL_EPSILON,DBL_MIN))))
/* Integers identifying the 3 plotting surfaces */
#define XY 1
#define XZ 2
#define YZ 3
/* Integers identifying the 8 corners of the graphics cube. */
#define LLL 0
#define ULL 1
#define LUL 2
#define UUL 3
#define LLU 4
#define ULU 5
#define LUU 6
#define UUU 7
/* Identify the 4 edges of a Plot */
#define LEFT 0
#define TOP 1
#define RIGHT 2
#define BOTTOM 3
/* A macros that returns a pointer to the Plot that spans a given plane. */
#define GET_PLOT(plane) ( \
( plane == XY ) ? this->plotxy : ( \
( plane == XZ ) ? this->plotxz : ( \
( plane == YZ ) ? this->plotyz : NULL ) ) )
/*
*
* Name:
* MAKE_CLEAR3
* Purpose:
* Implement a method to clear a single value in a multi-valued attribute.
* Type:
* Private macro.
* Synopsis:
* #include "plot3d.h"
* MAKE_CLEAR3(attr,component,assign,nval)
* Class Membership:
* Defined by the Plot3D class.
* Description:
* This macro expands to an implementation of a private member function of
* the form:
*
* static void Clear( AstPlot *this, int axis )
*
* and an external interface function of the form:
*
* void astClear_( AstPlot *this, int axis )
*
* which implement a method for clearing a single value in a specified
* multi-valued attribute for an axis of a Plot3D.
* Parameters:
* attr
* The name of the attribute to be cleared, as it appears in the function
* name (e.g. LabelAt in "astClearLabelAt").
* component
* The name of the class structure component that holds the attribute
* value.
* assign
* An expression that evaluates to the value to assign to the component
* to clear its value.
* nval
* Specifies the number of values in the multi-valued attribute. The
* "axis" values supplied to the created function should be in the
* range zero to (nval - 1).
* Notes:
* - To avoid problems with some compilers, you should not leave any white
* space around the macro arguments.
*
*/
/* Define the macro. */
#define MAKE_CLEAR3(attr,component,assign,nval) \
\
/* Private member function. */ \
/* ------------------------ */ \
static void Clear##attr( AstPlot3D *this, int axis, int *status ) { \
\
/* Check the global error status. */ \
if ( !astOK ) return; \
\
/* Validate the axis index. */ \
if( axis < 0 || axis >= nval ){ \
astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \
#attr " - it should be in the range 1 to %d.", status, \
"astClear" #attr, astGetClass( this ), \
axis + 1, nval ); \
\
/* Assign the "clear" value. */ \
} else { \
this->component[ axis ] = (assign); \
} \
} \
\
/* External interface. */ \
/* ------------------- */ \
void astClear##attr##_( AstPlot3D *this, int axis, int *status ) { \
\
/* Check the global error status. */ \
if ( !astOK ) return; \
\
/* Invoke the required method via the virtual function table. */ \
(**astMEMBER(this,Plot3D,Clear##attr))( this, axis, status ); \
}
/*
*
* Name:
* MAKE_GET3
* Purpose:
* Implement a method to get a single value in a multi-valued attribute.
* Type:
* Private macro.
* Synopsis:
* #include "plot3d.h"
* MAKE_GET3(attr,type,bad_value,assign,nval)
* Class Membership:
* Defined by the Plot3D class.
* Description:
* This macro expands to an implementation of a private member function of
* the form:
*
* static Get( AstPlot3D *this, int axis )
*
* and an external interface function of the form:
*
* astGet_( AstPlot3D *this, int axis )
*
* which implement a method for getting a single value from a specified
* multi-valued attribute for an axis of a Plot3D.
* Parameters:
* attr
* The name of the attribute whose value is to be obtained, as it
* appears in the function name (e.g. Label in "astGetLabel").
* type
* The C type of the attribute.
* bad_value
* A constant value to return if the global error status is set, or if
* the function fails.
* assign
* An expression that evaluates to the value to be returned. This can
* use the string "axis" to represent the zero-based value index.
* nval
* Specifies the number of values in the multi-valued attribute. The
* "axis" values supplied to the created function should be in the
* range zero to (nval - 1).
* Notes:
* - To avoid problems with some compilers, you should not leave any white
* space around the macro arguments.
*
*/
/* Define the macro. */
#define MAKE_GET3(attr,type,bad_value,assign,nval) \
\
/* Private member function. */ \
/* ------------------------ */ \
static type Get##attr( AstPlot3D *this, int axis, int *status ) { \
type result; /* Result to be returned */ \
\
/* Initialise */ \
result = (bad_value); \
\
/* Check the global error status. */ \
if ( !astOK ) return result; \
\
/* Validate the axis index. */ \
if( axis < 0 || axis >= nval ){ \
astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \
#attr " - it should be in the range 1 to %d.", status, \
"astGet" #attr, astGetClass( this ), \
axis + 1, nval ); \
\
/* Assign the result value. */ \
} else { \
result = (assign); \
} \
\
/* Check for errors and clear the result if necessary. */ \
if ( !astOK ) result = (bad_value); \
\
/* Return the result. */ \
return result; \
} \
/* External interface. */ \
/* ------------------- */ \
type astGet##attr##_( AstPlot3D *this, int axis, int *status ) { \
\
/* Check the global error status. */ \
if ( !astOK ) return (bad_value); \
\
/* Invoke the required method via the virtual function table. */ \
return (**astMEMBER(this,Plot3D,Get##attr))( this, axis, status ); \
}
/*
*
* Name:
* MAKE_SET3
* Purpose:
* Implement a method to set a single value in a multi-valued attribute
* for a Plot3D.
* Type:
* Private macro.
* Synopsis:
* #include "plot3d.h"
* MAKE_SET3(attr,type,component,assign,nval)
* Class Membership:
* Defined by the Plot3D class.
* Description:
* This macro expands to an implementation of a private member function of
* the form:
*
* static void Set( AstPlot3D *this, int axis, value )
*
* and an external interface function of the form:
*
* void astSet_( AstPlot3D *this, int axis, value )
*
* which implement a method for setting a single value in a specified
* multi-valued attribute for a Plot3D.
* Parameters:
* attr
* The name of the attribute to be set, as it appears in the function
* name (e.g. LabelAt in "astSetLabelAt").
* type
* The C type of the attribute.
* component
* The name of the class structure component that holds the attribute
* value.
* assign
* An expression that evaluates to the value to be assigned to the
* component.
* nval
* Specifies the number of values in the multi-valued attribute. The
* "axis" values supplied to the created function should be in the
* range zero to (nval - 1). If a value of 0 is supplied, the
* value of the Plot3D's Nin attribute is used instead.
* Notes:
* - To avoid problems with some compilers, you should not leave any white
* space around the macro arguments.
*-
*/
/* Define the macro. */
#define MAKE_SET3(attr,type,component,assign,nval) \
\
/* Private member function. */ \
/* ------------------------ */ \
static void Set##attr( AstPlot3D *this, int axis, type value, int *status ) { \
\
/* Check the global error status. */ \
if ( !astOK ) return; \
\
/* Validate the axis index. */ \
if( axis < 0 || axis >= nval ){ \
astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \
#attr " - it should be in the range 1 to %d.", status, \
"astSet" #attr, astGetClass( this ), \
axis + 1, nval ); \
\
/* Store the new value in the structure component. */ \
} else { \
this->component[ axis ] = (assign); \
} \
} \
\
/* External interface. */ \
/* ------------------- */ \
void astSet##attr##_( AstPlot3D *this, int axis, type value, int *status ) { \
\
/* Check the global error status. */ \
if ( !astOK ) return; \
\
/* Invoke the required method via the virtual function table. */ \
(**astMEMBER(this,Plot3D,Set##attr))( this, axis, value, status ); \
}
/*
*
* Name:
* MAKE_TEST3
* Purpose:
* Implement a method to test if a single value has been set in a
* multi-valued attribute for a class.
* Type:
* Private macro.
* Synopsis:
* #include "plot3d.h"
* MAKE_TEST3(attr,assign,nval)
* Class Membership:
* Defined by the Plot3D class.
* Description:
* This macro expands to an implementation of a private member function of
* the form:
*
* static int Test( AstPlot3D *this, int axis )
*
* and an external interface function of the form:
*
* int astTest_( AstPlot3D *this, int axis )
*
* which implement a method for testing if a single value in a specified
* multi-valued attribute has been set for a class.
* Parameters:
* attr
* The name of the attribute to be tested, as it appears in the function
* name (e.g. LabelAt in "astTestLabelAt").
* assign
* An expression that evaluates to 0 or 1, to be used as the returned
* value. This can use the string "axis" to represent the zero-based
* index of the value within the attribute.
* nval
* Specifies the number of values in the multi-valued attribute. The
* "axis" values supplied to the created function should be in the
* range zero to (nval - 1).
* Notes:
* - To avoid problems with some compilers, you should not leave any white
* space around the macro arguments.
*-
*/
/* Define the macro. */
#define MAKE_TEST3(attr,assign,nval) \
\
/* Private member function. */ \
/* ------------------------ */ \
static int Test##attr( AstPlot3D *this, int axis, int *status ) { \
int result; /* Value to return */ \
\
/* Initialise */ \
result = 0; \
\
/* Check the global error status. */ \
if ( !astOK ) return result; \
\
/* Validate the axis index. */ \
if( axis < 0 || axis >= nval ){ \
astError( AST__AXIIN, "%s(%s): Index (%d) is invalid for attribute " \
#attr " - it should be in the range 1 to %d.", status, \
"astTest" #attr, astGetClass( this ), \
axis + 1, nval ); \
\
/* Assign the result value. */ \
} else { \
result = (assign); \
} \
\
/* Check for errors and clear the result if necessary. */ \
if ( !astOK ) result = 0; \
\
/* Return the result. */ \
return result; \
} \
/* External interface. */ \
/* ------------------- */ \
int astTest##attr##_( AstPlot3D *this, int axis, int *status ) { \
\
/* Check the global error status. */ \
if ( !astOK ) return 0; \
\
/* Invoke the required method via the virtual function table. */ \
return (**astMEMBER(this,Plot3D,Test##attr))( this, axis, status ); \
}
/*
*
* Name:
* MAKE_CLEAR2
* Purpose:
* Implement a method to clear an element-specific attribute inherited
* from the Plot class.
* Type:
* Private macro.
* Synopsis:
* #include "plot3d.h"
* MAKE_CLEAR2(attr)
* Class Membership:
* Defined by the Plot3d class.
* Description:
* This macro expands to an implementation of a private member function of
* the form:
*
* static void Clear( AstPlot *this, int element )
*
* which implements a method for clearing one of the element-specific
* attributes (e.g. Size, Colour, Width, etc) inherited from the
* parent Plot class.
* Parameters:
* attr
* The name of the attribute to be cleared, as it appears in the function
* name (e.g. LabelAt in "astClearSize").
* Notes:
* - To avoid problems with some compilers, you should not leave any white
* space around the macro arguments.
*
*/
/* Define the macro. */
#define MAKE_CLEAR2(attr) \
\
/* Private member function. */ \
/* ------------------------ */ \
static void Clear##attr( AstPlot *this_plot, int element, int *status ) { \
\
/* Local Variables: */ \
AstPlot3D *this; \
int axis3d; \
int elem2d1; \
int elem2d2; \
\
/* Check the global error status. */ \
if ( !astOK ) return; \
\
/* Clear the attribute value in the parent Plot structure. */ \
(*parent_clear##attr)( this_plot, element, status ); \
\
/* If OK, clear the attribute in the encapsulated Plots. */ \
if( astOK ) { \
this = (AstPlot3D *) this_plot; \
\
/* Get the zero-based index of the 3D axis to which the supplied element \
refers. Use an index of -1 to indicate that the element does not \
relate to a specific axis. Also get the corresponding elements to use \
with the two Plots that share the specified 3D axis. */ \
axis3d = Element2D( this, element, &elem2d1, &elem2d2, status ); \
\
/* If the element is not axis-specific, clear the attribute value in all \
three plots. */ \
if( axis3d == -1 ) { \
astClear##attr( this->plotxy, element ); \
astClear##attr( this->plotxz, element ); \
astClear##attr( this->plotyz, element ); \
\
/* Otherwise, clear the attribute in the two plots that share the \
specified 3D axis. */ \
} else { \
astClear##attr( GET_PLOT(this->axis_plot1[ axis3d ]), elem2d1 ); \
astClear##attr( GET_PLOT(this->axis_plot2[ axis3d ]), elem2d2 ); \
} \
} \
}
/*
*
* Name:
* MAKE_SET2
* Purpose:
* Implement a method to set an element-specific attribute inherited
* from the Plot class.
* Type:
* Private macro.
* Synopsis:
* #include "plot3d.h"
* MAKE_SET2(attr,type)
* Class Membership:
* Defined by the Plot3d class.
* Description:
* This macro expands to an implementation of a private member function of
* the form:
*
* static void Set( AstPlot *this, int element, type value )
*
* which implements a method for setting one of the element-specific
* attributes (e.g. Size, Colour, Width, etc) inherited from the
* parent Plot class.
* Parameters:
* attr
* The name of the attribute to be cleared, as it appears in the function
* name (e.g. LabelAt in "astClearSize").
* type
* The attribute data type.
* Notes:
* - To avoid problems with some compilers, you should not leave any white
* space around the macro arguments.
*
*/
/* Define the macro. */
#define MAKE_SET2(attr,type) \
\
/* Private member function. */ \
/* ------------------------ */ \
static void Set##attr( AstPlot *this_plot, int element, type value, int *status ) { \
\
/* Local Variables: */ \
AstPlot3D *this; \
int axis3d; \
int elem2d1; \
int elem2d2; \
\
/* Check the global error status. */ \
if ( !astOK ) return; \
\
/* Set the attribute value in the parent Plot structure. */ \
(*parent_set##attr)( this_plot, element, value, status ); \
\
/* If OK, set the attribute in the encapsulated Plots. */ \
if( astOK ) { \
this = (AstPlot3D *) this_plot; \
\
/* Get the zero-based index of the 3D axis to which the supplied element \
refers. Use an index of -1 to indicate that the element does not \
relate to a specific axis. Also get the corresponding elements to use \
with the two Plots that share the specified 3D axis. */ \
axis3d = Element2D( this, element, &elem2d1, &elem2d2, status ); \
\
/* If the element is not axis-specific, clear the attribute value in all \
three plots. */ \
if( axis3d == -1 ) { \
astSet##attr( this->plotxy, element, value ); \
astSet##attr( this->plotxz, element, value ); \
astSet##attr( this->plotyz, element, value ); \
\
/* Otherwise, clear the attribute in the two plots that share the \
specified 3D axis. */ \
} else { \
astSet##attr( GET_PLOT(this->axis_plot1[ axis3d ]), elem2d1, value ); \
astSet##attr( GET_PLOT(this->axis_plot2[ axis3d ]), elem2d2, value ); \
} \
} \
}
/*
*
* Name:
* MAKE_CLEAR1
* Purpose:
* Implement a method to clear an attribute inherited from the Plot class.
* Type:
* Private macro.
* Synopsis:
* #include "plot3d.h"
* MAKE_CLEAR1(attr)
* Class Membership:
* Defined by the Plot3d class.
* Description:
* This macro expands to an implementation of a private member function of
* the form:
*
* static void Clear( AstPlot *this )
*
* which implements a method for clearing a Plot3D attribute inherited
* from the parent Plot class. It clears the attribute in all three
* plots encapsulated within the Plot3D.
* Parameters:
* attr
* The name of the attribute to be cleared, as it appears in the function
* name (e.g. LabelAt in "astClearLabelAt").
* Notes:
* - To avoid problems with some compilers, you should not leave any white
* space around the macro arguments.
*
*/
/* Define the macro. */
#define MAKE_CLEAR1(attr) \
\
/* Private member function. */ \
/* ------------------------ */ \
static void Clear##attr( AstPlot *this_plot, int *status ) { \
\
/* Local Variables: */ \
AstPlot3D *this; \
\
/* Check the global error status. */ \
if ( !astOK ) return; \
\
/* Clear the attribute value in the parent Plot structure. */ \
(*parent_clear##attr)( this_plot, status ); \
\
/* If OK, clear the attribute in all three of the encapsulated Plots. */ \
if( astOK ) { \
this = (AstPlot3D *) this_plot; \
astClear##attr( this->plotxy ); \
astClear##attr( this->plotxz ); \
astClear##attr( this->plotyz ); \
} \
}
/*
*
* Name:
* MAKE_SET1
* Purpose:
* Implement a method to set an attribute inherited from the Plot class.
* Type:
* Private macro.
* Synopsis:
* #include "plot3d.h"
* MAKE_SET1(attr,type)
* Class Membership:
* Defined by the Plot3d class.
* Description:
* This macro expands to an implementation of a private member function of
* the form:
*
* static void Set( AstPlot *this, type value )
*
* which implements a method for setting a Plot3D attribute inherited
* from the parent Plot class. It sets the attribute in all three
* plots encapsulated within the Plot3D.
* Parameters:
* attr
* The name of the attribute to be set, as it appears in the function
* name (e.g. LabelAt in "astSetLabelAt").
* type
* The C data type for the attribute value.
* Notes:
* - To avoid problems with some compilers, you should not leave any white
* space around the macro arguments.
*
*/
/* Define the macro. */
#define MAKE_SET1(attr,type) \
\
/* Private member function. */ \
/* ------------------------ */ \
static void Set##attr( AstPlot *this_plot, type value, int *status ) { \
\
/* Local Variables: */ \
AstPlot3D *this; \
\
/* Check the global error status. */ \
if ( !astOK ) return; \
\
/* Set the attribute value in the parent Plot structure. */ \
(*parent_set##attr)( this_plot, value, status ); \
\
/* If OK, set the attribute in all three of the encapsulated Plots. */ \
if( astOK ) { \
this = (AstPlot3D *) this_plot; \
astSet##attr( this->plotxy, value ); \
astSet##attr( this->plotxz, value ); \
astSet##attr( this->plotyz, value ); \
} \
}
/*
*
* Name:
* MAKE_CLEAR
* Purpose:
* Implement a method to clear an attribute inherited from the Plot class.
* Type:
* Private macro.
* Synopsis:
* #include "plot3d.h"
* MAKE_CLEAR(attr,whichplots)
* Class Membership:
* Defined by the Plot3d class.
* Description:
* This macro expands to an implementation of a private member function of
* the form:
*
* static void Clear( AstPlot *this, int axis )
*
* which implements a method for clearing an axis specific Plot3D
* attribute inherited from the parent Plot class.
* Parameters:
* attr
* The name of the attribute to be cleared, as it appears in the function
* name (e.g. LabelAt in "astClearLabelAt").
* whichplots
* A value indicating which Plots should be affected. A negative value
* means "all threee plots", a value of zero means "just the two plots
* that touch at the specified axis in 3D space", a positive value
* means "just the Plot that is used to label th 3D axis."
* Notes:
* - To avoid problems with some compilers, you should not leave any white
* space around the macro arguments.
*
*/
/* Define the macro. */
#define MAKE_CLEAR(attr,whichplots) \
\
/* Private member function. */ \
/* ------------------------ */ \
static void Clear##attr( AstPlot *this_plot, int axis, int *status ) { \
\
/* Local Variables: */ \
AstPlot *plot; \
AstPlot3D *this; \
int axis2d; \
\
/* Check the global error status. */ \
if ( !astOK ) return; \
\
/* Clear the attribute value in the parent Plot structure. This will \
validate the axis index. */ \
(*parent_clear##attr)( this_plot, axis, status ); \
\
/* If OK, clear the attribute for the relevant axis, or axes, of the Plots \
encapsulated inside the Plot3D. First get a pointer to the Plot3D \
structure. */ \
if( astOK ) { \
this = (AstPlot3D *) this_plot; \
\
/* If requested clear the attribute in all three Plots. */ \
if( whichplots < 0 ) { \
astClear##attr( this->plotxy, axis ); \
astClear##attr( this->plotxz, axis ); \
astClear##attr( this->plotyz, axis ); \
\
/* Each axis in 3D graphics space is described by two of the encapsulated \
Plots, but only one of these two Plots is used to generate labels for \
the axis. Now deal with cases where we are clearing the attribute \
value in both of the two Plots that describe the axis. */ \
} else if ( whichplots == 0 ) { \
if( axis == 0 ) { \
astClear##attr( this->plotxy, 0 ); \
astClear##attr( this->plotxz, 0 ); \
\
} else if( axis == 1 ) { \
astClear##attr( this->plotxy, 1 ); \
astClear##attr( this->plotyz, 0 ); \
\
} else { \
astClear##attr( this->plotxz, 1 ); \
astClear##attr( this->plotyz, 1 ); \
} \
\
/* Now deal with cases where we are clearing the attribute value only in \
the Plot that is used to label the axis. */ \
} else { \
plot = AxisPlot( this, axis, &axis2d, status ); \
astClear##attr( plot, axis2d ); \
} \
} \
}
/*
*
* Name:
* MAKE_GET
* Purpose:
* Implement a method to get the value of an attribute inherited from the
* Plot class.
* Type:
* Private macro.
* Synopsis:
* #include "plot.h"
* MAKE_GET(attr,type,bad_value)
* Class Membership:
* Defined by the Plot3D class.
* Description:
* This macro expands to an implementation of a private member function of
* the form:
*
* static Get( AstPlot *this, int axis )
*
* which implements a method for getting a value for an axis specific
* attribute for a Plot3D.
* Parameters:
* attr
* The name of the attribute whose value is to be obtained, as it
* appears in the function name (e.g. Label in "astGetLabel").
* type
* The C type of the attribute.
* bad_value
* A constant value to return if the global error status is set, or if
* the function fails.
* Notes:
* - To avoid problems with some compilers, you should not leave any white
* space around the macro arguments.
*
*/
/* Define the macro. */
#define MAKE_GET(attr,type,bad_value) \
\
/* Private member function. */ \
/* ------------------------ */ \
static type Get##attr( AstPlot *this_plot, int axis, int *status ) { \
\
/* Local Variables: */ \
AstPlot *plot; \
AstPlot3D *this; \
int axis2d; \
type result; \
\
/* Initialise */ \
result = (bad_value); \
\
/* Check the global error status. */ \
if ( !astOK ) return result; \
\
/* See if the attribute value is set in the parent Plot structure. If so, \
use the parent get method to get its value. */ \
if( astTest##attr( this_plot, axis ) ) { \
result = (*parent_get##attr)( this_plot, axis, status ); \
\
/* If the attribute value is not set in the parent Plot structure, get \
the default value from the Plot that is used to label the 3D axis. The \
parent test method called above will have reported an error if the axis \
index is invalid, so check astOK here. */ \
} else if( astOK ) { \
this = (AstPlot3D *) this_plot; \
plot = AxisPlot( this, axis, &axis2d, status ); \
result = astGet##attr( plot, axis2d ); \
} \
\
/* Return the result. */ \
return result; \
}
/*
*
* Name:
* MAKE_SET
* Purpose:
* Implement a method to set a value for an attribute inherited from the
* Plot class.
* Type:
* Private macro.
* Synopsis:
* #include "plot3d.h"
* MAKE_SET(attr,type,whichplots)
* Class Membership:
* Defined by the Plot3d class.
* Description:
* This macro expands to an implementation of a private member function of
* the form:
*
* static void Set( AstPlot *this, int axis, value )
*
* which implements a method for setting a value for an axis specific
* attribute inherited from the parent Plot class.
* Parameters:
* attr
* The name of the attribute to be set, as it appears in the function
* name (e.g. LabelAt in "astSetLabelAt").
* type
* The C type of the attribute.
* whichplots
* A value indicating which Plots should be affected. A negative value
* means "all threee plots", a value of zero means "just the two plots
* that touch at the specified axis in 3D space", a positive value
* means "just the Plot that is used to label the 3D axis."
* Notes:
* - To avoid problems with some compilers, you should not leave any white
* space around the macro arguments.
*-
*/
/* Define the macro. */
#define MAKE_SET(attr,type,whichplots) \
\
/* Private member function. */ \
/* ------------------------ */ \
static void Set##attr( AstPlot *this_plot, int axis, type value, int *status ) { \
\
/* Local Variables: */ \
AstPlot3D *this; \
AstPlot *plot; \
int axis2d; \
\
/* Check the global error status. */ \
if ( !astOK ) return; \
\
/* Set the supplied value in the parent Plot class. This will validate \
the axis index. */ \
(*parent_set##attr)( this_plot, axis, value, status ); \
\
/* If this went OK, also set the value for the appropriate axis of the \
appropriate encapsulated Plot(s). First get a pointer to the Plot3D \
structure. */ \
if( astOK ) { \
this = (AstPlot3D *) this_plot; \
\
/* If requested set the attribute in all three Plots. */ \
if( whichplots < 0 ) { \
astSet##attr( this->plotxy, axis, value ); \
astSet##attr( this->plotxz, axis, value ); \
astSet##attr( this->plotyz, axis, value ); \
\
/* Each axis in 3D graphics space is described by two of the encapsulated \
Plots, but only one of these two Plots is used to generate labels for \
the axis. First deal with cases where we are setting the attribute \
value in both of the two Plots that describe the axis. */ \
} else if( whichplots == 0 ) { \
if( axis == 0 ) { \
astSet##attr( this->plotxy, 0, value ); \
astSet##attr( this->plotxz, 0, value ); \
\
} else if( axis == 1 ) { \
astSet##attr( this->plotxy, 1, value ); \
astSet##attr( this->plotyz, 0, value ); \
\
} else { \
astSet##attr( this->plotxz, 1, value ); \
astSet##attr( this->plotyz, 1, value ); \
} \
\
/* Now deal with cases where we are setting the attribute value only in \
the Plot that is used to label the axis. */ \
} else { \
plot = AxisPlot( this, axis, &axis2d, status ); \
astSet##attr( plot, axis2d, value ); \
} \
} \
}
/* Header 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 "cmpframe.h" /* Compound Frames */
#include "cmpmap.h" /* Compound Mappings */
#include "unitmap.h" /* Unit mappings */
#include "permmap.h" /* Axis permutations */
#include "winmap.h" /* Scale and shift mappings */
#include "frame.h" /* Coordinate systems */
#include "frameset.h" /* Inter-related coordinate systems */
#include "keymap.h" /* Hash array */
#include "plot.h" /* Interface definition for parent class */
#include "plot3d.h" /* Interface definition for this class */
#include "grf3d.h" /* The grf3D interface */
#include "pointset.h" /* Sets of points */
#include "globals.h" /* Thread-safe global data access */
/* Error code definitions. */
/* ----------------------- */
#include "ast_err.h" /* AST error codes */
/* C header files. */
/* --------------- */
#include
#include
#include
#include
#include
#include
#include
/* 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 used or extended by this
class. */
static AstObject *(* parent_cast)( AstObject *, AstObject *, int * );
static void (* parent_removeframe)( AstFrameSet *, int, int * );
static int (* parent_getobjsize)( AstObject *, int * );
static int (* parent_equal)( AstObject *, AstObject *, int * );
static void (* parent_vset)( AstObject *, const char *, char **, va_list, int * );
static void (* parent_clear)( AstObject *, const char *, int * );
static void (* parent_clearcurrent)( AstFrameSet *, int * );
static void (* parent_setcurrent)( AstFrameSet *, int, int * );
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 * );
/* A FrameSet pointer that is used when calling astCast. */
static AstFrameSet *dummy_frameset = NULL;
#if defined(THREAD_SAFE)
static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * );
#endif
/* 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->GetAttrib_Buff[ 0 ] = 0;
/* Create the function that initialises global data for this module. */
astMAKE_INITGLOBALS(Plot3D)
/* Define macros for accessing each item of thread specific global data. */
#define class_init astGLOBAL(Plot3D,Class_Init)
#define class_vtab astGLOBAL(Plot3D,Class_Vtab)
#define getattrib_buff astGLOBAL(Plot3D,GetAttrib_Buff)
static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 );
#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 );
static pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER;
#define LOCK_MUTEX3 pthread_mutex_lock( &mutex3 );
#define UNLOCK_MUTEX3 pthread_mutex_unlock( &mutex3 );
/* If thread safety is not needed, declare and initialise globals at static
variables. */
#else
static char getattrib_buff[ 101 ];
/* Define the class virtual function table and its initialisation flag
as static variables. */
static AstPlot3DVtab class_vtab; /* Virtual function table */
static int class_init = 0; /* Virtual function table initialised? */
#define LOCK_MUTEX2
#define UNLOCK_MUTEX2
#define LOCK_MUTEX3
#define UNLOCK_MUTEX3
#endif
/* Prototypes for Private Member Functions. */
/* ======================================== */
static AstFrameSet *Fset3D( AstFrameSet *, int, int * );
static AstKeyMap *GetGrfContext( AstPlot *, int * );
static AstObject *Cast( AstObject *, AstObject *, int * );
static AstPlot *AxisPlot( AstPlot3D *, int, int *, int * );
static AstPointSet *ExtendTicks( AstPlot *, AstPointSet *, int * );
static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
static const char *RootCornerString( int, int * );
static int Attr3D( AstKeyMap *, int, double, double *, int, int * );
static int Border( AstPlot *, int * );
static int Element2D( AstPlot3D *, int, int *, int *, int * );
static int Equal( AstObject *, AstObject *, int * );
static int GetObjSize( AstObject *, int * );
static int Plot3DAttr( AstKeyMap *, int, double, double *, int );
static int Plot3DCap( AstKeyMap *, int, int );
static int Plot3DFlush( AstKeyMap * );
static int Plot3DLine( AstKeyMap *, int, const float *, const float * );
static int Plot3DMark( AstKeyMap *, int, const float *, const float *, int );
static int Plot3DQch( AstKeyMap *, float *, float * );
static int Plot3DScales( AstKeyMap *, float *, float * );
static int Plot3DText( AstKeyMap *, const char *, float, float, const char *, float, float );
static int Plot3DTxExt( AstKeyMap *, const char *, float, float, const char *, float, float, float *, float * );
static int RootCornerInt( const char *, int * );
static void BoundingBox( AstPlot *, float[2], float[2], int * );
static void ChangeRootCorner( AstPlot3D *, int, int, int * );
static void Clear( AstObject *, const char *, int * );
static void ClearCurrent( AstFrameSet *, int * );
static void Clip( AstPlot *, int, const double [], const double [], int * );
static void Copy( const AstObject *, AstObject *, int * );
static void CreatePlots( AstPlot3D *, AstFrameSet *, const float *, const double *, int * );
static void Curve( AstPlot *, const double [], const double [], int * );
static void Delete( AstObject *, int * );
static void Dump( AstObject *, AstChannel *, int * );
static void GenCurve( AstPlot *, AstMapping *, int * );
static void GrfPop( AstPlot *, int * );
static void GrfPush( AstPlot *, int * );
static void GrfSet( AstPlot *, const char *, AstGrfFun, int * );
static void Grid( AstPlot *, int * );
static void GridLine( AstPlot *, int, const double [], double, int * );
static void Mark( AstPlot *, int, int, int, const double *, int, int * );
static void PolyCurve( AstPlot *, int, int, int, const double *, int * );
static void RemoveFrame( AstFrameSet *, int, int * );
static void Set3DGrf( AstPlot3D *, AstPlot *, int, int * );
static void SetCurrent( AstFrameSet *, int, int * );
static void SetPlotAttr( AstPlot *, int, int[ 2 ], int * );
static void SetTickValues( AstPlot *, int, int, double *, int, double *, int * );
static void SplitFrameSet( AstFrameSet *, AstFrameSet **, int[2], int[2], AstFrameSet **, int[2], int[2], AstFrameSet **, int[2], int[2], int *, int * );
static void StoreAxisInfo( AstPlot3D *, int[2], int[2], int[2], int[2], int[2], int[2], int * );
static void Text( AstPlot *, const char *, const double [], const float [2], const char *, int * );
static void UpdatePlots( AstPlot3D *, int * );
static void VSet( AstObject *, const char *, char **, va_list, int * );
static const char *GetAttrib( AstObject *, const char *, int * );
static int TestAttrib( AstObject *, const char *, int * );
static void ClearAttrib( AstObject *, const char *, int * );
static void SetAttrib( AstObject *, const char *, int * );
#if defined(THREAD_SAFE)
static int ManageLock( AstObject *, int, int, AstObject **, int * );
#endif
/* Declare private member functions that access Plot3D attributes.
--------------------------------------------------------------*/
/* Axis independent... */
#define DECLARE_PLOT3D_ACCESSORS(attr,type) \
static type Get##attr(AstPlot3D *,int *); \
static void Set##attr(AstPlot3D *,type,int *); \
static void Clear##attr(AstPlot3D *,int *); \
static int Test##attr(AstPlot3D *,int *);
DECLARE_PLOT3D_ACCESSORS(RootCorner,int)
#undef DECLARE_PLOT3D_ACCESSORS
/* Axis specific... */
#define DECLARE_PLOT3D_ACCESSORS(attr,type) \
static type Get##attr(AstPlot3D *,int,int *); \
static void Set##attr(AstPlot3D *,int,type,int *); \
static void Clear##attr(AstPlot3D *,int,int *); \
static int Test##attr(AstPlot3D *,int,int *);
DECLARE_PLOT3D_ACCESSORS(Norm,double)
#undef DECLARE_PLOT3D_ACCESSORS
/* Declare private member functions that access axis-specific attributes
inherited from the Plot class. Also declare pointers to hold the parent
function pointers.
----------------------------------------------------------------------*/
#define DECLARE_PLOT_ACCESSORS(attr,type) \
static type Get##attr(AstPlot *,int,int *); \
static void Set##attr(AstPlot *,int,type,int *); \
static void Clear##attr(AstPlot *,int,int *); \
static type (*parent_get##attr)(AstPlot *,int,int *); \
static void (*parent_set##attr)(AstPlot *,int,type,int *); \
static void (*parent_clear##attr)(AstPlot *,int,int *);
DECLARE_PLOT_ACCESSORS(MinTick,int)
DECLARE_PLOT_ACCESSORS(Abbrev,int)
DECLARE_PLOT_ACCESSORS(Gap,double)
DECLARE_PLOT_ACCESSORS(LogGap,double)
DECLARE_PLOT_ACCESSORS(LogPlot,int)
DECLARE_PLOT_ACCESSORS(LogTicks,int)
DECLARE_PLOT_ACCESSORS(LogLabel,int)
DECLARE_PLOT_ACCESSORS(LabelUp,int)
DECLARE_PLOT_ACCESSORS(DrawAxes,int)
DECLARE_PLOT_ACCESSORS(LabelUnits,int)
DECLARE_PLOT_ACCESSORS(MinTickLen,double)
DECLARE_PLOT_ACCESSORS(MajTickLen,double)
DECLARE_PLOT_ACCESSORS(NumLab,int)
DECLARE_PLOT_ACCESSORS(NumLabGap,double)
DECLARE_PLOT_ACCESSORS(TextLab,int)
DECLARE_PLOT_ACCESSORS(TextLabGap,double)
#undef DECLARE_PLOT_ACCESSORS
/* Declare private member functions that access element-specific attributes
inherited from the Plot class. Also declare pointers to hold the parent
function pointers.
----------------------------------------------------------------------*/
#define DECLARE_PLOT_ACCESSORS(attr,type) \
static void Set##attr(AstPlot *,int,type,int *); \
static void Clear##attr(AstPlot *,int,int *); \
static void (*parent_set##attr)(AstPlot *,int,type,int *); \
static void (*parent_clear##attr)(AstPlot *,int,int *);
DECLARE_PLOT_ACCESSORS(Style,int)
DECLARE_PLOT_ACCESSORS(Font,int)
DECLARE_PLOT_ACCESSORS(Colour,int)
DECLARE_PLOT_ACCESSORS(Width,double)
DECLARE_PLOT_ACCESSORS(Size,double)
#undef DECLARE_PLOT_ACCESSORS
/* Declare private member functions that access attributes inherited from
the Plot class that do not need to override the Get method. This
includes attributes that are not axis-specific or that do not have
dynamic defaults. Also declare pointers to hold the parent function
pointers.
----------------------------------------------------------------------*/
#define DECLARE_PLOT_ACCESSORS(attr,type) \
static void Set##attr(AstPlot *,type,int *); \
static void Clear##attr(AstPlot *,int *); \
static void (*parent_set##attr)(AstPlot *,type,int *); \
static void (*parent_clear##attr)(AstPlot *,int *);
DECLARE_PLOT_ACCESSORS(Ink,int)
DECLARE_PLOT_ACCESSORS(Tol,double)
DECLARE_PLOT_ACCESSORS(Invisible,int)
DECLARE_PLOT_ACCESSORS(TickAll,int)
DECLARE_PLOT_ACCESSORS(ForceExterior,int)
DECLARE_PLOT_ACCESSORS(Border,int)
DECLARE_PLOT_ACCESSORS(Clip,int)
DECLARE_PLOT_ACCESSORS(ClipOp,int)
DECLARE_PLOT_ACCESSORS(Escape,int)
DECLARE_PLOT_ACCESSORS(Grid,int)
DECLARE_PLOT_ACCESSORS(Labelling,int)
#undef DECLARE_PLOT_ACCESSORS
/* Member functions. */
/* ================= */
static int Attr3D( AstKeyMap *grfconID, int attr, double value,
double *old_value, int prim, int *status ){
/*
* Name:
* Attr3D
* Purpose:
* Get or set the value of a 3D grf attribute.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* Attr3D( AstKeyMap *grfconID, int attr, double value, double *old_value,
* int prim, int *status )
* Class Membership:
* Plot3D member function.
* Description:
* This function gets or sets the current value of a specified graphics
* attribute in the parent Plot structure of a Plot3D. It forwards the
* call to the grf3D module being used by this Plot3D. It should be
* registered with the parent Plot using astGrfSet.
* Parameters:
* grfconID
* The Plot's GrfContext KeyMap.
* attr
* An integer value identifying the required attribute. This should
* be one of the symbolic values defined in grf.h.
* value
* A new value to store for the attribute. If this is AST__BAD
* no value is stored.
* old_value
* A pointer to a double in which to return the attribute value.
* If this is NULL, no value is returned.
* prim
* The sort of graphics primitive to be drawn with the new attribute.
* Identified by one of the values defined in grf.h.
* status
* Pointer to the inherited status variable.
* Returned Value:
* An integer value of 0 is returned if an error occurs, and 1 otherwise.
*/
/* Local Variables: */
int result;
/* Check the inherited status. */
if( !astOK ) return 0;
/* Since we are about to call an external function which may not be
thread safe, prevent any other thread from executing the following code
until the current thread has finished executing it. */
LOCK_MUTEX2;
/* Use the function in the external Grf3D module, selected at link-time
using ast_link options. */
result = astG3DAttr( attr, value, old_value, prim );
/* Allow the next thread to proceed. */
UNLOCK_MUTEX2;
/* Return the result. */
return result;
}
static AstPlot *AxisPlot( AstPlot3D *this, int axis3d, int *axis2d, int *status ){
/*
* Name:
* AxisPlot
* Purpose:
* Find the Plot used to label a 3D axis.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* AstPlot *AxisPlot( AstPlot3D *this, int axis3d, int *axis2d, int *status )
* Class Membership:
* Plot method.
* Description:
* This function returns a pointer to the encapsulated 2D Plot that
* is used to label the given 3D axis. It also returns the index
* of the labelled axis within the 2D Plot.
* Parameters:
* this
* Pointer to a Plot3D.
* axis3d
* A zero-based axis index within the Plot3D.
* axis2d
* Pointer to an int in which to put the index of the labelled axis
* within the returned 2D Plot.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the Plot used to label the 3D axis. Do not annul this
* pointer.
*-
*/
/* Local Variables: */
AstPlot *plot;
/* Check the global status. */
if( !astOK ) return NULL;
/* Return the required information form the Plot3D structure. */
plot = GET_PLOT( this->axis_plot1[ axis3d ] );
if( ! plot ) {
astError( AST__INTER, "AxisPlot(Plot3D): Illegal value %d "
"for axis3d (internal AST programming error).", status,
this->axis_plot1[ axis3d ] );
}
*axis2d = this->axis_index1[ axis3d ];
return plot;
}
static int Border( AstPlot *this_plot, int *status ){
/*
* Name:
* Border
* Purpose:
* Draw a border around valid regions of a Plot.
* Type:
* Private member function.
* Synopsis:
* #include "plot3d.h"
* int Border( AstPlot *this, int *status )
* Class Membership:
* Plot method (overrides the astBorder method inherited from the
* Plot class)
* Description:
* This function draws a (line) border around regions of the
* plotting area of a Plot which correspond to valid, unclipped
* physical coordinates. For example, when plotting using an
* all-sky map projection, this function could be used to draw the
* boundary of the celestial sphere when it is projected on to the
* plotting surface.
*
* If the entire plotting area contains valid, unclipped physical
* coordinates, then the boundary will just be a rectangular box
* around the edges of the plotting area.
* Parameters:
* this
* Pointer to the Plot.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Zero is returned if the plotting area is completely filled by
* valid, unclipped physical coordinates (so that only a
* rectangular box was drawn around the edge). Otherwise, one is
* returned.
* Notes:
* - The Plot3D implementation of this method, invokes the astBorder
* method on each of the three encapsulated Plots, and returns the
* logical OR of the three returned flags.
* - A value of zero will be returned if this function is invoked
* with the AST error status set, or if it should fail for any
* reason.
* - An error results if either the current Frame or the base Frame
* of the Plot is not 2-dimensional, or (for a Plot3D) 3-dimensional.
* - An error also results if the transformation between the base
* and current Frames of the Plot is not defined (i.e. the Plot's
* TranForward attribute is zero).
*/
/* Local Variables: */
AstPlot3D *this;
const char *class;
const char *method;
float x1;
float y1;
float z1;
float x[ 2 ];
float y[ 2 ];
float z[ 2 ];
int flag1;
int flag2;
int flag3;
int naxes;
int ok;
int result;
int root_corner;
/* Check the global error status. */
if ( !astOK ) return 0;
/* Get a pointer to the Plot3D structure. */
this = (AstPlot3D *) this_plot;
/* Store the current method, and the class of the supplied object for use
in error messages.*/
method = "astBorder";
class = astGetClass( this );
/* Check the base Frame of the Plot is 3-D. */
naxes = astGetNin( this );
if( naxes != 3 && astOK ){
astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base "
"Frame of the supplied %s is invalid - this number should "
"be 3.", status, method, class, naxes, class );
}
/* Check the current Frame of the Plot is 3-D. */
naxes = astGetNout( this );
if( naxes != 3 && astOK ){
astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the current "
"Frame of the supplied %s is invalid - this number should "
"be 3.", status, method, class, naxes, class );
}
/* Invoke the astBorder method on each of the three encapsulated Plots. */
flag1 = astBorder( this->plotxy );
flag2 = astBorder( this->plotxz );
flag3 = astBorder( this->plotyz );
/* If no bad values were encountered in any of the Plots, draw lines
along the remaining plot edges. */
result = ( flag1 || flag2 || flag3 );
if( !result ) {
/* The three remaining edges ot be drawn all meet at the corner
diagonally opposite the root corner. Get the root corner. */
root_corner = astGetRootCorner( this );
/* The (x0,y0,z0) position is the graphics coords at the corner
diagonally opposite the root corner. The x1, y1 and z1 values
are the graphics x, y and z values at the ends of the three
lines that remain to be drawn. */
if( root_corner & 1 ) {
x[ 0 ] = this->gbox[ 0 ];
x1 = this->gbox[ 3 ];
} else {
x[ 0 ] = this->gbox[ 3 ];
x1 = this->gbox[ 0 ];
}
if( root_corner & 2 ) {
y[ 0 ] = this->gbox[ 1 ];
y1 = this->gbox[ 4 ];
} else {
y[ 0 ] = this->gbox[ 4 ];
y1 = this->gbox[ 1 ];
}
if( root_corner & 4 ) {
z[ 0 ] = this->gbox[ 2 ];
z1 = this->gbox[ 5 ];
} else {
z[ 0 ] = this->gbox[ 5 ];
z1 = this->gbox[ 2 ];
}
/* Establish the correct graphical attributes as defined by attributes
with the supplied Plot. */
astGrfAttrs( this, AST__BORDER_ID, 1, GRF__LINE, method, class );
/* Since we are about to call an external function which may not be
thread safe, prevent any other thread from executing the following code
until the current thread has finished executing it. */
LOCK_MUTEX2;
/* Draw the remaining line parallel to the X axis. */
x[ 1 ] = x1;
y[ 1 ] = y[ 0 ];
z[ 1 ] = z[ 0 ];
ok = astG3DLine( 2, x, y, z );
/* Draw the remaining line parallel to the Y axis. */
x[ 1 ] = x[ 0 ];
y[ 1 ] = y1;
z[ 1 ] = z[ 0 ];
ok = ok && astG3DLine( 2, x, y, z );
/* Draw the remaining line parallel to the X axis. */
x[ 1 ] = x[ 0 ];
y[ 1 ] = y[ 0 ];
z[ 1 ] = z1;
ok = ok && astG3DLine( 2, x, y, z );
/* Allow the next thread to proceed. */
UNLOCK_MUTEX2;
/* Re-establish the original graphical attributes. */
astGrfAttrs( this, AST__BORDER_ID, 0, GRF__LINE, method, class );
/* Report an error if anything went wrong in the grf3d module. */
if( !ok && astOK ) {
astError( AST__GRFER, "%s(%s): Graphics error in astG3DLine. ", status,
method, class );
}
}
/* Return zero if an error has occurrred. */
if( !astOK ) result = 0;
/* Return a flag indicating if any bad values were encountered in any of
the Plots. */
return result;
}
static AstObject *Cast( AstObject *this_object, AstObject *obj, int *status ) {
/*
* Name:
* Cast
* Purpose:
* Cast an Object into an instance of a sub-class.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* AstObject *Cast( AstObject *this, AstObject *obj, int *status )
* Class Membership:
* Plot3D member function (over-rides the protected astCast
* method inherited from the Frame class).
* Description:
* This function returns a deep copy of an ancestral component of the
* supplied object. The required class of the ancestral component is
* specified by another object. Specifically, if "this" and "new" are
* of the same class, a copy of "this" is returned. If "this" is an
* instance of a subclass of "obj", then a copy of the component
* of "this" that matches the class of "obj" is returned. Otherwise,
* a NULL pointer is returned without error.
* Parameters:
* this
* Pointer to the Object to be cast.
* obj
* Pointer to an Object that defines the class of the returned Object.
* The returned Object will be of the same class as "obj".
* Returned Value:
* A pointer to the new Object. NULL if "this" is not a sub-class of
* "obj", or if an error occurs.
* 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; */
AstObject *new;
astDECLARE_GLOBALS
int generation_gap;
/* Initialise */
new = NULL;
/* Check inherited status */
if( !astOK ) return new;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(NULL);
/* See how many steps up the class inheritance ladder it is from "obj"
to this class (Plot3D). A positive value is returned if Plot3D
is a sub-class of "obj". A negative value is returned if "obj" is
a sub-class of Plot3D. Zero is returned if "obj" is a Plot3D.
AST__COUSIN is returned if "obj" is not on the same line of descent
as Plot3D. */
generation_gap = astClassCompare( (AstObjectVtab *) &class_vtab,
astVTAB( obj ) );
/* If "obj" is a Plot3D or a sub-class of Plot3D, we can cast by
truncating the vtab for "this" so that it matches the vtab of "obJ",
and then taking a deep copy of "this". */
if( generation_gap <= 0 && generation_gap != AST__COUSIN ) {
new = astCastCopy( this_object, obj );
/* If "obj" is a Plot (the parent class), we cast by returning a deep
copy of the Plot covering the XY face. */
} else if( generation_gap == 1 ) {
new = astCopy( ( (AstPlot3D *) this_object)->plotxy );
/* If "obj" is a FrameSet or higher, we attempt to use the implementation
inherited from the parent class to cast the FrameSet component into the
class indicated by "obj". */
} else {
new = (*parent_cast)( this_object, obj, status );
}
/* Return the new pointer. */
return new;
}
static void ChangeRootCorner( AstPlot3D *this, int old, int new, int *status ){
/*
* Name:
* ChangeRootCorner
* Purpose:
* Use a new RootCorner value.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* void ChangeRootCorner( AstPlot3D *this, int old, int new, int *status )
* Class Membership:
* Plot method.
* Description:
* This function sets the attributes of the encapsulated Plots so that
* labels appear on the edges of the 3D graphics cube that join at the
* specified new root corner. It also reverses the graphics axes in
* the encapsulated Plots as required in order to ensure that the Plots
* look "normal" when viewed from the outside of the 3D graphics cube.
* This happens if a the Plot used to label a specific axis moves from
* one face the the 3D graphics cube to the opposite face.
* Parameters:
* this
* Pointer to a Plot3D.
* old
* The old RootCorner value.
* new
* The new RootCorner value.
* status
* Pointer to the inherited status variable.
* Notes:
* - Each RootCorner value is in the range 0 to 7 and is a 3 bit
* bit-mask. Bit 0 describes the 3D graphics X axis, bit 1 describes
* the graphics Y axis and bit 2 describes the graphics Z axis. If a
* bit is set it means that the corner is at the upper bound on the
* corresponding axis. If a bit is unset it means that the corner is
* at the lower bound on the corresponding axis.
*-
*/
/* Local Variables: */
AstKeyMap *grfcon;
AstPlot *plot;
AstPlot *plots[ 24 ];
int edges[ 24 ];
int axes[ 24 ];
int axis2d;
int edge;
int i;
int np;
int xeqy;
int xeqz;
/* Check the global status. */
if( !astOK ) return;
/* If the corner has moved on the 3D X axis (from upper X bound to lower X
bound or vice-versa), mirror the axis of the encapsulated Plot that is
perpendicular to the 3D X axis. This means that the Plot can be thought
of as being viewed from the outside of the 3D graphics cube. Also,
update the constant X axis value at which the YZ plane is drawn. */
if( ( old & 1 ) != ( new & 1 ) ) astMirror( this->plotyz, 0 );
grfcon = (AstKeyMap *) astGetGrfContext( this->plotyz );
astMapPut0D( grfcon, "Gcon", this->gbox[ ( new & 1 ) ? 3 : 0 ], "Constant X value" );
astMapPut0I( grfcon, "RootCorner", new, "Labelled corner" );
grfcon= astAnnul( grfcon );
/* Likewise mirror the other two axes if required. */
if( ( old & 2 ) != ( new & 2 ) ) astMirror( this->plotxz, 0 );
grfcon = (AstKeyMap *) astGetGrfContext( this->plotxz );
astMapPut0D( grfcon, "Gcon", this->gbox[ ( new & 2 ) ? 4 : 1 ], "Constant Y value" );
astMapPut0I( grfcon, "RootCorner", new, "Labelled corner" );
grfcon= astAnnul( grfcon );
if( ( old & 4 ) != ( new & 4 ) ) astMirror( this->plotxy, 0 );
grfcon = (AstKeyMap *) astGetGrfContext( this->plotxy );
astMapPut0D( grfcon, "Gcon", this->gbox[ ( new & 4 ) ? 5 : 2 ], "Constant Z value" );
astMapPut0I( grfcon, "RootCorner", new, "Labelled corner" );
grfcon= astAnnul( grfcon );
/* Set a flag saying whether the limits are equal at the new corner for
the X and Y axes. */
xeqy = ( ( ( new & 1 ) > 0 ) == ( ( new & 2 ) > 0 ) );
/* Set a flag saying whether the limits are equal at the new corner for
the X and Z axes. */
xeqz = ( ( ( new & 1 ) > 0 ) == ( ( new & 4 ) > 0 ) );
/* Ensure all Edge attributes are clear. This means that the public
attribute accessors routines used below will return dynamic defaults for
the Edge attributes. */
astClearEdge( this->plotxy, 0 );
astClearEdge( this->plotxy, 1 );
astClearEdge( this->plotxz, 0 );
astClearEdge( this->plotxz, 1 );
astClearEdge( this->plotyz, 0 );
astClearEdge( this->plotyz, 1 );
/* So far we have recorded no edges changes. */
np = 0;
/* We now adjust the Edge attributes in the Plot used to annotate the 3D
X axis in order to get the X axis labels on the correct edge of the 3D
graphics cube. Get the Plot used to produce X axis labels (this will
be either this->plotxy or this->plotxz). */
plot = AxisPlot( this, 0, &axis2d, status );
/* See what edge of the Plot is used to annotate the first of the two WCS
axis described by the 2D Plot. If the Edge(1) attribute has not been
assigned a value, then a dynamic default will be used by the Plot class.
We want to know what this dynamic default is, so we first cleared the
attribute above. Now we set the attribute value explicitly to the dynamic
default returned by astGetC. Note, we use astGetC rather than astGetEdge
because the dynamic default is not calculated when calling astGetEdge. */
astSetC( plot, "Edge(1)", astGetC( plot, "Edge(1)" ));
edge = astGetEdge( plot, 0 );
astClearEdge( plot, 0 );
/* If the 3D X axis is labelled using the Plot that spans the XY plane... */
if( plot == this->plotxy ) {
/* ... and if the new root corner is at the upper limit on the Y axis... */
if( new & 2 ) {
/* If the first WCS axis is currently labelled on either the top or bottom
edge, ensure it is labelled on the upper Y (top) edge. */
if( edge == 3 || edge == 1 ) {
plots[ np ] = plot;
axes[ np ] = 0;
edges[ np++ ] = TOP;
/* Otherwise ensure that the second WCS axis is labelled on the upper Y (top)
edge. */
} else {
plots[ np ] = plot;
axes[ np ] = 1;
edges[ np++ ] = TOP;
}
/* If the new root corner is at the lower limit on the Y axis... */
} else {
/* If the first WCS axis is currently labelled on either the top or bottom
edge, ensure it is labelled on the lower Y (bottom) edge. */
if( edge == 3 || edge == 1 ) {
plots[ np ] = plot;
axes[ np ] = 0;
edges[ np++ ] = BOTTOM;
/* Otherwise ensure that the second WCS axis is labelled on the lower Y
(bottom) edge. */
} else {
plots[ np ] = plot;
axes[ np ] = 1;
edges[ np++ ] = BOTTOM;
}
}
/* If the 3D X axis is labelled using the Plot that spans the XZ plane... */
} else {
/* ... and if the new root corner is at the upper limit on the Z axis... */
if( new & 4 ) {
/* If the first WCS axis is currently labelled on either the top or bottom
edge, ensure it is labelled on the upper Z (top) edge. */
if( edge == 3 || edge == 1 ) {
plots[ np ] = plot;
axes[ np ] = 0;
edges[ np++ ] = TOP;
/* Otherwise ensure that the second WCS axis is labelled on the upper Y (top)
edge. */
} else {
plots[ np ] = plot;
axes[ np ] = 1;
edges[ np++ ] = TOP;
}
/* If the new root corner is at the lower limit on the Z axis... */
} else {
/* If the first WCS axis is currently labelled on either the top or bottom
edge, ensure it is labelled on the lower Z (bottom) edge. */
if( edge == 3 || edge == 1 ) {
plots[ np ] = plot;
axes[ np ] = 0;
edges[ np++ ] = BOTTOM;
/* Otherwise ensure that the second WCS axis is labelled on the lower Z
(bottom) edge. */
} else {
plots[ np ] = plot;
axes[ np ] = 1;
edges[ np++ ] = BOTTOM;
}
}
}
/* We now adjust the Edge attributes in the Plot used to annotate the 3D
Y axis in order to get the Y axis labels on the correct edge of the 3D
graphics cube. Get the Plot used to produce Y axis labels. */
plot = AxisPlot( this, 1, &axis2d, status );
/* See what edge of the Plot is used to annotate the first of the two WCS
axis described by the Plot. */
astSetC( plot, "Edge(1)", astGetC( plot, "Edge(1)" ));
edge = astGetEdge( plot, 0 );
astClearEdge( plot, 0 );
/* If the 3D Y axis is labelled using the Plot that spans the XY plane... */
if( plot == this->plotxy ) {
/* ... and if the new root corner is at the same limit on the X and Z axes,
put Y labels on the right side of the Plot. */
if( xeqz ) {
if( edge == 0 || edge == 2 ) {
plots[ np ] = plot;
axes[ np ] = 0;
edges[ np++ ] = RIGHT;
} else {
plots[ np ] = plot;
axes[ np ] = 1;
edges[ np++ ] = RIGHT;
}
/* If the new root corner is at a different limit on the X and Z axes,
put Y labels on the left side of the Plot. */
} else {
if( edge == 0 || edge == 2 ) {
plots[ np ] = plot;
axes[ np ] = 0;
edges[ np++ ] = LEFT;
} else {
plots[ np ] = plot;
axes[ np ] = 1;
edges[ np++ ] = LEFT;
}
}
/* If the 3D Y axis is labelled using the Plot that spans the YZ plane... */
} else {
/* ... and if the new root corner is at the upper Z limit, put Y labels on
the top of the Plot. */
if( new & 4 ) {
if( edge == 1 || edge == 3 ) {
plots[ np ] = plot;
axes[ np ] = 0;
edges[ np++ ] = TOP;
} else {
plots[ np ] = plot;
axes[ np ] = 1;
edges[ np++ ] = TOP;
}
/* If the new root corner is at the lower Z limit, put Y labels on the
bottom of the Plot. */
} else {
if( edge == 1 || edge == 3 ) {
plots[ np ] = plot;
axes[ np ] = 0;
edges[ np++ ] = BOTTOM;
} else {
plots[ np ] = plot;
axes[ np ] = 1;
edges[ np++ ] = BOTTOM;
}
}
}
/* We now adjust the Edge attributes in the Plot used to annotate the 3D
Z axis in order to get the Z axis labels on the correct edge of the 3D
graphics cube. Get the Plot used to produce Z axis labels. */
plot = AxisPlot( this, 2, &axis2d, status );
/* See what edge of the Plot is used to annotate the first of the two WCS
axis described by the Plot. */
astSetC( plot, "Edge(1)", astGetC( plot, "Edge(1)" ));
edge = astGetEdge( plot, 0 );
astClearEdge( plot, 0 );
/* If the 3D Z axis is labelled using the Plot that spans the XZ plane... */
if( plot == this->plotxz ) {
/* ... and if the new root corner is at the same limit on the X and Y axes,
put Z labels on the left side of the Plot. */
if( xeqy ) {
if( edge == 0 || edge == 2 ) {
plots[ np ] = plot;
axes[ np ] = 0;
edges[ np++ ] = LEFT;
} else {
plots[ np ] = plot;
axes[ np ] = 1;
edges[ np++ ] = LEFT;
}
/* If the new root corner is at a different limit on the X and Y axes,
put Y labels on the right side of the Plot. */
} else {
if( edge == 0 || edge == 2 ) {
plots[ np ] = plot;
axes[ np ] = 0;
edges[ np++ ] = RIGHT;
} else {
plots[ np ] = plot;
axes[ np ] = 1;
edges[ np++ ] = RIGHT;
}
}
/* If the 3D Z axis is labelled using the Plot that spans the YZ plane... */
} else {
/* ... and if the new root corner is at the same limit on the X and Y axes,
put Z labels on the right side of the Plot. */
if( xeqz ) {
if( edge == 0 || edge == 2 ) {
plots[ np ] = plot;
axes[ np ] = 0;
edges[ np++ ] = RIGHT;
} else {
plots[ np ] = plot;
axes[ np ] = 1;
edges[ np++ ] = RIGHT;
}
/* If the new root corner is at a different limit on the X and Y axes,
put Y labels on the left side of the Plot. */
} else {
if( edge == 0 || edge == 2 ) {
plots[ np ] = plot;
axes[ np ] = 0;
edges[ np++ ] = LEFT;
} else {
plots[ np ] = plot;
axes[ np ] = 1;
edges[ np++ ] = LEFT;
}
}
}
/* Apply the set of edge changes determined above. */
for( i = 0; i < np; i++ ) {
astSetEdge( plots[ i ], axes[ i ], edges[ i ] );
}
/* Ensure that the 2 Plot axes that are not being used have suitable values
for their attributes. That is, no labels are drawn, and the ticked
edges are the one that meet at the new RootCorner. */
if( !astTestEdge( this->plotxy, 0 ) ) {
astSetEdge( this->plotxy, 0, ( new & 2 ) ? TOP : BOTTOM );
}
if( !astTestEdge( this->plotxy, 1 ) ) {
astSetEdge( this->plotxy, 1, xeqz ? RIGHT: LEFT );
}
if( !astTestEdge( this->plotxz, 0 ) ) {
astSetEdge( this->plotxz, 0, ( new & 4 ) ? TOP : BOTTOM );
}
if( !astTestEdge( this->plotxz, 1 ) ) {
astSetEdge( this->plotxz, 1, xeqy ? LEFT : RIGHT );
}
if( !astTestEdge( this->plotyz, 0 ) ) {
astSetEdge( this->plotyz, 0, ( new & 4 ) ? TOP : BOTTOM );
}
if( !astTestEdge( this->plotyz, 1 ) ) {
astSetEdge( this->plotyz, 1, xeqy ? RIGHT : LEFT );
}
}
static void Clear( AstObject *this_object, const char *attrib, int *status ) {
/*
* Name:
* Clear
* Purpose:
* Clear attribute values for a Plot3D.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* void Clear( AstObject *this, const char *attrib, int *status )
* Class Membership:
* Plot3D member function (over-rides the public astClear method
* inherited from the Object class).
* Description:
* This function clears the values of a specified set of attributes
* for a Plot3D. Clearing an attribute cancels any value that has
* previously been explicitly set for it, so that the standard
* default attribute value will subsequently be used instead. This
* also causes the astTest function to return the value zero for
* the attribute, indicating that no value has been set.
* Parameters:
* this
* Pointer to the Plot3D.
* attrib
* Pointer to a null-terminated character string containing a
* comma-separated list of the names of the attributes to be
* cleared.
* status
* Pointer to the inherited status variable.
* Notes:
* - This function preserves the integrity of the Plot3D (if
* possible) by appropriately modifying the three encapsulated Plots.
*/
/* Check the global error status. */
if ( !astOK ) return;
/* Invoke the parent astClear method to clear the Plot3D's attribute values. */
(*parent_clear)( this_object, attrib, status );
/* Update the three 2D Plots stored in the Plot3D structure so that they
reflect this modified FrameSet. */
UpdatePlots( (AstPlot3D *) this_object, status );
}
static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
* Name:
* ClearAttrib
* Purpose:
* Clear an attribute value for a Plot3D.
* Type:
* Private function.
* Synopsis:
* #include "plot.h"
* void ClearAttrib( AstObject *this, const char *attrib )
* Class Membership:
* Plot3D member function (over-rides the astClearAttrib protected
* method inherited from the Plot class).
* Description:
* This function clears the value of a specified attribute for a
* Plot3D, so that the default value will subsequently be used.
* Parameters:
* this
* Pointer to the Plot3D.
* attrib
* Pointer to a null terminated string specifying the attribute
* name. This should be in lower case with no surrounding white
* space.
*/
/* Local Variables: */
AstPlot3D *this; /* Pointer to the Plot3D structure */
AstPlot *plot; /* Pointer to specific Plot */
char attname[50]; /* Plot attribute base name */
char patt[50]; /* Plot attribute full name */
char spec[10]; /* Plane specification */
int axis; /* Axis index */
int len; /* Length of attrib string */
int nc; /* Number of characters read */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the Plot3D structure. */
this = (AstPlot3D *) this_object;
/* Obtain the length of the "attrib" string. */
len = strlen( attrib );
/* Check the attribute name and clear the appropriate attribute. */
/* Norm. */
/* ----------- */
if ( !strcmp( attrib, "norm" ) ) {
astClearNorm( this, 0 );
astClearNorm( this, 1 );
astClearNorm( this, 2 );
/* Norm(axis). */
/* ----------- */
} else if ( nc = 0,
( 1 == astSscanf( attrib, "norm(%d)%n", &axis, &nc ) )
&& ( nc >= len ) ) {
astClearNorm( this, axis - 1 );
/* RootCorner. */
/* ----------- */
} else if ( !strcmp( attrib, "rootcorner" ) ) {
astClearRootCorner( this );
/* ..._XY etc */
/* ---------- */
} else if ( nc = 0,
( 2 == astSscanf( attrib, "%[a-z]_%[xyz]%n", attname, spec,
&nc ) ) ) {
if( !strcmp( spec, "xy" ) || !strcmp( spec, "yx" ) ) {
plot = this->plotxy;
} else if( !strcmp( spec, "xz" ) || !strcmp( spec, "zx" ) ) {
plot = this->plotyz;
} else if( !strcmp( spec, "yz" ) || !strcmp( spec, "zy" ) ) {
plot = this->plotxz;
} else {
plot = NULL;
}
if( plot ) {
sprintf( patt, "%s%s", attname, attrib + nc );
astClearAttrib( plot, patt );
} else {
(*parent_clearattrib)( this_object, attrib, status );
}
/* 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 void ClearCurrent( AstFrameSet *this_frameset, int *status ) {
/*
* Name:
* ClearCurrent
* Purpose:
* Clear the value of the Current attribute for a Plot3D.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* int astClearCurrent( AstFrameSet *this )
* Class Membership:
* Plot3D member function (over-rides the public astClearCurrent method
* inherited from the FrameSet class).
* Description:
* This function clears the value of the Current attribute for a
* Plot3D. This attribute is an index that identifies the current
* Frame for the Plot3D.
* Parameters:
* this
* Pointer to the Plot3D.
*/
/* Invoke the parent astClearCurrent method. */
(*parent_clearcurrent)( this_frameset, status );
/* Update the three 2D Plots stored in the Plot3D structure so that they
reflect this modified FrameSet. */
UpdatePlots( (AstPlot3D *) this_frameset, status );
}
static void ClearRootCorner( AstPlot3D *this, int *status ){
/*
*+
* Name:
* astClearRootCorner
* Purpose:
* Clear the RootCorner attribute.
* Type:
* Protected virtual function.
* Synopsis:
* #include "plot3d.h"
* void astClearRootCorner( AstPlot3D *this )
* Class Membership:
* Plot method.
* Description:
* This function clears the RootCorner attribute.
* Parameters:
* this
* Pointer to a Plot3D.
*-
*/
/* Local Variables: */
int old;
int new;
/* Check the global status. */
if( !astOK ) return;
/* Get the current rootcorner value. */
old = astGetRootCorner( this );
/* Clear the RootCorner attribute. */
this->rootcorner = -1;
/* Get the new (default) rootcorner value. */
new = astGetRootCorner( this );
/* If the root corner has changed, mirror any axes of the encapsulated Plots
that need mirroring (this is done to ensure that Plots look right when
viewed from the outside of the graphics cube), and modify the Edge
attributes in the encapsulated Plots to ensure the labels appear on the
requested edges of the 3D graphics cube. . */
if( old != new ) ChangeRootCorner( this, old, new, status );
}
static void CreatePlots( AstPlot3D *this, AstFrameSet *fset, const float *gbox,
const double *bbox, int *status ) {
/*
* Name:
* CreatePlots
* Purpose:
* Create three 2D plots and store in the Plot3D.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* void CreatePlots( AstPlot3D *this, AstFrameSet *fset, const float *gbox,
const double *bbox, int *status )
* Class Membership:
* Plot3D method.
* Description:
* This function splits the supplied FrameSet up into 3 independent 2D
* FrameSets, each describing a 2D plane in the supplied 3D FrameSet.
* It then uses these 2D FrameSets to create three Plots, one for each
* plane in the graphics plotting space, and stores them in the Plot3D.
*
* Each of the three Plots is notionally pasted onto one face of the
* 3D graphics cube (the RootCorner attribute is used to determine which
* of the two parallel faces a particular Plot is pasted onto). The
* Plot is pasted in such a way that, when viewed from the outside of
* the graphics cube, the first graphics axis increases left to right
* and the second increases bottom to top (this assumes that "up" is
* parallel to the 3D Z axis).
*
* Initially, the Plots are created assuming the default RootCorner
* value ("LLL"). They will be changed later if the value of the
* RootCorner attribute is changed.
* Parameters:
* this
* Pointer to the Plot3D.
* fset
* Pointer to the FrameSet.
* gbox
* A pointer to an array of 6 values giving the graphics coordinates
* of the bottom left and top right corners of a box on the graphics
* output device. The first triple of values should be the graphics
* coordinates of the bottom left corner of the box and the second
* triple of values are the graphics coordinates of the top right corner.
* bbox
* A pointer to an array of 6 values giving the coordinates in the
* supplied Frame or base Frame of the supplied FrameSet at the bottom
* left and top right corners of the box specified by parameter gbox.
* These should be supplied in the same order as for parameter "gbox".
* status
* Pointer to the inherited status variable.
* Notes:
* - Each returned plot has 3 Frames: Frame 1 is the base (GRAPHICS)
* Frame; Frame 2 is spanned by 2 of the 3 axes in the base Frame of
* the supplied FrameSet; Frame 3 is the current Frame and is spanned
* by 2 of the 3 axes in the current Frame of the supplied FrameSet.
* Any future changes to this function that alter this structure should
* reflected in equivalent changes to function UpdatePlots.
*/
/* Local Variables: */
AstFrameSet *fsetxy;
AstFrameSet *fsetxz;
AstFrameSet *fsetyz;
double basebox2d[ 4 ];
float graphbox2d[ 4 ];
int baseplane;
int labelxy[ 2 ];
int labelxz[ 2 ];
int labelyz[ 2 ];
int wcsxy[ 2 ];
int wcsxz[ 2 ];
int wcsyz[ 2 ];
/* Check the inherited status. */
if( !astOK ) return;
/* Split the supplied FrameSet up into 3 FrameSets, each with a 2D base
and current Frame. Each of these FrameSets describes one plane of
the 3D cube. One of them will be spanned by two axes picked from the
supplied 3D FrameSet. The other two FrameSets will each include a copy
of the remaining 3rd axis from the supplied FrameSet, plus an extra
dummy axis. These dummy axes will never be labelled. */
SplitFrameSet( fset, &fsetxy, labelxy, wcsxy, &fsetxz, labelxz, wcsxz,
&fsetyz, labelyz, wcsyz, &baseplane, status );
/* If OK, annul any existing 2D plots. */
if( astOK ) {
if( this->plotxy ) this->plotxy = astAnnul( this->plotxy );
if( this->plotxz ) this->plotxz = astAnnul( this->plotxz );
if( this->plotyz ) this->plotyz = astAnnul( this->plotyz );
/* Create three Plots; one for each 2D plane in the graphics plotting
space. Set the attributes of these plots so that the required axes are
labelled and other axes are left blank. The "graphbox2d" and "basebox2d"
values used to create each Plot define the sense, as well as the extent,
of each axis. The first pair of values in each give the lower left corner
of the Plot and the second pair give the top right corner. We want each
Plot to have X increasing left to right and Y increasing bottom to
top when viewed from the outside of the cube. We assume an initial
RootCorner value of "LLL" (that is, the Plots are pasted onto the cube
faces that meet at the lower limit on every axis). */
graphbox2d[ 0 ] = gbox[ 3 ];
graphbox2d[ 1 ] = gbox[ 1 ];
graphbox2d[ 2 ] = gbox[ 0 ];
graphbox2d[ 3 ] = gbox[ 4 ];
basebox2d[ 0 ] = bbox[ 3 ];
basebox2d[ 1 ] = bbox[ 1 ];
basebox2d[ 2 ] = bbox[ 0 ];
basebox2d[ 3 ] = bbox[ 4 ];
if( this->plotxy ) this->plotxy = astAnnul( this->plotxy );
this->plotxy = astPlot( fsetxy, graphbox2d, basebox2d, "", status );
SetPlotAttr( this->plotxy, XY, labelxy, status );
graphbox2d[ 0 ] = gbox[ 0 ];
graphbox2d[ 1 ] = gbox[ 2 ];
graphbox2d[ 2 ] = gbox[ 3 ];
graphbox2d[ 3 ] = gbox[ 5 ];
basebox2d[ 0 ] = bbox[ 0 ];
basebox2d[ 1 ] = bbox[ 2 ];
basebox2d[ 2 ] = bbox[ 3 ];
basebox2d[ 3 ] = bbox[ 5 ];
this->plotxz = astPlot( fsetxz, graphbox2d, basebox2d, "", status );
SetPlotAttr( this->plotxz, XZ, labelxz, status );
graphbox2d[ 0 ] = gbox[ 4 ];
graphbox2d[ 1 ] = gbox[ 2 ];
graphbox2d[ 2 ] = gbox[ 1 ];
graphbox2d[ 3 ] = gbox[ 5 ];
basebox2d[ 0 ] = bbox[ 4 ];
basebox2d[ 1 ] = bbox[ 2 ];
basebox2d[ 2 ] = bbox[ 1 ];
basebox2d[ 3 ] = bbox[ 5 ];
this->plotyz = astPlot( fsetyz, graphbox2d, basebox2d, "", status );
SetPlotAttr( this->plotyz, YZ, labelyz, status );
/* Store information that allows each 3D WCS axis to be associatedf with
a pair of Plots. Also store the WCS axis within each Plot that
corresponds to the 3D WCS axis. */
StoreAxisInfo( this, labelxy, wcsxy, labelxz, wcsxz, labelyz, wcsyz, status );
/* Store the Plot that spans two connected 3D axes. */
this->baseplot = baseplane;
/* Free resources */
fsetxy = astAnnul( fsetxy );
fsetxz = astAnnul( fsetxz );
fsetyz = astAnnul( fsetyz );
}
}
static int Element2D( AstPlot3D *this, int element, int *elem2d1,
int *elem2d2, int *status ){
/*
* Name:
* Element2D
* Purpose:
* Convert a 3D graphics element identifier to a corresponding pair of
* 2D identifiers.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* int Element2D( AstPlot3D *this, int element, int *elem2d1,
* int *elem2d2, int *status )
* Class Membership:
* Plot3D method.
* Description:
* This function takes an integer identifier for an element of a 3D
* annotated grid (e.g. ticks, axis 1 labels, border, etc), and returns
* a element identifers that can be used with the encapsualted 2D Plots.
* Parameters:
* this
* Pointer to the Plot2D structure.
* element
* The 3D element identifier to convert.
* elem2d1
* Pointer to an int in which to return the 2D element identifier
* to use with the first of the two Plots that span the axis to
* which the 3D element identifier refers. Returned holding 0 if
* the given 3D element identifier is not axis specific.
* elem2d2
* Pointer to an int in which to return the 2D element identifier
* to use with the second of the two Plots that span the axis to
* which the 3D element identifier refers. Returned holding 0 if
* the given 3D element identifier is not axis specific.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The zero-based index of the 3D axis to which the given element
* identifier refers, or -1 if the element identifier is not axis
* specific.
*/
/* Local Variables: */
int axis3d;
/* Check the global error status. */
if ( !astOK ) return 0;
/* Get the zero-based index of the 3D axis to which the supplied element
refers. Use an index of -1 to indicate that the element does not
relate to a specific axis. Also get the corresponding element to use
with the two Plots that share the speified 3D axis. */
/* Define a macro used to set the 2d element identifiers for a given 3d
element identifier. */
#define SET_ELEM2D(id1,id2) \
*elem2d1 = this->axis_index1[ axis3d ] ? id2 : id1; \
*elem2d2 = this->axis_index2[ axis3d ] ? id2 : id1;
if( element == AST__BORDER_ID ){
axis3d = -1;
} else if( element == AST__CURVE_ID ){
axis3d = -1;
} else if( element == AST__TITLE_ID ){
axis3d = -1;
} else if( element == AST__MARKS_ID ){
axis3d = -1;
} else if( element == AST__TEXT_ID ){
axis3d = -1;
} else if( element == AST__AXIS1_ID ){
axis3d = 0;
SET_ELEM2D(AST__AXIS1_ID,AST__AXIS2_ID)
} else if( element == AST__AXIS2_ID ){
axis3d = 1;
SET_ELEM2D(AST__AXIS1_ID,AST__AXIS2_ID)
} else if( element == AST__AXIS3_ID ){
axis3d = 2;
SET_ELEM2D(AST__AXIS1_ID,AST__AXIS2_ID)
} else if( element == AST__NUMLAB1_ID ){
axis3d = 0;
SET_ELEM2D(AST__NUMLAB1_ID,AST__NUMLAB2_ID)
} else if( element == AST__NUMLAB2_ID ){
axis3d = 1;
SET_ELEM2D(AST__NUMLAB1_ID,AST__NUMLAB2_ID)
} else if( element == AST__NUMLAB3_ID ){
axis3d = 2;
SET_ELEM2D(AST__NUMLAB1_ID,AST__NUMLAB2_ID)
} else if( element == AST__TEXTLAB1_ID ){
axis3d = 0;
SET_ELEM2D(AST__TEXTLAB1_ID,AST__TEXTLAB2_ID)
} else if( element == AST__TEXTLAB2_ID ){
axis3d = 1;
SET_ELEM2D(AST__TEXTLAB1_ID,AST__TEXTLAB2_ID)
} else if( element == AST__TEXTLAB3_ID ){
axis3d = 2;
SET_ELEM2D(AST__TEXTLAB1_ID,AST__TEXTLAB2_ID)
} else if( element == AST__TICKS1_ID ){
axis3d = 0;
SET_ELEM2D(AST__TICKS1_ID,AST__TICKS2_ID)
} else if( element == AST__TICKS2_ID ){
axis3d = 1;
SET_ELEM2D(AST__TICKS1_ID,AST__TICKS2_ID)
} else if( element == AST__TICKS3_ID ){
axis3d = 2;
SET_ELEM2D(AST__TICKS1_ID,AST__TICKS2_ID)
} else if( element == AST__GRIDLINE1_ID ){
axis3d = 0;
SET_ELEM2D(AST__GRIDLINE1_ID,AST__GRIDLINE2_ID)
} else if( element == AST__GRIDLINE2_ID ){
axis3d = 1;
SET_ELEM2D(AST__GRIDLINE1_ID,AST__GRIDLINE2_ID)
} else if( element == AST__GRIDLINE3_ID ){
axis3d = 2;
SET_ELEM2D(AST__GRIDLINE1_ID,AST__GRIDLINE2_ID)
} else {
axis3d = 0;
astError( AST__INTER, "Element2D(Plot3D): The MAKE_CLEAR2 macro "
"does not yet support element index %d (internal "
"AST programming error).", status, element );
}
#undef SET_ELEM2D
return axis3d;
}
static int Equal( AstObject *this_object, AstObject *that_object, int *status ) {
/*
* Name:
* Equal
* Purpose:
* Test if two Plot3Ds are equivalent.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* int Equal( AstObject *this, AstObject *that, int *status )
* Class Membership:
* Plot3D member function (over-rides the astEqual protected
* method inherited from the Plot Object class).
* Description:
* This function returns a boolean result (0 or 1) to indicate whether
* two Plot3Ds are equivalent.
* Parameters:
* this
* Pointer to the first Plot3D.
* that
* Pointer to the second Plot3D.
* status
* Pointer to the inherited status variable.
* Returned Value:
* One if the Plot3Ds are equivalent, zero otherwise.
* 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: */
AstPlot3D *that; /* Pointer to the second Plot3D structure */
AstPlot3D *this; /* Pointer to the first Plot3D structure */
int result; /* Result value to return */
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Invoke the Equal method inherited from the parent Plot class. This checks
that the Plots are both of the same class (amongst other things). */
if( (*parent_equal)( this_object, that_object, status ) ) {
/* Obtain pointers to the two Plot3D structures. */
this = (AstPlot3D *) this_object;
that = (AstPlot3D *) that_object;
/* Check the encapsulated Plots for equality. */
result = ( astEqual( this->plotxz, that->plotxz ) &&
astEqual( this->plotyz, that->plotyz ) &&
astEqual( this->plotxy, that->plotxy ) );
}
/* If an error occurred, clear the result value. */
if ( !astOK ) result = 0;
/* Return the result, */
return result;
}
static AstPointSet *ExtendTicks( AstPlot *plot, AstPointSet *ticks, int *status ){
/*
* Name:
* ExtendTicks
* Purpose:
* Add an extra tick to the start and end of a list of tick marks.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* AstPointSet *ExtendTicks( AstPlot *plot, AstPointSet *ticks, int *status )
* Class Membership:
* Plot3D method.
* Description:
* This function takes a list of tick marks drawn using the supplied
* Plot, and adds in an extra tick mark at the start and end of the
* supplied list of ticks, returning the expanded list in a new
* PointSet. The extra points are guaranteed to fall outside the area
* enclosed within the supplied Plot.
* Parameters:
* plot
* The Plot that was used to generate the list of tick marks.
* ticks
* A PointSet holding the 2D graphics coordinates (within the base
* Frame of the supplied Plot) at which each tick mark starts.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to a new PointSet that has 2 more entries than the
* supplied PointSet. The first point is a new tick mark, then comes
* all the ticks mark positions supplied in "ticks", and finally there
* is another new tick mark.
*/
/* Local Variables: */
AstPointSet *result;
double **ptr_in;
double **ptr_out;
double *a_in;
double *a_out;
double *b_in;
double *b_out;
double *p;
double delta;
double hi;
double lo;
double range[ 2 ];
double v;
int axis;
int i;
int increasing;
int np;
/* Check inherited status */
if( !astOK || !ticks ) return NULL;
/* Get the number of tick marsk in the supplied PointSet and get pointers
to the 3D Graphics values contained in the PointSet. */
np = astGetNpoint( ticks );
ptr_in = astGetPoints( ticks );
/* Create the returned PointSet with room for an extra pair of ticks. Get
pointers to its data arrays */
result = astPointSet( np + 2, 2, "", status );
ptr_out = astGetPoints( result );
/* Check the pointers can be used safely. */
if( astOK ) {
/* Find the index of the 2D graphics axis (0 or 1) that varies along the
set of tick marks. We do this by finding the max and min value
supplied for each axis, and then choosing the axis that has the highest
range. */
for( axis = 0; axis < 2; axis++ ) {
hi = -DBL_MAX;
lo = DBL_MAX;
p = ptr_in[ axis ];
for( i = 0; i < np; i++, p++ ) {
v = *p;
if( v != AST__BAD ) {
if( v > hi ) hi = v;
if( v < lo ) lo = v;
}
}
if( lo != DBL_MAX ) {
range[ axis ] = hi - lo;
} else {
astError( AST__INTER, "ExtendTicks{Plot3D): no good ticks on "
"axis %d (internal AST prgramming error).", status, axis );
}
}
/* Get the index of the axis with the largest range (the other range
should be zero). */
axis = ( range[ 0 ] > range[ 1 ] ) ? 0 : 1;
/* Copy the input graphics positions to the output PointSet, and add an
extra position at the beginning and end of the output PointSet. */
if( axis == 0 ) {
lo = plot->xlo;
hi = plot->xhi;
a_in = ptr_in[ 0 ];
b_in = ptr_in[ 1 ];
a_out = ptr_out[ 0 ];
b_out = ptr_out[ 1 ];
} else {
lo = plot->ylo;
hi = plot->yhi;
a_in = ptr_in[ 1 ];
b_in = ptr_in[ 0 ];
a_out = ptr_out[ 1 ];
b_out = ptr_out[ 0 ];
}
delta = 0.2*( hi - lo );
if( a_in[ 0 ] < a_in[ 1 ] ) {
increasing = 1;
*(a_out++) = lo - delta;
} else {
increasing = 0;
*(a_out++) = hi + delta;
}
*(b_out++) = *b_in;
for( i = 0; i < np; i++ ) {
*(a_out++) = *(a_in++);
*(b_out++) = *(b_in++);
}
*(b_out++) = b_in[ -1 ];
if( increasing ) {
*(a_out++) = hi + delta;
} else {
*(a_out++) = lo - delta;
}
}
/* Return the extended PointSet. */
return result;
}
static AstFrameSet *Fset3D( AstFrameSet *fset, int ifrm, int *status ) {
/*
* Name:
* Fset3D
* Purpose:
* Create a FrameSet with no more than 3 dimensions for a given Frame.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* AstFrameSet *Fset3D( AstFrameSet *fset, int ifrm, int *status )
* Class Membership:
* Plot3D method.
* Description:
* This function checks a specified Frame in the supplied FrameSet.
* If the Frame has more than 3 dimensions, a new Frame is added to
* the FrameSet containing just the first three axes of the specified
* Frame. A PermMap is used to connect this Frame to the specified
* Frame, which supplied bad values for any missing axes. If the
* specified Frame is the base Frame in the supplied FrameSet, then the
* new Frame becomes the base Frame in the returned FrameSet. Like-wise,
* if the specified Frame is the current Frame, then the new Frame
* will be the current Frame in the returned FrameSet.
*
* If the specified Frame does not have more than 3 axes, then a clone
* of the FrameSet pointer is returned, otherwise the returned pointer
* points to a copy of the supplied FrameSet with the new 3-D Frame
* added.
* Parameters:
* fset
* Pointer to the FrameSet.
* ifrm
* The index of the Frame to check. This should be AST__BASE or
* AST_CURRENT.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to a FrameSet in which the Frame with index given by ifrm
* has no more than 3 axes.
*/
/* Local Variables: */
AstFrame *frm;
AstFrame *newfrm;
AstFrameSet *ret;
AstPermMap *map;
double zero;
int *inperm;
int axes[3];
int i;
int ic;
int nax;
/* Check the inherited status. */
if( !astOK ) return NULL;
/* Initialise variables to avoid "used of uninitialised variable"
messages from dumb compilers. */
map = NULL;
/* Get a pointer to the requested Frame in the supplied FrameSet. */
frm = astGetFrame( fset, ifrm );
/* See how many dimensions the specified Frame of the supplied FrameSet
has. */
nax = astGetNaxes( frm );
/* If it is more than 3-dimensionbal, create a 3D Frame by picking
axes 1, 2 and 3 from the original Frame. */
if( nax > 3 ) {
axes[ 0 ] = 0;
axes[ 1 ] = 1;
axes[ 2 ] = 2;
newfrm = astPickAxes( frm, 3, axes, NULL );
/* Create a PermMap to describe the mapping between the two Frames.
Use zero as the value for unknown axes (the optional mapping which
can be returned by astPickAxes uses AST__BAD for unknown axes). */
inperm = (int *) astMalloc( sizeof(int)*(size_t) nax );
if( astOK ){
inperm[ 0 ] = 0;
inperm[ 1 ] = 1;
inperm[ 2 ] = 2;
for( i = 3; i < nax; i++ ) inperm[ i ] = -1;
zero = 0.0;
map = astPermMap( nax, inperm, 3, axes, &zero, "", status );
inperm = (int *) astFree( (void *) inperm );
}
/* Get a copy of the supplied FrameSet. */
ret = astCopy( fset );
/* Add the new Frame to the FrameSet (it becomes the current Frame). */
ic = astGetCurrent( ret );
astAddFrame( ret, ifrm, map, newfrm );
newfrm = astAnnul( newfrm );
/* If the new Frame was derived from the base frame, set the new base
Frame, and re-instate the original current Frame */
if( ifrm == AST__BASE ){
astSetBase( ret, astGetCurrent( ret ) );
astSetCurrent( ret, ic );
}
/* If the specified Frame in the supplied FrameSet is 3-dimensional, just
return a clone of it. */
} else {
ret = astClone( fset );
}
/* Annul the pointer to the original Frame. */
frm = astAnnul( frm );
return ret;
}
static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
* Name:
* GetAttrib
* Purpose:
* Get the value of a specified attribute for a Plot3D.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* const char *GetAttrib( AstObject *this, const char *attrib, int *status )
* Class Membership:
* Plot3D member function (over-rides the protected astGetAttrib
* method inherited from the FrameSet class).
* Description:
* This function returns a pointer to the value of a specified
* attribute for a Plot3D, formatted as a character string.
* Parameters:
* this
* Pointer to the Plot3D.
* 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 Plot3D, 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 Plot3D. 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: */
AstPlot *plot; /* Pointer to specific Plot */
AstPlot3D *this; /* Pointer to the Plot3D structure */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
char attname[50]; /* Plot attribute base name */
char patt[50]; /* Plot attribute full name */
char spec[10]; /* Plane specification */
const char *result; /* Pointer value to return */
double dval; /* Floating point attribute value */
int axis; /* Axis index */
int ival; /* Int attribute value */
int len; /* Length of attrib string */
int nc; /* Number of character read */
/* Initialise. */
result = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(this_object);
/* Obtain a pointer to the Plot3D structure. */
this = (AstPlot3D *) this_object;
/* Obtain the length of the attrib string. */
len = strlen( attrib );
/* 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. */
/* Norm(axis). */
/* ----------- */
if ( nc = 0,
( 1 == astSscanf( attrib, "norm(%d)%n", &axis, &nc ) )
&& ( nc >= len ) ) {
dval = astGetNorm( this, axis - 1 );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval );
result = getattrib_buff;
}
/* RootCorner. */
/* ----------- */
} else if ( !strcmp( attrib, "rootcorner" ) ) {
ival = astGetRootCorner( this );
result = RootCornerString( ival, status );
if( !result && astOK ) {
astError( AST__INTER, "astGetAttrib(Plot3D): Illegal value %d "
"for RootCorner attribute (internal AST programming "
"error).", status, ival );
}
/* ..._XY etc */
/* ---------- */
} else if ( nc = 0,
( 2 == astSscanf( attrib, "%[a-z]_%[xyz]%n", attname, spec,
&nc ) ) ) {
if( !strcmp( spec, "xy" ) || !strcmp( spec, "yx" ) ) {
plot = this->plotxy;
} else if( !strcmp( spec, "xz" ) || !strcmp( spec, "zx" ) ) {
plot = this->plotyz;
} else if( !strcmp( spec, "yz" ) || !strcmp( spec, "zy" ) ) {
plot = this->plotxz;
} else {
plot = NULL;
}
if( plot ) {
sprintf( patt, "%s%s", attname, attrib + nc );
result = astGetAttrib( plot, patt );
} else {
result = (*parent_getattrib)( this_object, attrib, status );
}
/* 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 GetObjSize( AstObject *this_object, int *status ) {
/*
* Name:
* GetObjSize
* Purpose:
* Return the in-memory size of an Object.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* int GetObjSize( AstObject *this, int *status )
* Class Membership:
* Plot3D member function (over-rides the astGetObjSize protected
* method inherited from the parent class).
* Description:
* This function returns the in-memory size of the supplied Plot3D,
* in bytes.
* Parameters:
* this
* Pointer to the Plot3D.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The Object size, in bytes.
* 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: */
AstPlot3D *this; /* Pointer to Plot3D structure */
int result; /* Result value to return */
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain a pointers to the Plot3D structure. */
this = (AstPlot3D *) this_object;
/* Invoke the GetObjSize method inherited from the parent class, and then
add on any components of the class structure defined by this class
which are stored in dynamically allocated memory. */
result = (*parent_getobjsize)( this_object, status );
result += astGetObjSize( this->plotxy );
result += astGetObjSize( this->plotxz );
result += astGetObjSize( this->plotyz );
/* If an error occurred, clear the result value. */
if ( !astOK ) result = 0;
/* Return the result, */
return result;
}
static void Grid( AstPlot *this_plot, int *status ) {
/*
* Name:
* Grid
* Purpose:
* Draw a set of labelled coordinate axes.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* void Grid( AstPlot *this, int *status )
* Class Membership:
* Plot member function (over-rides the astGrid method inherited from
* the Plot class).
* Description:
* This function draws a complete annotated set of 3-dimensional
* coordinate axes for a Plot3D with (optionally) a coordinate grid
* superimposed.
* Parameters:
* this
* Pointer to the Plot3D.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
AstPlot *baseplot;
AstPlot *plot;
AstPlot3D *this;
AstPointSet *majticks;
AstPointSet *minticks;
AstPointSet *tmp;
AstPointSet *wcsmajticks;
AstPointSet *wcsminticks;
const char *edge;
double **ptrmin;
double **ptrmaj;
double gcon;
int base_wax2d;
int itick;
int new_gaxis;
int new_plot;
int new_wax2d;
int nmaj;
int nmin;
int perm[ 2 ];
int rootcorner;
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the Plot3D structure. */
this = (AstPlot3D *) this_plot;
/* Invoke the astGrid method on the 2D base Plot. Both WCS axes in this
Plot are inherited from the 3D FrameSet that was supplied when the Plot3D
was constructed, and will be labelled. The other two Plots encapsulated
in the Plot3D only inherit a single axis from the original 3D FrameSet
(a dummy axis is used for the second WCS axis in these Plots). */
baseplot = GET_PLOT( this->baseplot );
astGrid( baseplot );
/* We next use astGrid to draw a grid on the 2D plane that shares the first
base plot graphics axis. Get the identifier for this Plot and the 2D
graphics axis index within the Plot that corresponds to the first base
plot graphics axis. Also get the constant value on the 3rd graphics
axis over the base plot */
rootcorner = astGetRootCorner( this );
if( this->baseplot == XY ) {
new_plot = XZ;
new_gaxis = 0;
gcon = this->gbox[ ( rootcorner & 4 ) ? 5 : 2 ];
} else if( this->baseplot == XZ ) {
new_plot = XY;
new_gaxis = 0;
gcon = this->gbox[ ( rootcorner & 2 ) ? 4 : 1 ];
} else {
new_plot = XY;
new_gaxis = 1;
gcon = this->gbox[ ( rootcorner & 1 ) ? 3 : 0 ];
}
/* Get a pointer to the Plot upon which astGrid is about to be invoked. */
plot = GET_PLOT( new_plot );
/* Find which 2D WCS axis was labelled on graphics axis 0 (the bottom or
top edge) of the base plot. */
if( ( edge = astGetC( baseplot, "Edge(1)" ) ) ) {
base_wax2d = ( !strcmp( edge, "bottom" ) || !strcmp( edge, "top" ) ) ? 0 : 1;
} else {
base_wax2d = 0;
}
/* Get the 2D graphics coords within the base Plot at which the tick
marks were drawn for this 2D WCS axis. Extend the PointSets holding
the major tick values to include an extra tick above and below the
ticks drawn by astGrid. These extra ticks are placed outside the
bounds of the plot. This ensures that the curves on the other axis
extend the full width of the plot. */
tmp = astGetDrawnTicks( baseplot, base_wax2d, 1 );
if( tmp ) {
majticks = ExtendTicks( baseplot, tmp, status );
nmaj = astGetNpoint( majticks );
ptrmaj = astGetPoints( majticks );
tmp = astAnnul( tmp );
} else {
majticks = NULL;
nmaj = 0;
ptrmaj = NULL;
}
minticks = astGetDrawnTicks( baseplot, base_wax2d, 0 );
if( minticks ) {
nmin = astGetNpoint( minticks );
ptrmin = astGetPoints( minticks );
} else {
nmin = 0;
ptrmin = NULL;
}
/* All the tick marks will have a constant value for the 2D graphics axis
that is not being ticked (axis 1 at the moment). Change this constant
value to be the value appropriate to the new Plot. */
if( ptrmaj && ptrmin ) {
for( itick = 0; itick < nmaj; itick++ ) ptrmaj[ 1 ][ itick ] = gcon;
for( itick = 0; itick < nmin; itick++ ) ptrmin[ 1 ][ itick ] = gcon;
}
/* If required, permute the axes in the tick mark positions. */
if( new_gaxis != 0 ) {
perm[ 0 ] = 1;
perm[ 1 ] = 0;
if( majticks ) astPermPoints( majticks, 1, perm );
if( minticks ) astPermPoints( minticks, 1, perm );
}
/* Transform the tick mark positions from 2D graphics coords to 2D WCS
coords. */
wcsmajticks = majticks ? astTransform( plot, majticks, 1, NULL ) : NULL;
wcsminticks = minticks ? astTransform( plot, minticks, 1, NULL ) : NULL;
/* Find the index of the 2D WCS axis that will be labelled on the bottom
or top edge (i.e. 2D graphics axis zero) of the new Plot. */
if( ( edge = astGetC( plot, "Edge(1)" ) ) ) {
new_wax2d = ( !strcmp( edge, "bottom" ) || !strcmp( edge, "top" ) ) ? 0 : 1;
} else {
new_wax2d = 0;
}
/* Use the other WCS axis if we are ticking the left or right edge (i.e.
2D graphics axis one) of the new Plot. This gives us the index of the
2D WCS axis for which tick values are to be stored in the Plot. */
if( new_gaxis == 1 ) new_wax2d = 1 - new_wax2d;
/* Store the tick mark values to be used on this WCS axis. */
ptrmaj = wcsmajticks ? astGetPoints( wcsmajticks ) : NULL;
ptrmin = wcsminticks ? astGetPoints( wcsminticks ) : NULL;
if( ptrmaj && ptrmin ) {
astSetTickValues( plot, new_wax2d, nmaj, ptrmaj[ new_gaxis ],
nmin, ptrmin[ new_gaxis ] );
}
/* Invoke the astGrid method on this plot. */
astGrid( plot );
/* Clear the stored tick values in the plot. */
astSetTickValues( plot, new_wax2d, 0, NULL, 0, NULL );
/* Free resources */
if( wcsmajticks ) wcsmajticks = astAnnul( wcsmajticks );
if( wcsminticks ) wcsminticks = astAnnul( wcsminticks );
if( majticks ) majticks = astAnnul( majticks );
if( minticks ) minticks = astAnnul( minticks );
/* We next use astGrid to draw a grid on the 2D plane that shares the
second base plot graphics axis. Get the identifier for this Plot and the
2D graphics axis index within the Plot that corresponds to the first
base plot graphics axis. */
if( this->baseplot == XY ) {
new_plot = YZ;
new_gaxis = 0;
} else if( this->baseplot == XZ ) {
new_plot = YZ;
new_gaxis = 1;
} else {
new_plot = XZ;
new_gaxis = 1;
}
/* Get a pointer to the Plot upon which astGrid is about to be invoked. */
plot = GET_PLOT( new_plot );
/* Find which 2D WCS axis was labelled on graphics axis 1 (the left or
right edge) of the base plot. */
base_wax2d = 1 - base_wax2d;
/* Get the 2D graphics coords within the base Plot at which the tick
marks were drawn for this 2D WCS axis. Extend the PointSets holding
the major tick values to include an extra tick above and below the
ticks drawn by astGrid. These extra ticks are placed outside the
bounds of the plot. This ensures that the curves on the other axis
extend the full width of the plot. */
tmp = astGetDrawnTicks( baseplot, base_wax2d, 1 );
if( tmp ) {
majticks = ExtendTicks( baseplot, tmp, status );
nmaj = astGetNpoint( majticks );
ptrmaj = astGetPoints( majticks );
tmp = astAnnul( tmp );
} else {
majticks = NULL;
nmaj = 0;
ptrmaj = NULL;
}
minticks = astGetDrawnTicks( baseplot, base_wax2d, 0 );
if( minticks ) {
nmin = astGetNpoint( minticks );
ptrmin = astGetPoints( minticks );
} else {
nmin = 0;
ptrmin = NULL;
}
/* All the tick marks will have a constant value for the 2D graphics axis
that is not being ticked (axis 0 at the moment). Change this constant
value to be the value appropriate to the new Plot. */
if( ptrmaj && ptrmin ) {
for( itick = 0; itick < nmaj; itick++ ) ptrmaj[ 0 ][ itick ] = gcon;
for( itick = 0; itick < nmin; itick++ ) ptrmin[ 0 ][ itick ] = gcon;
}
/* If required, permute the axes in the tick mark positions. */
if( new_gaxis != 1 ) {
perm[ 0 ] = 1;
perm[ 1 ] = 0;
if( majticks ) astPermPoints( majticks, 1, perm );
if( minticks ) astPermPoints( minticks, 1, perm );
}
/* Transform the tick mark positions from 2D graphics coords to 2D WCS
coords. */
wcsmajticks = majticks ? astTransform( plot, majticks, 1, NULL ) : NULL;
wcsminticks = minticks ? astTransform( plot, minticks, 1, NULL ) : NULL;
/* Find the index of the 2D WCS axis that will be labelled on the bottom
or top edge (i.e. 2D graphics axis zero) of the new Plot. */
if( ( edge = astGetC( plot, "Edge(1)" ) ) ) {
new_wax2d = ( !strcmp( edge, "bottom" ) || !strcmp( edge, "top" ) ) ? 0 : 1;
} else {
new_wax2d = 0;
}
/* Use the other WCS axis if we are ticking the left or right edge (i.e.
2D graphics axis one) of the new Plot. This gives us the index of the
2D WCS axis for which tick values are to be stored in the Plot. */
if( new_gaxis == 1 ) new_wax2d = 1 - new_wax2d;
/* Store the tick mark values to be used on this WCS axis. */
ptrmaj = wcsmajticks ? astGetPoints( wcsmajticks ) : NULL;
ptrmin = wcsminticks ? astGetPoints( wcsminticks ) : NULL;
if( ptrmaj && ptrmin ) {
astSetTickValues( plot, new_wax2d, nmaj, ptrmaj[ new_gaxis ],
nmin, ptrmin[ new_gaxis ] );
}
/* Invoke the astGrid method on this plot. */
astGrid( plot );
/* Clear the stored tick values in the plot. */
astSetTickValues( plot, new_wax2d, 0, NULL, 0, NULL );
/* Free resources */
if( wcsmajticks ) wcsmajticks = astAnnul( wcsmajticks );
if( wcsminticks ) wcsminticks = astAnnul( wcsminticks );
if( majticks ) majticks = astAnnul( majticks );
if( minticks ) minticks = astAnnul( minticks );
}
void astInitPlot3DVtab_( AstPlot3DVtab *vtab, const char *name, int *status ) {
/*
*+
* Name:
* astInitPlot3DVtab
* Purpose:
* Initialise a virtual function table for a Plot3D.
* Type:
* Protected function.
* Synopsis:
* #include "plot3d.h"
* void astInitPlot3DVtab( AstPlot3DVtab *vtab, const char *name )
* Class Membership:
* Plot3D vtab initialiser.
* Description:
* This function initialises the component of a virtual function
* table which is used by the Plot3D 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 */
AstFrame *dummy_frame; /* The Frame to put in dummy_frameset */
AstPlotVtab *plot; /* Pointer to Plot component of Vtab */
AstFrameSetVtab *fset; /* Pointer to FrameSet component of Vtab */
AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */
AstObjectVtab *object; /* Pointer to Object 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. */
astInitPlotVtab( (AstPlotVtab *) vtab, name );
/* Store a unique "magic" value in the virtual function table. This
will be used (by astIsAPlot3D) 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 = &(((AstPlotVtab *) vtab)->id);
/* Initialise member function pointers. */
/* ------------------------------------ */
/* Store pointers to the member functions (implemented here) that
provide virtual methods for this class. */
#define SET_PLOT3D_ACCESSORS(attr) \
vtab->Set##attr = Set##attr; \
vtab->Get##attr = Get##attr; \
vtab->Test##attr = Test##attr; \
vtab->Clear##attr = Clear##attr;
SET_PLOT3D_ACCESSORS(RootCorner)
SET_PLOT3D_ACCESSORS(Norm)
#undef SET_PLOT3D_ACCESSORS
/* Save the inherited pointers to methods that will be extended, and
replace them with pointers to the new member functions. */
object = (AstObjectVtab *) vtab;
fset = (AstFrameSetVtab *) vtab;
mapping = (AstMappingVtab *) vtab;
plot = (AstPlotVtab *) vtab;
parent_getobjsize = object->GetObjSize;
object->GetObjSize = GetObjSize;
parent_equal = object->Equal;
object->Equal = Equal;
parent_vset = object->VSet;
object->VSet = VSet;
parent_cast = object->Cast;
object->Cast = Cast;
parent_clear = object->Clear;
object->Clear = Clear;
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;
parent_clearcurrent = fset->ClearCurrent;
fset->ClearCurrent = ClearCurrent;
parent_setcurrent = fset->SetCurrent;
fset->SetCurrent = SetCurrent;
parent_removeframe = fset->RemoveFrame;
fset->RemoveFrame = RemoveFrame;
#if defined(THREAD_SAFE)
parent_managelock = object->ManageLock;
object->ManageLock = ManageLock;
#endif
/* Define a macro to override attribute accessors inherited from the
parent Plot class. First do axis specific attributes. */
#define SET_PLOT_ACCESSORS(attr) \
parent_get##attr = plot->Get##attr; \
plot->Get##attr = Get##attr; \
parent_set##attr = plot->Set##attr; \
plot->Set##attr = Set##attr; \
parent_clear##attr = plot->Clear##attr; \
plot->Clear##attr = Clear##attr;
/* Use the above macro to override all the inherited attribute accessors. */
SET_PLOT_ACCESSORS(MinTick)
SET_PLOT_ACCESSORS(Abbrev)
SET_PLOT_ACCESSORS(Gap)
SET_PLOT_ACCESSORS(LogGap)
SET_PLOT_ACCESSORS(LogPlot)
SET_PLOT_ACCESSORS(LogTicks)
SET_PLOT_ACCESSORS(LogLabel)
SET_PLOT_ACCESSORS(LabelUp)
SET_PLOT_ACCESSORS(DrawAxes)
SET_PLOT_ACCESSORS(LabelUnits)
SET_PLOT_ACCESSORS(MinTickLen)
SET_PLOT_ACCESSORS(MajTickLen)
SET_PLOT_ACCESSORS(NumLab)
SET_PLOT_ACCESSORS(NumLabGap)
SET_PLOT_ACCESSORS(TextLab)
SET_PLOT_ACCESSORS(TextLabGap)
#undef SET_PLOT_ACCESSORS
/* Now do attributes that are not axis specific. */
#define SET_PLOT_ACCESSORS(attr) \
parent_set##attr = plot->Set##attr; \
plot->Set##attr = Set##attr; \
parent_clear##attr = plot->Clear##attr; \
plot->Clear##attr = Clear##attr;
SET_PLOT_ACCESSORS(Ink)
SET_PLOT_ACCESSORS(Tol)
SET_PLOT_ACCESSORS(Invisible)
SET_PLOT_ACCESSORS(TickAll)
SET_PLOT_ACCESSORS(ForceExterior)
SET_PLOT_ACCESSORS(Border)
SET_PLOT_ACCESSORS(Clip)
SET_PLOT_ACCESSORS(ClipOp)
SET_PLOT_ACCESSORS(Escape)
SET_PLOT_ACCESSORS(Grid)
SET_PLOT_ACCESSORS(Labelling)
SET_PLOT_ACCESSORS(Style)
SET_PLOT_ACCESSORS(Font)
SET_PLOT_ACCESSORS(Colour)
SET_PLOT_ACCESSORS(Width)
SET_PLOT_ACCESSORS(Size)
#undef SET_PLOT_ACCESSORS
/* Store replacement pointers for methods which will be over-ridden by new
member functions implemented here. */
plot->Grid = Grid;
plot->Text = Text;
plot->SetTickValues = SetTickValues;
plot->PolyCurve = PolyCurve;
plot->Border = Border;
plot->BoundingBox = BoundingBox;
plot->GetGrfContext = GetGrfContext;
plot->GrfPop = GrfPop;
plot->GrfPush = GrfPush;
plot->GrfSet = GrfSet;
plot->GridLine = GridLine;
plot->Mark = Mark;
plot->Curve = Curve;
plot->GenCurve = GenCurve;
plot->Clip = Clip;
mapping->Transform = Transform;
/* Declare the copy constructor, destructor and class dump
function. */
astSetCopy( vtab, Copy );
astSetDelete( vtab, Delete );
astSetDump( vtab, Dump, "Plot3D", "Provide facilities for 3D graphical output" );
/* Create a FrameSet that can be used when calling astCast to indicate
the class to which we want to cast. */
LOCK_MUTEX3
if( !dummy_frameset ) {
dummy_frame = astFrame( 1, " ", status );
dummy_frameset = astFrameSet( dummy_frame, " ", status );
dummy_frame = astAnnul( dummy_frame );
}
UNLOCK_MUTEX3
/* 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) );
}
}
#if defined(THREAD_SAFE)
static int ManageLock( AstObject *this_object, int mode, int extra,
AstObject **fail, int *status ) {
/*
* Name:
* ManageLock
* Purpose:
* Manage the thread lock on an Object.
* Type:
* Private function.
* Synopsis:
* #include "object.h"
* AstObject *ManageLock( AstObject *this, int mode, int extra,
* AstObject **fail, int *status )
* Class Membership:
* CmpMap member function (over-rides the astManageLock protected
* method inherited from the parent class).
* Description:
* This function manages the thread lock on the supplied Object. The
* lock can be locked, unlocked or checked by this function as
* deteremined by parameter "mode". See astLock for details of the way
* these locks are used.
* Parameters:
* this
* Pointer to the Object.
* mode
* An integer flag indicating what the function should do:
*
* AST__LOCK: Lock the Object for exclusive use by the calling
* thread. The "extra" value indicates what should be done if the
* Object is already locked (wait or report an error - see astLock).
*
* AST__UNLOCK: Unlock the Object for use by other threads.
*
* AST__CHECKLOCK: Check that the object is locked for use by the
* calling thread (report an error if not).
* extra
* Extra mode-specific information.
* fail
* If a non-zero function value is returned, a pointer to the
* Object that caused the failure is returned at "*fail". This may
* be "this" or it may be an Object contained within "this". Note,
* the Object's reference count is not incremented, and so the
* returned pointer should not be annulled. A NULL pointer is
* returned if this function returns a value of zero.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A local status value:
* 0 - Success
* 1 - Could not lock or unlock the object because it was already
* locked by another thread.
* 2 - Failed to lock a POSIX mutex
* 3 - Failed to unlock a POSIX mutex
* 4 - Bad "mode" value supplied.
* Notes:
* - This function attempts to execute even if an error has already
* occurred.
*/
/* Local Variables: */
AstPlot3D *this; /* Pointer to Plot3D structure */
int result; /* Returned status value */
/* Initialise */
result = 0;
/* Check the supplied pointer is not NULL. */
if( !this_object ) return result;
/* Obtain a pointers to the Plot3D structure. */
this = (AstPlot3D *) this_object;
/* Invoke the ManageLock method inherited from the parent class. */
if( !result ) result = (*parent_managelock)( this_object, mode, extra,
fail, status );
/* Invoke the astManageLock method on any Objects contained within
the supplied Object. */
if( !result ) result = astManageLock( this->plotxy, mode, extra, fail );
if( !result ) result = astManageLock( this->plotxz, mode, extra, fail );
if( !result ) result = astManageLock( this->plotyz, mode, extra, fail );
return result;
}
#endif
static void Mark( AstPlot *this_plot, int nmark, int ncoord, int indim,
const double *in, int type, int *status ){
/*
* Name:
* Mark
* Purpose:
* Draw a set of markers for a Plot3D.
* Type:
* Private member function.
* Synopsis:
* #include "plot3d.h"
* void Mark( AstPlot *this, int nmark, int ncoord, int indim,
* const double *in, int type, int *status )
* Class Membership:
* Plot3d member function (overrides the astMark method inherited form
* the parent Plot class).
* Description:
* This function draws a set of markers (symbols) at positions
* specified in the physical coordinate system of a Plot3D. The
* positions are transformed into graphical coordinates to
* determine where the markers should appear within the plotting
* area.
*
* They are drawn on a 2D plane that has a normal vector given by the
* current value of the Plot3D's "Norm" attribute.
* Parameters:
* this
* Pointer to the Plot3D.
* nmark
* The number of markers to draw. This may be zero, in which
* case nothing will be drawn.
* ncoord
* The number of coordinates being supplied for each mark
* (i.e. the number of axes in the current Frame of the Plot, as
* given by its Naxes attribute).
* indim
* The number of elements along the second dimension of the "in"
* array (which contains the marker coordinates). This value is
* required so that the coordinate values can be correctly
* located if they do not entirely fill this array. The value
* given should not be less than "nmark".
* in
* The address of the first element of a 2-dimensional array of
* shape "[ncoord][indim]" giving the
* physical coordinates of the points where markers are to be
* drawn. These should be stored such that the value of
* coordinate number "coord" for input mark number "mark" is
* found in element "in[coord][mark]".
* type
* A value specifying the type (e.g. shape) of marker to be
* drawn. The set of values which may be used (and the shapes
* that will result) is determined by the underlying graphics
* system.
* status
* Pointer to the inherited status variable.
* Notes:
* - Markers are not drawn at positions which have any coordinate
* equal to the value AST__BAD (or where the transformation into
* graphical coordinates yields coordinates containing the value
* AST__BAD).
* - An error results if the base Frame of the Plot is not 3-dimensional.
* - An error also results if the transformation between the
* current and base Frames of the Plot is not defined (i.e. the
* Plot's TranInverse attribute is zero).
*/
/* Local Variables: */
AstMapping *mapping; /* Pointer to graphics->physical mapping */
AstPlot3D *this; /* Pointer to the Plot3D structure */
AstPointSet *pset1; /* PointSet holding physical positions */
AstPointSet *pset2; /* PointSet holding graphics positions */
const char *class; /* Object class */
const char *method; /* Current method */
const double **ptr1; /* Pointer to physical positions */
double **ptr2; /* Pointer to graphics positions */
double *xpd; /* Pointer to next double precision x value */
double *ypd; /* Pointer to next double precision y value */
double *zpd; /* Pointer to next double precision z value */
float *x; /* Pointer to single precision x values */
float *xpf; /* Pointer to next single precision x value */
float *y; /* Pointer to single precision y values */
float *ypf; /* Pointer to next single precision y value */
float *z; /* Pointer to single precision z values */
float *zpf; /* Pointer to next single precision z value */
float norm[ 3 ]; /* Single precision normal vector */
int axis; /* Axis index */
int i; /* Loop count */
int naxes; /* No. of axes in the base Frame */
int nn; /* Number of good marker positions */
/* Check the global error status. */
if ( !astOK ) return;
/* Get a pointer to the Plot3D structure. */
this = (AstPlot3D *) this_plot;
/* Store the current method and class for inclusion in error messages
generated by lower level functions. */
method = "astMark";
class = astClass( this );
/* Check the base Frame of the Plot is 3-D. */
naxes = astGetNin( this );
if( naxes != 3 && astOK ){
astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base "
"Frame of the supplied %s is invalid - this number should "
"be 3.", status, method, class, naxes, class );
}
/* Also validate the input array dimension argument. */
if ( astOK && ( indim < nmark ) ) {
astError( AST__DIMIN, "%s(%s): The input array dimension value "
"(%d) is invalid.", status, method, class, indim );
astError( AST__DIMIN, "This should not be less than the number of "
"markers being drawn (%d).", status, nmark );
}
/* Establish the correct graphical attributes as defined by attributes
with the supplied Plot. */
astGrfAttrs( this, AST__MARKS_ID, 1, GRF__MARK, method, class );
/* Create a PointSet to hold the supplied physical coordinates. */
pset1 = astPointSet( nmark, ncoord, "", status );
/* Allocate memory to hold pointers to the first value on each axis. */
ptr1 = (const double **) astMalloc( sizeof( const double * )*
(size_t)( ncoord ));
/* Check the pointer can be used, then store pointers to the first value
on each axis. */
if( astOK ){
for( axis = 0; axis < ncoord; axis++ ){
ptr1[ axis ] = in + axis*indim;
}
}
/* Store these pointers in the PointSet. */
astSetPoints( pset1, (double **) ptr1 );
/* Transform the supplied data from the current frame (i.e. physical
coordinates) to the base frame (i.e. graphics coordinates) using
the inverse Mapping defined by the Plot. */
mapping = astGetMapping( this, AST__BASE, AST__CURRENT );
pset2 = astTransform( mapping, pset1, 0, NULL );
mapping = astAnnul( mapping );
/* Get pointers to the graphics coordinates. */
ptr2 = astGetPoints( pset2 );
/* Allocate memory to hold single precision versions of the graphics
coordinates. */
x = (float *) astMalloc( sizeof( float )*(size_t) nmark );
y = (float *) astMalloc( sizeof( float )*(size_t) nmark );
z = (float *) astMalloc( sizeof( float )*(size_t) nmark );
/* Check the pointers can be used. */
if( astOK ){
/* Store pointers to the next single and double precision x, y and z
values. */
xpf = x;
ypf = y;
zpf = z;
xpd = ptr2[ 0 ];
ypd = ptr2[ 1 ];
zpd = ptr2[ 2 ];
/* Convert the double precision values to single precision, rejecting
any bad marker positions. */
nn = 0;
for( i = 0; i < nmark; i++ ){
if( *xpd != AST__BAD && *ypd != AST__BAD && *zpd != AST__BAD ){
nn++;
*(xpf++) = (float) *(xpd++);
*(ypf++) = (float) *(ypd++);
*(zpf++) = (float) *(zpd++);
} else {
xpd++;
ypd++;
zpd++;
}
}
/* If the nornmal vector has non-zero length, draw the remaining markers. */
norm[ 0 ] = (float) astGetNorm( this, 0 );
norm[ 1 ] = (float) astGetNorm( this, 1 );
norm[ 2 ] = (float) astGetNorm( this, 2 );
if( norm[ 0 ] != 0.0 || norm[ 1 ] != 0.0 || norm[ 2 ] != 0.0 ){
/* Since we are about to call an external function which may not be
thread safe, prevent any other thread from executing the following code
until the current thread has finished executing it. */
LOCK_MUTEX2;
if( !astG3DMark( nn, x, y, z, type, norm ) ) {
astError( AST__GRFER, "%s(%s): Graphics error in astG3DMark. ", status,
method, class );
}
/* Allow the next thread to proceed. */
UNLOCK_MUTEX2;
} else if( astOK ) {
astError( AST__ATTIN, "%s(%s): The vector specified by the Norm "
"attribute has zero length.", status, method, class );
}
}
/* Free the memory used to store single precision graphics coordinates. */
x = (float *) astFree( (void *) x );
y = (float *) astFree( (void *) y );
z = (float *) astFree( (void *) z );
/* Annul the PointSets. */
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
/* Free the memory holding the pointers to the first value on each axis. */
ptr1 = (const double **) astFree( (void *) ptr1 );
/* Re-establish the original graphical attributes. */
astGrfAttrs( this, AST__MARKS_ID, 0, GRF__MARK, method, class );
/* Return */
return;
}
static int Plot3DAttr( AstKeyMap *grfconID, int attr, double value,
double *old_value, int prim ){
/*
* Name:
* Plot3DAttr
* Purpose:
* Get or set the value of a 3D grf attribute.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* int Plot3DAttr( AstKeyMap *grfconID, int attr, double value,
* double *old_value, int prim )
* Class Membership:
* Plot3D member function.
* Description:
* This function is called by one of the three encapsulated 2D Plots to
* get or set the current value of a specified graphics attribute. It
* forwards the call to the grf3D module being used by this Plot3D. It
* should be registered with each of the 2D Plots using astGrfSet.
* Parameters:
* grfconID
* The Plot's GrfContext KeyMap. This is used to identify which of
* the three Plots is calling this function.
* attr
* An integer value identifying the required attribute. This should
* be one of the symbolic values defined in grf.h.
* value
* A new value to store for the attribute. If this is AST__BAD
* no value is stored.
* old_value
* A pointer to a double in which to return the attribute value.
* If this is NULL, no value is returned.
* prim
* The sort of graphics primitive to be drawn with the new attribute.
* Identified by one of the values defined in grf.h.
* Returned Value:
* An integer value of 0 is returned if an error occurs, and 1 otherwise.
*/
/* Local Variables: */
int result;
int *status;
/* Get a pointer to the inherited status value. */
status = astGetStatusPtr;
/* Check the inherited status. */
if( !astOK ) return 0;
/* Since we are about to call an external function which may not be
thread safe, prevent any other thread from executing the following code
until the current thread has finished executing it. */
LOCK_MUTEX2;
/* Use the function in the external Grf3D module, selected at link-time
using ast_link options. Note, attribute values are the same for each
of the three Plot. */
result = astG3DAttr( attr, value, old_value, prim );
/* Allow the next thread to proceed. */
UNLOCK_MUTEX2;
/* Return the result. */
return result;
}
static int Plot3DCap( AstKeyMap *grfconID, int cap, int value ){
/*
* Name:
* Plot3DCap
* Purpose:
* Determine if the 3D grf module has a given capability.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* int Plot3DCap( AstKeyMap *grfconID, int cap, int value )
* Class Membership:
* Plot3D member function.
* Description:
* This function is called by one of the three encapsulated 2D Plots to
* determine if a given graphics capability is available. It forwards
* the call to the grf3D module being used by this Plot3D. It should be
* registered with each of the 2D Plots using astGrfSet.
* Parameters:
* grfconID
* The Plot's GrfContext KeyMap. This is
* used to identify which of the three Plots is calling this function.
* cap
* The capability being inquired about. This will be one of the
* following constants defined in grf.h: GRF__SCALES, GRF__MJUST,
* GRF__ESC,
* value
* The use of this parameter depends on the value of "cap" as
* described in the documentation for the astGrfSet function in the
* Plot class.
* Returned Value:
* The value returned by the function depends on the value of "cap"
* as described in the astGrfSet documentation. Zero is returned if
* the supplied capability is not recognised.
*/
/* Local Variables: */
int result;
int *status;
/* Get a pointer to the inherited status value. */
status = astGetStatusPtr;
/* Check the inherited status. */
if( !astOK ) return 0;
/* The astGScales function is implemented by code within the Plot3D class
(not within the grf3D module). The Plot3D class assumes all axes are
equally scaled. */
if( cap == GRF__SCALES ) {
result = 1;
/* All grf3D modules are assumed to support "M" justification. */
} else if( cap == GRF__MJUST ) {
result = 1;
/* Forward all other capability requests to the grf3D module. */
} else {
/* Since we are about to call an external function which may not be
thread safe, prevent any other thread from executing the following code
until the current thread has finished executing it. */
LOCK_MUTEX2;
result = astG3DCap( cap, value );
/* Allow the next thread to proceed. */
UNLOCK_MUTEX2;
}
/* Return the result. */
return result;
}
static int Plot3DFlush( AstKeyMap *grfconID ){
/*
* Name:
* Plot3DFlush
* Purpose:
* Flush any buffered graphical output.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* int Plot3DFlush( AstKeyMap *grfconID )
* Class Membership:
* Plot3D member function.
* Description:
* This function is called by one of the three encapsulated 2D Plots to
* ensure that the display device is up-to-date by flushing any pending
* graphics to the output device. It forwards the call to the grf3D module
* being used by this Plot3D. It should be registered with each of the
* 2D Plots using astGrfSet.
* Parameters:
* grfconID
* The Plot's GrfContext KeyMap. This is
* used to identify which of the three Plots is calling this function.
* Returned Value:
* An integer value of 0 is returned if an error occurs, and 1 otherwise.
*/
/* Local Variables: */
int result;
int *status;
/* Get a pointer to the inherited status value. */
status = astGetStatusPtr;
/* Check the inherited status. */
if( !astOK ) return 0;
/* Since we are about to call an external function which may not be
thread safe, prevent any other thread from executing the following code
until the current thread has finished executing it. */
LOCK_MUTEX2;
/* Use the function in the external Grf3D module, selected at link-time
using ast_link options. */
result = astG3DFlush();
/* Allow the next thread to proceed. */
UNLOCK_MUTEX2;
/* Return the result. */
return result;
}
static int Plot3DLine( AstKeyMap *grfconID, int n, const float *x, const float *y ){
/*
* Name:
* Plot3DLine
* Purpose:
* Draw a polyline on a 2D surface.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* int Plot3DLine( AstKeyMap *grfconID, int n, const float *x,
* const float *y )
* Class Membership:
* Plot3D member function.
* Description:
* This function is called by one of the three encapsulated 2D Plots to
* draw a polyline. It forwards the call to the grf3D module being used
* by this Plot3D. It should be registered with each of the 2D Plots
* using astGrfSet.
* Parameters:
* grfconID
* The Plot's GrfContext KeyMap. This is
* used to identify which of the three Plots is calling this function.
* n
* The number of positions to be joined together.
* x
* A pointer to an array holding the "n" x values.
* y
* A pointer to an array holding the "n" y values.
* Returned Value:
* An integer value of 0 is returned if an error occurs, and 1 otherwise.
*/
/* Local Variables: */
AstKeyMap *grfcon;
double gcon;
float *work;
float *x3d = NULL;
float *y3d = NULL;
float *z3d = NULL;
int i;
int plane;
int result = 0;
int *status;
/* Get a pointer to the inherited status value. */
status = astGetStatusPtr;
/* Check the inherited status. */
if( !astOK ) return result;
/* Get a genuine pointer from the supplied grfcon identifier. */
grfcon = astMakePointer( grfconID );
/* Report an error if no grfcon object was supplied. */
if( !grfcon ) {
astError( AST__INTER, "astG3DLine(Plot3D): No grfcon Object supplied "
"(internal AST programming error)." , status);
/* If a grfcon Object was supplied, get the graphics box array out of it. */
} else if( !astMapGet0D( grfcon, "Gcon", &gcon ) ) {
astError( AST__INTER, "astG3DLine(Plot3D): No \"Gcon\" key found "
"in the supplied grfcon Object (internal AST programming "
"error)." , status);
/* Also get the plane index out of it. */
} else if( !astMapGet0I( grfcon, "Plane", &plane ) ) {
astError( AST__INTER, "astG3DLine(Plot3D): No \"Plane\" key found "
"in the supplied grfcon Object (internal AST programming "
"error)." , status);
}
/* Allocate memory to hold the "n" values for the missing coordinate. */
work = astMalloc( sizeof( float )*(size_t) n );
if( work ) {
/* Set up pointers to the x, y and z arrays in the 3D graphics system.
Fill the missing array with suitable values (the constant value of
the third axis on the plane being drawn). */
if( plane == XY ) {
x3d = (float *) x;
y3d = (float *) y;
z3d = work;
for( i = 0; i < n; i++ ) z3d[ i ] = gcon;
} else if( plane == XZ ) {
x3d = (float *) x;
y3d = work;
z3d = (float *) y;
for( i = 0; i < n; i++ ) y3d[ i ] = gcon;
} else if( plane == YZ ) {
x3d = work;
y3d = (float *) x;
z3d = (float *) y;
for( i = 0; i < n; i++ ) x3d[ i ] = gcon;
} else {
astError( AST__INTER, "astG3DLine(Plot3D): Illegal plane "
"identifier %d supplied (internal AST programming "
"error).", status, plane );
}
/* If ok, draw the lines in the 3D graphics coordinate space. */
if( x3d ) {
/* Since we are about to call an external function which may not be
thread safe, prevent any other thread from executing the following code
until the current thread has finished executing it. */
LOCK_MUTEX2;
/* Draw the line */
result = astG3DLine( n, x3d, y3d, z3d );
/* Allow the next thread to proceed. */
UNLOCK_MUTEX2;
}
}
/* Free resources. */
work = astFree( work );
/* Return the result. */
return result;
}
static int Plot3DMark( AstKeyMap *grfconID, int n, const float *x,
const float *y, int type ){
/*
* Name:
* Plot3DMark
* Purpose:
* Draw a set of markers.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* int Plot3DMark( AstKeyMap *grfconID, int n, const float *x,
* const float *y, int type )
* Class Membership:
* Plot3D member function.
* Description:
* This function is called by one of the three encapsulated 2D Plots to
* draw a set of markers. It forwards the call to the grf3D module being
* used by this Plot3D. It should be registered with each of the 2D Plots
* using astGrfSet.
* Parameters:
* grfconID
* The Plot's GrfContext KeyMap. This is
* used to identify which of the three Plots is calling this function.
* n
* The number of markers to draw.
* x
* A pointer to an array holding the "n" x values.
* y
* A pointer to an array holding the "n" y values.
* type
* An integer which can be used to indicate the type of marker symbol
* required.
* Returned Value:
* An integer value of 0 is returned if an error occurs, and 1 otherwise.
*/
/* Local Variables: */
AstKeyMap *grfcon;
double gcon;
float *work;
float *x3d = NULL;
float *y3d = NULL;
float *z3d = NULL;
float norm[ 3 ];
int i;
int plane;
int rc;
int result = 0;
int *status;
/* Get a pointer to the inherited status value. */
status = astGetStatusPtr;
/* Check the inherited status. */
if( !astOK ) return result;
/* Get a genuine pointer from the supplied grfcon identifier. */
grfcon = astMakePointer( grfconID );
/* Report an error if no grfcon object was supplied. */
if( !grfcon ) {
astError( AST__INTER, "astG3DMark(Plot3D): No grfcon Object supplied "
"(internal AST programming error)." , status);
/* If a grfcon Object was supplied, get the graphics box array out of it. */
} else if( !astMapGet0D( grfcon, "Gcon", &gcon ) ) {
astError( AST__INTER, "astG3DMark(Plot3D): No \"Gcon\" key found "
"in the supplied grfcon Object (internal AST programming "
"error)." , status);
/* If a grfcon Object was supplied, get the RootCorner value out of it. */
} else if( !astMapGet0I( grfcon, "RootCorner", &rc ) ) {
astError( AST__INTER, "astG3DLine(Plot3D): No \"RootCornern\" key found "
"in the supplied grfcon Object (internal AST programming "
"error)." , status);
/* Also get the plane index out of it. */
} else if( !astMapGet0I( grfcon, "Plane", &plane ) ) {
astError( AST__INTER, "astG3DMark(Plot3D): No \"Plane\" key found "
"in the supplied grfcon Object (internal AST programming "
"error)." , status);
}
/* Allocate memory to hold the "n" values for the missing coordinate. */
work = astMalloc( sizeof( float )*(size_t) n );
if( work ) {
/* Set up pointers to the x, y and z arrays in the 3D graphics system.
Fill the missing array with suitable values (the constant value of
the third axis on the plane being drawn). Set the normal vector for
the markers so that they are drawn in the plane described by the Plot.*/
if( plane == XY ) {
x3d = (float *) x;
y3d = (float *) y;
z3d = work;
for( i = 0; i < n; i++ ) z3d[ i ] = gcon;
norm[ 0 ] = 0;
norm[ 1 ] = 0;
norm[ 2 ] = ( rc & 4 ) ? 1 : -1;
} else if( plane == XZ ) {
x3d = (float *) x;
y3d = work;
z3d = (float *) y;
for( i = 0; i < n; i++ ) y3d[ i ] = gcon;
norm[ 0 ] = 0;
norm[ 1 ] = ( rc & 2 ) ? 1 : -1;
norm[ 2 ] = 0;
} else if( plane == YZ ) {
x3d = work;
y3d = (float *) x;
z3d = (float *) y;
for( i = 0; i < n; i++ ) x3d[ i ] = gcon;
norm[ 0 ] = ( rc & 1 ) ? 1 : -1;
norm[ 1 ] = 0;
norm[ 2 ] = 0;
} else {
astError( AST__INTER, "astG3DMark(Plot3D): Illegal plane "
"identifier %d supplied (internal AST programming "
"error).", status, plane );
}
/* If ok, draw the markers in the 3D graphics coordinate space. */
if( x3d ) {
/* Since we are about to call an external function which may not be
thread safe, prevent any other thread from executing the following code
until the current thread has finished executing it. */
LOCK_MUTEX2;
/* Draw the markers */
result = astG3DMark( n, x3d, y3d, z3d, type, norm );
/* Allow the next thread to proceed. */
UNLOCK_MUTEX2;
}
}
/* Free resources. */
work = astFree( work );
/* Return the result. */
return result;
}
static int Plot3DQch( AstKeyMap *grfconID, float *chv, float *chh ){
/*
* Name:
* Plot3DQch
* Purpose:
* Get the current text size.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* int Plot3DQch( AstKeyMap *grfconID, float *chv, float *chh )
* Class Membership:
* Plot3D member function.
* Description:
* This function is called by one of the three encapsulated 2D Plots to
* get the current text size. It forwards the call to the grf3D module
* being used by this Plot3D. It should be registered with each of the
* 2D Plots using astGrfSet.
*
* The grf3D module assumes that the 3D graphics axes are equally
* scaled and therefore the text height does not depend on the text
* orientation. Therefore, "chv" and "chh" are returned holding the
* same value.
* Parameters:
* grfconID
* The Plot's GrfContext KeyMap. This is
* used to identify which of the three Plots is calling this function.
* chv
* A pointer to the float which is to receive the height of
* characters drawn with a vertical baseline in the 2D Plot. This
* will be an increment in the 2D X axis.
* chh
* A pointer to the float which is to receive the height of
* characters drawn with a horizontal baseline in the 2D Plot. This
* will be an increment in the 2D Y axis.
* Returned Value:
* An integer value of 0 is returned if an error occurs, and 1 otherwise.
*/
/* Local Variables: */
int result;
float ch;
int *status;
/* Get a pointer to the inherited status value. */
status = astGetStatusPtr;
/* Check the inherited status. */
if( !astOK ) return 0;
/* Since we are about to call an external function which may not be
thread safe, prevent any other thread from executing the following code
until the current thread has finished executing it. */
LOCK_MUTEX2;
/* Use the function in the external Grf3D module, selected at link-time
using ast_link options. Note, text height is the same for each
of the three Plot. */
result = astG3DQch( &ch );
/* Allow the next thread to proceed. */
UNLOCK_MUTEX2;
/* Store the value in both the returned values. */
*chv = ch;
*chh = ch;
/* Return the error flag. */
return result;
}
static int Plot3DScales( AstKeyMap *grfconID, float *alpha, float *beta ){
/*
* Name:
* Plot3DScales
* Purpose:
* Get the 2D axis scales.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* int Plot3DScales( AstKeyMap *grfconID, float *alpha, float *beta )
* Class Membership:
* Plot3D member function.
* Description:
* This function is called by one of the three encapsulated 2D Plots to
* get the relative scales of the 2D axes. Since the grf3D module
* assumes that all graphics axes are equally scaled, it just returns 1.0
* for alpha and beta.
* Parameters:
* grfconID
* The Plot's GrfContext KeyMap. This is
* used to identify which of the three Plots is calling this function.
* alpha
* A pointer to the float which is to receive the scale for the X
* axis
* beta
* A pointer to the float which is to receive the scale for the Y
* axis
* Returned Value:
* An integer value of 0 is returned if an error occurs, and 1 otherwise.
*/
*alpha = 1.0;
*beta = 1.0;
return 1;
}
static int Plot3DText( AstKeyMap *grfconID, const char *text, float x, float y,
const char *just, float upx, float upy ){
/*
* Name:
* Plot3DText
* Purpose:
* Draw a text string.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* int Plot3DText( AstKeyMap *grfconID, const char *text, float x, float y,
* const char *just, float upx, float upy )
* Class Membership:
* Plot3D member function.
* Description:
* This function is called by one of the three encapsulated 2D Plots to
* draw a text string. It forwards the call to the grf3D module being
* used by this Plot3D. It should be registered with each of the 2D Plots
* using astGrfSet.
* Parameters:
* grfconID
* The Plot's GrfContext KeyMap. This is
* used to identify which of the three Plots is calling this function.
* text
* Pointer to a null-terminated character string to be displayed.
* x
* The reference x coordinate.
* y
* The reference y coordinate.
* just
* A character string which specifies the location within the
* text string which is to be placed at the reference position
* given by x and y.
* upx
* The x component of the up-vector for the text.
* upy
* The y component of the up-vector for the text.
* Returned Value:
* An integer value of 0 is returned if an error occurs, and 1 otherwise.
*/
/* Local Variables: */
AstKeyMap *grfcon;
double gcon;
float norm[ 3 ];
float ref[ 3 ];
float up[ 3 ];
int plane;
int rc;
int result = 0;
int *status;
/* Get a pointer to the inherited status value. */
status = astGetStatusPtr;
/* Check the inherited status. */
if( !astOK ) return result;
/* Get a genuine pointer from the supplied grfcon identifier. */
grfcon = astMakePointer( grfconID );
/* Report an error if no grfcon object was supplied. */
if( !grfcon ) {
astError( AST__INTER, "astG3DText(Plot3D): No grfcon Object supplied "
"(internal AST programming error)." , status);
/* If a grfcon Object was supplied, get the graphics box array out of it. */
} else if( !astMapGet0D( grfcon, "Gcon", &gcon ) ) {
astError( AST__INTER, "astG3DText(Plot3D): No \"Gcon\" key found "
"in the supplied grfcon Object (internal AST programming "
"error)." , status);
/* If a grfcon Object was supplied, get the RootCorner value out of it. */
} else if( !astMapGet0I( grfcon, "RootCorner", &rc ) ) {
astError( AST__INTER, "astG3DLine(Plot3D): No \"RootCornern\" key found "
"in the supplied grfcon Object (internal AST programming "
"error)." , status);
/* Also get the plane index out of it. */
} else if( !astMapGet0I( grfcon, "Plane", &plane ) ) {
astError( AST__INTER, "astG3DText(Plot3D): No \"Plane\" key found "
"in the supplied grfcon Object (internal AST programming "
"error)." , status);
/* If OK, draw the text. */
} else {
/* Set up the reference, up and normal vectors so that the text appears
on the required plane. */
if( plane == XY ) {
ref[ 0 ] = x;
ref[ 1 ] = y;
ref[ 2 ] = gcon;
norm[ 0 ] = 0;
norm[ 1 ] = 0;
norm[ 2 ] = ( rc & 4 ) ? 1 : -1;
up[ 0 ] = upx;
up[ 1 ] = upy;
up[ 2 ] = 0;
} else if( plane == XZ ) {
ref[ 0 ] = x;
ref[ 1 ] = gcon;
ref[ 2 ] = y;
norm[ 0 ] = 0;
norm[ 1 ] = ( rc & 2 ) ? 1 : -1;
norm[ 2 ] = 0;
up[ 0 ] = upx;
up[ 1 ] = 0;
up[ 2 ] = upy;
} else if( plane == YZ ) {
ref[ 0 ] = gcon;
ref[ 1 ] = x;
ref[ 2 ] = y;
norm[ 0 ] = ( rc & 1 ) ? 1 : -1;
norm[ 1 ] = 0;
norm[ 2 ] = 0;
up[ 0 ] = 0;
up[ 1 ] = upx;
up[ 2 ] = upy;
} else {
astError( AST__INTER, "astG3DText(Plot3D): Illegal plane "
"identifier %d supplied (internal AST programming "
"error).", status, plane );
}
/* Since we are about to call an external function which may not be
thread safe, prevent any other thread from executing the following code
until the current thread has finished executing it. */
LOCK_MUTEX2;
/* If ok, draw the markers in the 3D graphics coordinate space. */
if( astOK ) result = astG3DText( text, ref, just, up, norm );
/* Allow the next thread to proceed. */
UNLOCK_MUTEX2;
}
/* Return the result. */
return result;
}
static int Plot3DTxExt( AstKeyMap *grfconID, const char *text, float x, float y,
const char *just, float upx, float upy, float *xb,
float *yb ){
/*
* Name:
* Plot3DTxExt
* Purpose:
* Determine the plotted extent of a text string.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* int Plot3DTxExt( AstKeyMap *grfconID, const char *text, float x,
* float y, const char *just, float upx, float upy,
* float *xb, float *yb )
* Class Membership:
* Plot3D member function.
* Description:
* This function is called by one of the three encapsulated 2D Plots to
* determine the extent a string would have if plotted using Plot3DText.
* It forwards the call to the grf3D module being used by this Plot3D. It
* should be registered with each of the 2D Plots using astGrfSet.
* Parameters:
* grfconID
* The Plot's GrfContext KeyMap. This is
* used to identify which of the three Plots is calling this function.
* text
* Pointer to a null-terminated character string to be displayed.
* x
* The reference x coordinate.
* y
* The reference y coordinate.
* just
* A character string which specifies the location within the
* text string which is to be placed at the reference position
* given by x and y.
* upx
* The x component of the up-vector for the text.
* upy
* The y component of the up-vector for the text.
* xb
* An array of 4 elements in which to return the x coordinate of
* each corner of the bounding box.
* yb
* An array of 4 elements in which to return the y coordinate of
* each corner of the bounding box.
* Returned Value:
* An integer value of 0 is returned if an error occurs, and 1 otherwise.
*/
/* Local Variables: */
AstKeyMap *grfcon;
double gcon;
float *xb3d = NULL;
float *yb3d = NULL;
float *zb3d = NULL;
float bl[ 3 ];
float norm[ 3 ];
float ref[ 3 ];
float unused[ 4 ];
float up[ 3 ];
int plane;
int rc;
int result = 0;
int *status;
/* Get a pointer to the inherited status value. */
status = astGetStatusPtr;
/* Check the inherited status. */
if( !astOK ) return result;
/* Get a genuine pointer from the supplied grfcon identifier. */
grfcon = astMakePointer( grfconID );
/* Report an error if no grfcon object was supplied. */
if( !grfcon ) {
astError( AST__INTER, "astG3DTxExt(Plot3D): No grfcon Object supplied "
"(internal AST programming error)." , status);
/* If a grfcon Object was supplied, get the graphics box array out of it. */
} else if( !astMapGet0D( grfcon, "Gcon", &gcon ) ) {
astError( AST__INTER, "astG3DTxExt(Plot3D): No \"Gcon\" key found "
"in the supplied grfcon Object (internal AST programming "
"error)." , status);
/* If a grfcon Object was supplied, get the RootCorner value out of it. */
} else if( !astMapGet0I( grfcon, "RootCorner", &rc ) ) {
astError( AST__INTER, "astG3DLine(Plot3D): No \"RootCornern\" key found "
"in the supplied grfcon Object (internal AST programming "
"error)." , status);
/* Also get the plane index out of it. */
} else if( !astMapGet0I( grfcon, "Plane", &plane ) ) {
astError( AST__INTER, "astG3DTxExt(Plot3D): No \"Plane\" key found "
"in the supplied grfcon Object (internal AST programming "
"error)." , status);
/* If OK, get the extent of the text. */
} else {
/* Set up the reference, up and normal vectors so that the text appears
on the required plane. */
if( plane == XY ) {
ref[ 0 ] = x;
ref[ 1 ] = y;
ref[ 2 ] = gcon;
norm[ 0 ] = 0;
norm[ 1 ] = 0;
norm[ 2 ] = ( rc & 4 ) ? 1 : -1;
up[ 0 ] = upx;
up[ 1 ] = upy;
up[ 2 ] = 0;
xb3d = xb;
yb3d = yb;
zb3d = unused;
} else if( plane == XZ ) {
ref[ 0 ] = x;
ref[ 1 ] = gcon;
ref[ 2 ] = y;
norm[ 0 ] = 0;
norm[ 1 ] = ( rc & 2 ) ? 1 : -1;
norm[ 2 ] = 0;
up[ 0 ] = upx;
up[ 1 ] = 0;
up[ 2 ] = upy;
xb3d = xb;
yb3d = unused;
zb3d = yb;
} else if( plane == YZ ) {
ref[ 0 ] = gcon;
ref[ 1 ] = x;
ref[ 2 ] = y;
norm[ 0 ] = ( rc & 1 ) ? 1 : -1;
norm[ 1 ] = 0;
norm[ 2 ] = 0;
up[ 0 ] = 0;
up[ 1 ] = upx;
up[ 2 ] = upy;
xb3d = unused;
yb3d = xb;
zb3d = yb;
} else {
astError( AST__INTER, "astG3DTxExt(Plot3D): Illegal plane "
"identifier %d supplied (internal AST programming "
"error).", status, plane );
}
/* Since we are about to call an external function which may not be
thread safe, prevent any other thread from executing the following code
until the current thread has finished executing it. */
LOCK_MUTEX2;
/* If ok, get the extent of the text. */
if( astOK ) result = astG3DTxExt( text, ref, just, up, norm, xb3d, yb3d,
zb3d, bl );
/* Allow the next thread to proceed. */
UNLOCK_MUTEX2;
}
/* Return the result. */
return result;
}
static void PolyCurve( AstPlot *this, int npoint, int ncoord, int indim,
const double *in, int *status ){
/*
* Name:
* PolyCurve
* Purpose:
* Draw a series of connected geodesic curves.
* Type:
* Private member function.
* Synopsis:
* #include "plot3d.h"
* void PolyCurve( AstPlot *this, int npoint, int ncoord, int indim,
* const double *in, int *status )
* Class Membership:
* Plot method (overrides the astPolyCurve method inherited form the
* Plot class)
* Description:
* This function joins a series of points specified in the physical
* coordinate system of a Plot by drawing a sequence of geodesic
* curves. It is equivalent to making repeated use of the astCurve
* function (q.v.), except that astPolyCurve will generally be more
* efficient when drawing many geodesic curves end-to-end. A
* typical application of this might be in drawing contour lines.
*
* As with astCurve, full account is taken of the Mapping between
* physical and graphical coordinate systems. This includes any
* discontinuities and clipping established using astClip.
* Parameters:
* this
* Pointer to the Plot.
* npoint
* The number of points between which geodesic curves are to be drawn.
* ncoord
* The number of coordinates being supplied for each point (i.e.
* the number of axes in the current Frame of the Plot, as given
* by its Naxes attribute).
* indim
* The number of elements along the second dimension of the "in"
* array (which contains the input coordinates). This value is
* required so that the coordinate values can be correctly
* located if they do not entirely fill this array. The value
* given should not be less than "npoint".
* in
* The address of the first element in a 2-dimensional array of shape
* "[ncoord][indim]" giving the
* physical coordinates of the points which are to be joined in
* sequence by geodesic curves. These should be stored such that
* the value of coordinate number "coord" for point number
* "point" is found in element "in[coord][point]".
* status
* Pointer to the inherited status variable.
* Notes:
* - No curve is drawn on either side of any point which has any
* coordinate equal to the value AST__BAD.
* - An error results if the base Frame of the Plot is not
* 2-dimensional.
* - An error also results if the transformation between the
* current and base Frames of the Plot is not defined (i.e. the
* Plot's TranInverse attribute is zero).
*/
/* Check the global error status. */
if ( !astOK ) return;
astError( AST__INTER, "astPolyCurve(%s): The astPolyCurve "
"method cannot be used with a %s (programming error).", status,
astGetClass( this ), astGetClass( this ) );
}
static void RemoveFrame( AstFrameSet *this_fset, int iframe, int *status ) {
/*
* Name:
* RemoveFrame
* Purpose:
* Remove a Frame from a Plot3D.
* Type:
* Public virtual function.
* Synopsis:
* #include "plot.h"
* void RemoveFrame( AstFrameSet *this_fset, int iframe, int *status )
* Class Membership:
* Plot3D member function (over-rides the astRemoveFrame public
* method inherited from the Plot class).
* Description:
* This function removes a Frame from a Plot3D. All other Frames
* in the Plot3D have their indices re-numbered from one (if
* necessary), but are otherwise unchanged.
*
* If the index of the original base Frame is changed, the index value
* stored in the Plot3D is updated. If the base Frame itself is
* removed, an error is reported.
* Parameters:
* this_fset
* Pointer to the FrameSet component of the Plot3D.
* iframe
* The index within the Plot3D of the Frame to be removed.
* This value should lie in the range from 1 to the number of
* Frames in the Plot3D (as given by its Nframe attribute).
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
AstPlot3D *this; /* Pointer to Plot3D structure */
int ifrm; /* Validated frame index */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the Plot3D structure. */
this = (AstPlot3D *) this_fset;
/* Validate the frame index. This translated AST__BASE and AST__CURRENT
into actual Frame indices. */
ifrm = astValidateFrameIndex( this_fset, iframe, "astRemoveFrame" );
/* Report an error if an attempt is made to delete the Frame that defines
the mapping onto the screen (the original base Frame in the FrameSet
supplied when the Plot3D was constructed). */
if( ifrm == this->pix_frame ){
astError( AST__PXFRRM, "astRemoveFrame(%s): Cannot delete Frame "
"number %d from the supplied %s since it is the Frame "
"that defines the mapping onto the graphics plane.", status,
astGetClass( this ), iframe, astGetClass( this ) );
/* Otherwise, invoke the parent astRemoveFrame method to remove the Frame. */
} else {
(*parent_removeframe)( this_fset, iframe, status );
/* If the index of the removed Frame is smaller than the original base Frame
index, then decrement the original base Frame index so that the same Frame
will be used in future. */
if( astOK ){
if( ifrm < this->pix_frame ) (this->pix_frame)--;
}
}
}
static int RootCornerInt( const char *rootcorner, int *status ){
/*
* Name:
* RootCornerInt
* Purpose:
* Convert a RootCorner string to an integer.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* int RootCornerInt( const char *rootcorner, int *status )
* Class Membership:
* Plot3D method.
* Description:
* This function converts a RootCorner string to an integer.
* Parameters:
* rootcorner
* The string value to convert. Should be 3 characters long
* and contain nothing but "U" or "L" (upper or lower case).
* status
* Pointer to the inherited status variable.
* Returned Value:
* The integer value that is is the protected equivalent of the
* supplied string. A negative value is returned if an error has
* already occurred, of the supplied string is not legal.
*/
/* Local Variables: */
int result;
/* Initialise */
result = -1;
/* Check the inherited status. */
if( !astOK ) return result;
/* Compare thr supplied value with every legal value. */
if( astChrMatch( rootcorner, "LLL" ) ) {
result = LLL;
} else if( astChrMatch( rootcorner, "ULL" ) ) {
result = ULL;
} else if( astChrMatch( rootcorner, "LUL" ) ) {
result = LUL;
} else if( astChrMatch( rootcorner, "UUL" ) ) {
result = UUL;
} else if( astChrMatch( rootcorner, "LLU" ) ) {
result = LLU;
} else if( astChrMatch( rootcorner, "ULU" ) ) {
result = ULU;
} else if( astChrMatch( rootcorner, "LUU" ) ) {
result = LUU;
} else if( astChrMatch( rootcorner, "UUU" ) ) {
result = UUU;
}
/* Return the result. */
return result;
}
static const char *RootCornerString( int rootcorner, int *status ){
/*
* Name:
* RootCornerString
* Purpose:
* Convert a RootCorner integer to a string.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* const char *RootCornerString( int rootcorner, int *status )
* Class Membership:
* Plot3D method.
* Description:
* This function converts a RootCorner integer to a string.
* Parameters:
* rootcorner
* The integer value to convert. Should be in the range 0 to 7.
* status
* Pointer to the inherited status variable.
* Returned Value:
* A pointer to a static string that is the public equivalent of the
* supplied integer. A NULL pointer is returned if an error has
* already occurred, of the supplied integer is not legal.
*/
/* Local Variables: */
char *result;
/* Initialise */
result = NULL;
/* Check the inherited status. */
if( !astOK ) return result;
/* Compare thr supplied value with every legal value. */
if( rootcorner == LLL ) {
result = "LLL";
} else if( rootcorner == ULL ) {
result = "ULL";
} else if( rootcorner == LUL ) {
result = "LUL";
} else if( rootcorner == UUL ) {
result = "UUL";
} else if( rootcorner == LLU ) {
result = "LLU";
} else if( rootcorner == ULU ) {
result = "ULU";
} else if( rootcorner == LUU ) {
result = "LUU";
} else if( rootcorner == UUU ) {
result = "UUU";
}
/* Return the result. */
return result;
}
static void Set3DGrf( AstPlot3D *this, AstPlot *plot, int plane, int *status ){
/*
* Name:
* Set3DGrf
* Purpose:
* Associate GRF functions with a Plot.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* void Set3DGrf( AstPlot3D *this, AstPlot *plot, int plane, int *status )
* Class Membership:
* Plot3D method.
* Description:
* This function registers grf functions defined in this class with
* the supplied Plot, so that, whenever the Plot draws anything, the
* plotting request is caught by this class and converted into an
* appropriate grf3D call.
* Parameters:
* this
* Pointer to the Plot3D.
* plot
* Pointer to the Plot.
* plane
* An integer identifier for the plane within 3D GRAPHICS
* coordinates upon which the supplied Plot should draw.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
AstKeyMap *grfcon;
/* Check the global error status. */
if ( !astOK ) return;
/* Register the plotting functions defined in this class, so that the
plot will call these functions to do its 2D plotting. */
astGrfSet( plot, "Attr", (AstGrfFun) Plot3DAttr );
astGrfSet( plot, "Cap", (AstGrfFun) Plot3DCap );
astGrfSet( plot, "Flush", (AstGrfFun) Plot3DFlush );
astGrfSet( plot, "Line", (AstGrfFun) Plot3DLine );
astGrfSet( plot, "Mark", (AstGrfFun) Plot3DMark );
astGrfSet( plot, "Qch", (AstGrfFun) Plot3DQch );
astGrfSet( plot, "Scales", (AstGrfFun) Plot3DScales );
astGrfSet( plot, "Text", (AstGrfFun) Plot3DText );
astGrfSet( plot, "TxExt", (AstGrfFun) Plot3DTxExt );
/* Ensure that the Plot uses the grf interface registered using
astGrfSet. */
astSetGrf( plot, 1 );
/* When the above functions are called, they need to know which plane
they are drawing on. So we put this information into the GrfContext
KeyMap stored in the Plot. This KeyMap will be passed to the above
drawing functions when they are called from within the Plot class. */
grfcon = astGetGrfContext( plot );
astMapPut0I( grfcon, "Plane", plane, "The 2D plane being drawn on" );
if( plane == XY ) {
astMapPut0D( grfcon, "Gcon", this->gbox[2], "Constant Z value" );
} else if( plane == XZ ) {
astMapPut0D( grfcon, "Gcon", this->gbox[1], "Constant Y value" );
} else {
astMapPut0D( grfcon, "Gcon", this->gbox[0], "Constant X value" );
}
astMapPut0I( grfcon, "RootCorner", astGetRootCorner(this), "The labelled corner" );
grfcon = astAnnul( grfcon );
}
static void SetAttrib( AstObject *this_object, const char *setting, int *status ) {
/*
* Name:
* SetAttrib
* Purpose:
* Set an attribute value for a Plot3D.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* void SetAttrib( AstObject *this, const char *setting, int *status )
* Class Membership:
* Plot3D member function (over-rides the astSetAttrib protected
* method inherited from the Plot class).
* Description:
* This function assigns an attribute value for a Plot3D, 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 Plot3D.
* setting
* Pointer to a null terminated string specifying the new attribute
* value.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
AstPlot *plot; /* Pointer to specific Plot */
AstPlot3D *this; /* Pointer to the Plot3D structure */
char pat[30]; /* Regular expression pattern */
char spec[10]; /* Plane specification */
const char *null = ""; /* Pointer to null string */
const char *psetting; /* Pointer to plot-specific attrib setting */
double dval; /* Floating point attribute value */
int axis; /* Axis index */
int ival; /* Int attribute value */
int len; /* Length of setting string */
int nc; /* Number of characters read by astSscanf */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the Plot3D structure. */
this = (AstPlot3D *) 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. */
/* Norm(axis). */
/* ----------- */
if ( nc = 0,
( 2 == astSscanf( setting, "norm(%d)= %lg %n",
&axis, &dval, &nc ) )
&& ( nc >= len ) ) {
astSetNorm( this, axis - 1, dval );
/* RootCorner. */
/* ----------- */
} else if( nc = 0,
( 0 == astSscanf( setting, "rootcorner=%n%*[^\n]%n", &ival, &nc ) )
&& ( nc >= len ) ) {
ival = RootCornerInt( setting + ival, status );
if( astOK && ival < 0 ) {
astError( AST__ATTIN, "astSetAttrib(Plot3D): Unusable value \"%s\" "
"given for attribute RootCorner.", status, setting + ival );
} else {
astSetRootCorner( this, ival );
}
/* ..._XY etc */
/* ---------- */
} else if ( nc = 0,
( 1 == astSscanf( setting, "%*[a-z]_%[xyz]%n", spec, &nc ) ) ) {
if( !strcmp( spec, "xy" ) || !strcmp( spec, "yx" ) ) {
plot = this->plotxy;
} else if( !strcmp( spec, "xz" ) || !strcmp( spec, "zx" ) ) {
plot = this->plotyz;
} else if( !strcmp( spec, "yz" ) || !strcmp( spec, "zy" ) ) {
plot = this->plotxz;
} else {
plot = NULL;
}
if( plot ) {
sprintf( pat, ".*(_%s).*", spec );
psetting = astChrSub( setting, pat, &null, 1 );
astSetAttrib( plot, psetting );
psetting = astFree( (void *) psetting );
} else {
(*parent_setattrib)( this_object, setting, status );
}
/* If the attribute is still not recognised, pass it on to the parent
method for further interpretation. */
} else {
(*parent_setattrib)( this_object, setting, status );
}
/* Undefine macros local to this function. */
#undef MATCH
}
static void SetCurrent( AstFrameSet *this_frameset, int iframe, int *status ) {
/*
* Name:
* SetCurrent
* Purpose:
* Set a value for the Current attribute of a Plot3D.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* int astSetCurrent( AstFrameSet *this, int iframe, int *status )
* Class Membership:
* Plot3D member function (over-rides the public astSetCurrent method
* inherited from the FrameSet class).
* Description:
* This function sets a value for the Current attribute of a
* Plot3D. This attribute is an index that identifies the current
* Frame for the Plot3D.
* Parameters:
* this
* Pointer to the Plot3D.
* iframe
* Value to be set for the Current attribute.
* status
* Pointer to the inherited status variable.
*/
/* Invoke the parent astSetCurrent method. */
(*parent_setcurrent)( this_frameset, iframe, status );
/* Update the three 2D Plots stored in the Plot3D structure so that they
reflect this modified FrameSet. */
UpdatePlots( (AstPlot3D *) this_frameset, status );
}
static void SetPlotAttr( AstPlot *plot, int plane, int label[ 2 ], int *status ){
/*
* Name:
* SetPlotAttr
* Purpose:
* Set the attributes ofr one of the encapsulated Plots.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* void SetPlotAttr( AstPlot *plot, int plane, int label[ 2 ], int *status )
* Class Membership:
* Plot3D method.
* Description:
* This function sets the attributes of one of the encapsulated Plots.
* Parameters:
* plot
* Pointer to the Plot to modify.
* plane
* The 3D plane spanned by the 2D plot.
* label
* Array indicating if each WCS axis should be labelled or not.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
int axis;
/* Check the inherited status. */
if( !astOK ) return;
/* Ensure that the Plot uses the grf interface registered using
astGrfSet. */
astSetGrf( plot, 1 );
/* Ensure that no title is drawn. */
astSetDrawTitle( plot, 0 );
/* For each axis, ensure that no axis labels or ticks are produced unless
the axis is indicated as a labelled axis in the supplied array. */
for( axis = 0; axis < 2; axis++ ) {
if( !label[ axis ] ) {
astSetLabelUnits( plot, axis, 0 );
astSetNumLab( plot, axis, 0 );
astSetTextLab( plot, axis, 0 );
}
}
}
static void SetRootCorner( AstPlot3D *this, int rootcorner, int *status ){
/*
*+
* Name:
* astSetRootCorner
* Purpose:
* Set a new value for the RootCorner attribute.
* Type:
* Protected virtual function.
* Synopsis:
* #include "plot3d.h"
* void astSetRootCorner( AstPlot3D *this, int rootcorner )
* Class Membership:
* Plot method.
* Description:
* This function sets a new value for the RootCorner attribute.
* Parameters:
* this
* Pointer to a Plot3D.
* rootcorner
* The new RootCorner value.
*-
*/
/* Check the global status. */
if( !astOK ) return;
/* Report an error if the new value is out of bounds. */
if( rootcorner < 0 || rootcorner > 7 ){
astError( AST__ATTIN, "astSetRootCorner(Plot3D): Invalid value %d "
"supplied for RootCorner attribute", status,rootcorner);
/* If the new corner is OK, mirror any axes of the encapsulated Plots
that need mirroring (this is done to ensure that Plots look right when
viewed from the outside of the graphics cube), and modify the Edge
attributes in the encapsulated Plots to ensure the labels appear on the
requested edges of the 3D graphics cube. . */
} else {
ChangeRootCorner( this, astGetRootCorner(this), rootcorner, status );
/* Store the new value. */
this->rootcorner = rootcorner;
}
}
static void SetTickValues( AstPlot *this, int axis, int nmajor, double *major,
int nminor, double *minor, int *status ){
/*
* Name:
* SetTickValues
* Purpose:
* Store the tick mark values to use for a given Plot axis.
* Type:
* Private member function.
* Synopsis:
* #include "plot3d.h"
* void SetTickValues( AstPlot *this, int axis, int nmajor,
* double *major, int nminor, double *minor, int *status )
* Class Membership:
* Plot method (overrides the astSetTickValues method inherited form
* the Plot class)
* Description:
* This function stores a set of tick mark values that should be used by
* subsequent calls to astGrid.
* Parameters:
* this
* Pointer to a Plot.
* axis
* The zero-based index of the axis for which tick marks values
* have been supplied.
* nmajor
* The number of major tick mark values. If zero is supplied then
* the other parameters are ignored, and subsequent calls to
* astGrid will itself determine the tick values to be used.
* major
* Pointer to an array holding "nmajor" values for axis "axis" in
* the current Frame of the suppled Plot. Major tick marks will be
* drawn at these values.
* nminor
* The number of minor tick mark values.
* minor
* Pointer to an array holding "nminor" values for axis "axis" in
* the current Frame of the suppled Plot. Minor tick marks will be
* drawn at these values.
* status
* Pointer to the inherited status variable.
*/
/* Check the global status. */
if( !astOK ) return;
astError( AST__INTER, "astSetTickValues(%s): The astSetTickValues "
"method cannot be used with a %s (programming error).", status,
astGetClass( this ), astGetClass( this ) );
}
static void SplitFrameSet( AstFrameSet *fset,
AstFrameSet **fsetxy, int labelxy[2], int wcsxy[2],
AstFrameSet **fsetxz, int labelxz[2], int wcsxz[2],
AstFrameSet **fsetyz, int labelyz[2], int wcsyz[2],
int *baseplane, int *status ){
/*
* Name:
* SplitFrameSet
* Purpose:
* Split a 3D FrameSet into three 2D FrameSets.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* void SplitFrameSet( AstFrameSet *fset,
* AstFrameSet **fsetxy, int labelxy[2], int wcsxy[2],
* AstFrameSet **fsetxz, int labelxz[2], int wcsxz[2],
* AstFrameSet **fsetyz, int labelyz[2], int wcsyz[2],
* int *baseplane, int *status )
* Class Membership:
* Plot3D member function
* Description:
* This function returns 3 FrameSets, each of which has 2-dimensional
* base and current Frames. Each of the 2D base Frames span a single
* plane in the 3D base Frame of the supplied FrameSet. Likewise, each
* of the 2D current Frames span a single plane in the 3D current Frame
* of the supplied FrameSet. An error is reported if there is no
* one-to-one association between the three planes in the supplied
* current Frame and the 3 planes in the supplied base Frame.
* Parameters:
* fset
* Pointer to a FrameSet that has a 3D base Frame and a 3D current
* Frame.
* fsetxy
* Pointer to a location at which to return a pointer to a new FrameSet
* that has a 2D base Frame and a 2D current. The base Frame spans
* axes 1 and 2 of the 3D base Frame in "fset".
* labelxy
* Returned holding flags indicating if the two axes of the current
* Frame in *fsetxy should be labelled or not.
* wcsxy
* Returned holding the zero based axis index within the current Frame
* of the supplied FrameSet that corresponds to each of the two
* current Frame axes in the "fsetxy" FrameSet.
* fsetxz
* Pointer to a location at which to return a pointer to a new FrameSet
* that has a 2D base Frame and a 2D current. The base Frame spans
* axes 1 and 3 of the 3D base Frame in "fset".
* labelxz
* Returned holding flags indicating if the two axes of the current
* Frame in *fsetxz should be labelled or not.
* wcsxz
* Returned holding the zero based axis index within the current Frame
* of the supplied FrameSet that corresponds to each of the two
* current Frame axes in the "fsetxz" FrameSet.
* fsetyz
* Pointer to a location at which to return a pointer to a new FrameSet
* that has a 2D base Frame and a 2D current. The base Frame spans
* axes 2 and 3 of the 3D base Frame in "fset".
* labelyz
* Returned holding flags indicating if the two axes of the current
* Frame in *fsetyz should be labelled or not.
* wcsyz
* Returned holding the zero based axis index within the current Frame
* of the supplied FrameSet that corresponds to each of the two
* current Frame axes in the "fsetxy" FrameSet.
* baseplane
* Index of the plane that is spanned by two connected 3D axes.
* status
* Pointer to the inherited status variable.
* Notes:
* - Null pointers will be returned for the three new FrameSets if this
* function is invoked with the global status set, or if it should fail
* for any reason.
* - Each returned FrameSet has 2 Frames: Frame 1 is the base Frame
* and is spanned by 2 of the 3 axes in the base Frame of the supplied
* FrameSet; Frame 2 is the current Frame and is spanned by 2 of the 3
* axes in the current Frame of the supplied FrameSet. Any future changes
* to this function that alter this structure should reflected in
* equivalent changes to functions CreatePlots and UpdatePlots.
*/
/* Local Variables: */
AstFrame *bfrm2d;
AstFrame *bfrm;
AstFrame *cfrm1d;
AstFrame *cfrm2d;
AstFrame *cfrm;
AstFrame *dummy;
AstFrameSet *fset1;
AstFrameSet *fset2;
AstFrameSet *fset3;
AstMapping *map1d;
AstMapping *map2d;
AstMapping *map;
AstMapping *smap;
AstUnitMap *unit1d;
int *axout;
int *other_axout;
int axin2[ 2 ];
int axin[ 2 ];
int i;
int label1[ 2 ];
int label2[ 2 ];
int label3[ 2 ];
int other_axin;
int wcsax1[ 2 ];
int wcsax2[ 2 ];
int wcsax3[ 2 ];
/* Initialise. */
*fsetxy = NULL;
*fsetxz = NULL;
*fsetyz = NULL;
*baseplane = 0;
/* Check the global error status. */
if ( !astOK ) return;
/* Get the simplified base -> current Mapping form the supplied FrameSet.
Also get the base and current Frames themselves. */
map = astGetMapping( fset, AST__BASE, AST__CURRENT );
smap = astSimplify( map );
map = astAnnul( map );
cfrm = astGetFrame( fset, AST__CURRENT );
bfrm = astGetFrame( fset, AST__BASE );
/* Create a 1D UnitMap. */
unit1d = astUnitMap( 1, "", status );
/* First try to identify a pair of base Frame axes that map onto a pair
of current Frame axes. This will be the case for instance if the 3D
FrameSet describes a spectral cube in which two of the axes are spanned
by a SkyFrame. We try all three possible pairs of axes in turn until a
pair is found succesfully. */
for( i = 0; i < 3 && ! *fsetxy; i++ ) {
axin[ 0 ] = ( i < 2 ) ? 0 : 1;
axin[ 1 ] = ( i == 0 ) ? 1 : 2;
/* Try to get a Mapping that connects the inputs given by axin to a
distinct subset of outputs. */
axout = astMapSplit( smap, 2, axin, &map2d );
/* If succesful, check that the 2 inputs correspond to exactly 2 outputs. */
if( map2d ){
if( astGetNout( map2d ) == 2 ) {
/* Get the index of the other input axis (the one not included in the pair). */
other_axin = 3 - axin[ 0 ] - axin[ 1 ];
/* Try to get a Mapping that connects this remaining input to a distinct
subset of outputs. */
other_axout = astMapSplit( smap, 1, &other_axin, &map1d );
/* If succesful, check that the 1 input correspond to exactly 1 output. */
if( map1d ){
if( astGetNout( map1d ) == 1 ) {
/* Pick the two axes from the 3D base and current Frames that correspond to
the paired axes found above. */
bfrm2d = astPickAxes( bfrm, 2, axin, NULL );
cfrm2d = astPickAxes( cfrm, 2, axout, NULL );
/* Pick the axis from the 3D current Frame that correspond to
the other axis. */
cfrm1d = astPickAxes( cfrm, 1, other_axout, NULL );
/* Construct a FrameSet using these 2D Frames and Mapping. */
fset1 = astFrameSet( bfrm2d, "", status );
astAddFrame( fset1, AST__BASE, map2d, cfrm2d );
bfrm2d = astAnnul( bfrm2d );
cfrm2d = astAnnul( cfrm2d );
map2d = astAnnul( map2d );
/* Indicate that both axes in the current Frame of this FrameSet should
be labelled. */
label1[ 0 ] = 1;
label1[ 1 ] = 1;
/* Store the index of the axis within the supplied current Frame that
corresponds to each axis in the current Frame of the FrameSet created
above. */
wcsax1[ 0 ] = axout[ 0 ];
wcsax1[ 1 ] = axout[ 1 ];
/* Pick the two axes from the 3D base Frame that correspond to the first of
the paired axes, and the unpaired axis. Order them so that we get the
2 axes in the order xy, xz or yz. Also, store the index of the axis
within the supplied current Frame that corresponds to each axis in
the current Frame of the FrameSet created below. */
if( i < 2 ) {
axin2[ 0 ] = axin[ 0 ];
axin2[ 1 ] = other_axin;
wcsax2[ 0 ] = axout[ 0 ];
wcsax2[ 1 ] = other_axout[ 0 ];
} else {
axin2[ 0 ] = other_axin;
axin2[ 1 ] = axin[ 0 ];
wcsax2[ 0 ] = other_axout[ 0 ];
wcsax2[ 1 ] = axout[ 0 ];
}
bfrm2d = astPickAxes( bfrm, 2, axin2, NULL );
/* The corresponding current Frame in the new FrameSet will be a compound
2D Frame holding the other current Frame axis and a copy of the input
base Frame axis. Combine them so that we get the 2 axes in the order
xy, xz or yz. */
dummy = astPickAxes( bfrm, 1, axin, NULL );
if( i < 2 ) {
cfrm2d = (AstFrame *) astCmpFrame( dummy, cfrm1d, "", status );
} else {
cfrm2d = (AstFrame *) astCmpFrame( cfrm1d, dummy, "", status );
}
dummy = astAnnul( dummy );
/* The Mapping that joins this 2D base Frame to the 2D current Frame uses
the above 1D mapping for the other axis, and a UnitMap for the copied
base Frame axis. */
if( i < 2 ) {
map2d = (AstMapping *) astCmpMap( unit1d, map1d, 0, "", status );
} else {
map2d = (AstMapping *) astCmpMap( map1d, unit1d, 0, "", status );
}
/* Construct a FrameSet using these 2D Frames and Mapping. */
fset2 = astFrameSet( bfrm2d, "", status );
astAddFrame( fset2, AST__BASE, map2d, cfrm2d );
bfrm2d = astAnnul( bfrm2d );
cfrm2d = astAnnul( cfrm2d );
map2d = astAnnul( map2d );
/* Indicate that only one of the axes in the current Frame of this FrameSet
should be labelled. */
if( i < 2 ) {
label2[ 0 ] = 0;
label2[ 1 ] = 1;
} else {
label2[ 0 ] = 1;
label2[ 1 ] = 0;
}
/* Pick the two axes from the 3D base Frame that correspond to the second
of the paired axes, and the unpaired axis. Order them so that we get the
2 axes in the order xy, xz or yz. Also, store the index of the axis
within the supplied current Frame that corresponds to each axis in
the current Frame of the FrameSet created below. */
if( i == 0 ) {
axin2[ 0 ] = axin[ 1 ];
axin2[ 1 ] = other_axin;
wcsax3[ 0 ] = axout[ 1 ];
wcsax3[ 1 ] = other_axout[ 0 ];
} else {
axin2[ 0 ] = other_axin;
axin2[ 1 ] = axin[ 1 ];
wcsax3[ 0 ] = other_axout[ 0 ];
wcsax3[ 1 ] = axout[ 1 ];
}
bfrm2d = astPickAxes( bfrm, 2, axin2, NULL );
/* The corresponding current Frame in the new FrameSet will be a compound
2D Frame holding the other current Frame axis and a copy of the input
base Frame axis. Combine them so that we get the 2 axes in the order
xy, xz or yz. */
dummy = astPickAxes( bfrm, 1, axin + 1, NULL );
if( i == 0 ) {
cfrm2d = (AstFrame *) astCmpFrame( dummy, cfrm1d, "", status );
} else {
cfrm2d = (AstFrame *) astCmpFrame( cfrm1d, dummy, "", status );
}
dummy = astAnnul( dummy );
/* The Mapping that joins this 2D base Frame to the 2D current Frame uses
the above 1D mapping for the other axis, and a UnitMap for the copied
base Frame axis. */
if( i == 0 ) {
map2d = (AstMapping *) astCmpMap( unit1d, map1d, 0, "", status );
} else {
map2d = (AstMapping *) astCmpMap( map1d, unit1d, 0, "", status );
}
/* Construct a FrameSet using these 2D Frames and Mapping. */
fset3 = astFrameSet( bfrm2d, "", status );
astAddFrame( fset3, AST__BASE, map2d, cfrm2d );
bfrm2d = astAnnul( bfrm2d );
cfrm2d = astAnnul( cfrm2d );
map2d = astAnnul( map2d );
/* Indicate that neither axis in the current Frame of this FrameSet
should be labelled. */
label3[ 0 ] = 0;
label3[ 1 ] = 0;
/* Store each FrameSet in the correct returned pointer. */
if( i == 0 ) {
*baseplane = XY;
*fsetxy = fset1;
*fsetxz = fset2;
*fsetyz = fset3;
labelxy[ 0 ] = label1[ 0 ];
labelxy[ 1 ] = label1[ 1 ];
labelxz[ 0 ] = label2[ 0 ];
labelxz[ 1 ] = label2[ 1 ];
labelyz[ 0 ] = label3[ 0 ];
labelyz[ 1 ] = label3[ 1 ];
wcsxy[ 0 ] = wcsax1[ 0 ];
wcsxy[ 1 ] = wcsax1[ 1 ];
wcsxz[ 0 ] = wcsax2[ 0 ];
wcsxz[ 1 ] = wcsax2[ 1 ];
wcsyz[ 0 ] = wcsax3[ 0 ];
wcsyz[ 1 ] = wcsax3[ 1 ];
} else if( i == 1 ) {
*baseplane = XZ;
*fsetxy = fset2;
*fsetxz = fset1;
*fsetyz = fset3;
labelxy[ 0 ] = label2[ 0 ];
labelxy[ 1 ] = label2[ 1 ];
labelxz[ 0 ] = label1[ 0 ];
labelxz[ 1 ] = label1[ 1 ];
labelyz[ 0 ] = label3[ 0 ];
labelyz[ 1 ] = label3[ 1 ];
wcsxy[ 0 ] = wcsax2[ 0 ];
wcsxy[ 1 ] = wcsax2[ 1 ];
wcsxz[ 0 ] = wcsax1[ 0 ];
wcsxz[ 1 ] = wcsax1[ 1 ];
wcsyz[ 0 ] = wcsax3[ 0 ];
wcsyz[ 1 ] = wcsax3[ 1 ];
} else {
*baseplane = YZ;
*fsetxy = fset2;
*fsetxz = fset3;
*fsetyz = fset1;
labelxy[ 0 ] = label2[ 0 ];
labelxy[ 1 ] = label2[ 1 ];
labelxz[ 0 ] = label3[ 0 ];
labelxz[ 1 ] = label3[ 1 ];
labelyz[ 0 ] = label1[ 0 ];
labelyz[ 1 ] = label1[ 1 ];
wcsxy[ 0 ] = wcsax2[ 0 ];
wcsxy[ 1 ] = wcsax2[ 1 ];
wcsxz[ 0 ] = wcsax3[ 0 ];
wcsxz[ 1 ] = wcsax3[ 1 ];
wcsyz[ 0 ] = wcsax1[ 0 ];
wcsyz[ 1 ] = wcsax1[ 1 ];
}
/* Free resources */
cfrm1d = astAnnul( cfrm1d );
}
/* Free resources */
map1d = astAnnul( map1d );
other_axout = astFree( other_axout );
}
}
/* Free resources */
if( map2d ) map2d = astAnnul( map2d );
axout = astFree( axout );
/* Leave the loop if we now have the required FrameSets, or an error has
occurred. */
if( *fsetxy || !astOK ) break;
}
}
/* Free resources */
cfrm = astAnnul( cfrm );
bfrm = astAnnul( bfrm );
smap = astAnnul( smap );
unit1d = astAnnul( unit1d );
/* Return null pointers if an error has occurred. */
if( !astOK ) {
*fsetxy = astAnnul( *fsetxy );
*fsetxz = astAnnul( *fsetxz );
*fsetyz = astAnnul( *fsetyz );
/* Report an error if the supplied FrameSet could not be split into 3
independent 2D planes. */
} if( ! *fsetxy ) {
astError( AST__3DFSET, "astInitPlot3D(Plot3D): Supplied %s contains "
"no independent axes.", status, astGetClass( fset ) );
}
}
static void StoreAxisInfo( AstPlot3D *this, int labelxy[2], int wcsxy[2],
int labelxz[2], int wcsxz[2], int labelyz[2],
int wcsyz[2], int *status ) {
/*
* Name:
* StoreAxisInfo
* Purpose:
* Store information connecting 3D axis with associuated 2D Plot axes.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* void StoreAxisInfo( AstPlot3D *this, int labelxy[2], int wcsxy[2],
* int labelxz[2], int wcsxz[2], int labelyz[2],
* int wcsyz[2], int *status )
* Class Membership:
* Plot3D method.
* Description:
* This function stores information inside the Plot3D that allows each
* 3D axis to be associated with the 2 Plots that share the 3D axis,
* and with the axis index within each of these two Plots.
* Parameters:
* this
* Pointer to the Plot3D.
* labelxy
* Flags indicating if the two axes of the current Frame in the XY
* Plot should be labelled or not.
* wcsxy
* The zero based axis index within the 3D current Frame that
* corresponds to each of the two current Frame axes in the XY Plot.
* A value of -1 should be used for 2D axes that do not correspond
* to any 3D axis.
* labelxz
* Flags indicating if the two axes of the current Frame in the XZ
* Plot should be labelled or not.
* wcsxz
* The zero based axis index within the 3D current Frame that
* corresponds to each of the two current Frame axes in the XZ Plot.
* A value of -1 should be used for 2D axes that do not correspond
* to any 3D axis.
* labelyz
* Flags indicating if the two axes of the current Frame in the YZ
* Plot should be labelled or not.
* wcsyz
* The zero based axis index within the 3D current Frame that
* corresponds to each of the two current Frame axes in the YZ Plot.
* A value of -1 should be used for 2D axes that do not correspond
* to any 3D axis.
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
int axis2d;
int axis3d;
int gotfirst;
int gotsecond;
int temp;
/* Check the inherited status. */
if( !astOK ) return;
/* Store information that allows each 3D WCS axis to be associated with
a pair of Plots. Also store the WCS axis within each Plot that
corresponds to the 3D WCS axis. Do each 3D WCS axis in turn. */
for( axis3d = 0; axis3d < 3; axis3d++ ) {
/* Indicate we have not yet found either of the two Plots that share the
current 3D axis. */
gotfirst = 0;
gotsecond = 0;
/* Check each of the 2 axes in the plot spanning the XY face of the 3D
graphics cube. Break early if we have found both Plots for the current
3D WCS axis. */
for( axis2d = 0; axis2d < 2 && !gotsecond; axis2d++ ) {
/* See if this 2D axis corresponds to the required 3D axis. If so, store
the Plot identifier and the 2D axis index. */
if( wcsxy[ axis2d ] == axis3d ) {
if( gotfirst ) {
this->axis_plot2[ axis3d ] = XY;
this->axis_index2[ axis3d ] = axis2d;
gotsecond = 1;
} else {
this->axis_plot1[ axis3d ] = XY;
this->axis_index1[ axis3d ] = axis2d;
gotfirst = 1;
}
}
}
/* Check the plot spanning the XZ face in the same way. */
for( axis2d = 0; axis2d < 2 && !gotsecond; axis2d++ ) {
if( wcsxz[ axis2d ] == axis3d ) {
if( gotfirst ) {
this->axis_plot2[ axis3d ] = XZ;
this->axis_index2[ axis3d ] = axis2d;
gotsecond = 1;
} else {
this->axis_plot1[ axis3d ] = XZ;
this->axis_index1[ axis3d ] = axis2d;
gotfirst = 1;
}
}
}
/* Check the plot spanning the YZ face in the same way. */
for( axis2d = 0; axis2d < 2 && !gotsecond; axis2d++ ) {
if( wcsyz[ axis2d ] == axis3d ) {
if( gotfirst ) {
this->axis_plot2[ axis3d ] = YZ;
this->axis_index2[ axis3d ] = axis2d;
gotsecond = 1;
} else {
this->axis_plot1[ axis3d ] = YZ;
this->axis_index1[ axis3d ] = axis2d;
gotfirst = 1;
}
}
}
}
/* Ensure that the first Plot within each pair is the one that is used to
generate labels for the 3D axis. */
for( axis2d = 0; axis2d < 2; axis2d++ ) {
if( labelxy[ axis2d ] ) {
axis3d = wcsxy[ axis2d ];
if( this->axis_plot1[ axis3d ] != XY ){
temp = this->axis_plot1[ axis3d ];
this->axis_plot1[ axis3d ] = this->axis_plot2[ axis3d ];
this->axis_plot2[ axis3d ] = temp;
temp = this->axis_index1[ axis3d ];
this->axis_index1[ axis3d ] = this->axis_index2[ axis3d ];
this->axis_index1[ axis3d ] = temp;
}
}
}
for( axis2d = 0; axis2d < 2; axis2d++ ) {
if( labelxz[ axis2d ] ) {
axis3d = wcsxz[ axis2d ];
if( this->axis_plot1[ axis3d ] != XZ ){
temp = this->axis_plot1[ axis3d ];
this->axis_plot1[ axis3d ] = this->axis_plot2[ axis3d ];
this->axis_plot2[ axis3d ] = temp;
temp = this->axis_index1[ axis3d ];
this->axis_index1[ axis3d ] = this->axis_index2[ axis3d ];
this->axis_index1[ axis3d ] = temp;
}
}
}
for( axis2d = 0; axis2d < 2; axis2d++ ) {
if( labelyz[ axis2d ] ) {
axis3d = wcsyz[ axis2d ];
if( this->axis_plot1[ axis3d ] != YZ ){
temp = this->axis_plot1[ axis3d ];
this->axis_plot1[ axis3d ] = this->axis_plot2[ axis3d ];
this->axis_plot2[ axis3d ] = temp;
temp = this->axis_index1[ axis3d ];
this->axis_index1[ axis3d ] = this->axis_index2[ axis3d ];
this->axis_index1[ axis3d ] = temp;
}
}
}
}
static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
* Name:
* TestAttrib
* Purpose:
* Test if a specified attribute value is set for a Plot3D.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* int TestAttrib( AstObject *this, const char *attrib, int *status )
* Class Membership:
* Plot3D member function (over-rides the astTestAttrib protected
* method inherited from the Plot class).
* Description:
* This function returns a boolean result (0 or 1) to indicate whether
* a value has been set for one of a Plot3D's attributes.
* Parameters:
* this
* Pointer to the Plot3D.
* 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: */
AstPlot *plot; /* Pointer to specific Plot */
AstPlot3D *this; /* Pointer to the Plot3D structure */
char attname[50]; /* Plot attribute base name */
char patt[50]; /* Plot attribute full name */
char spec[10]; /* Plane specification */
int axis; /* Axis index */
int len; /* Length of attrib string */
int nc; /* Number of character read */
int result; /* Result value to return */
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Obtain a pointer to the Plot3D structure. */
this = (AstPlot3D *) this_object;
/* Obtain the length of the attrib string. */
len = strlen( attrib );
/* Check the attribute name and test the appropriate attribute. */
/* Norm. */
/* ----- */
if ( !strcmp( attrib, "norm" ) ) {
result = astTestNorm( this, 0 ) ||
astTestNorm( this, 1 ) ||
astTestNorm( this, 2 );
/* Norm(axis). */
/* ----------- */
} else if ( nc = 0,
( 1 == astSscanf( attrib, "norm(%d)%n", &axis, &nc ) )
&& ( nc >= len ) ) {
result = astTestNorm( this, axis - 1 );
/* RootCorner. */
/* ----------- */
} else if ( !strcmp( attrib, "rootcorner" ) ) {
result = astTestRootCorner( this );
/* ..._XY etc */
/* ---------- */
} else if ( nc = 0,
( 2 == astSscanf( attrib, "%[a-z]_%[xyz]%n", attname, spec,
&nc ) ) ) {
if( !strcmp( spec, "xy" ) || !strcmp( spec, "yx" ) ) {
plot = this->plotxy;
} else if( !strcmp( spec, "xz" ) || !strcmp( spec, "zx" ) ) {
plot = this->plotyz;
} else if( !strcmp( spec, "yz" ) || !strcmp( spec, "zy" ) ) {
plot = this->plotxz;
} else {
plot = NULL;
}
if( plot ) {
sprintf( patt, "%s%s", attname, attrib + nc );
result = astTestAttrib( plot, patt );
} else {
result = (*parent_testattrib)( this_object, attrib, status );
}
/* 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 void Text( AstPlot *this_plot, const char *text, const double pos[],
const float up[], const char *just, int *status ){
/*
* Name:
* Text
* Purpose:
* Draw a text string for a Plot3D.
* Type:
* Private member function.
* Synopsis:
* #include "plot3d.h"
* void Text( AstPlot *this, const char *text, const double pos[],
* const float up[], const char *just, int *status )
* Class Membership:
* Plot3D method (overrides the Text method inherited form the Plot
* class).
* Description:
* This function draws a string of text at a position specified in
* the physical coordinate system of a Plot3D. The physical position
* is transformed into graphical coordinates to determine where the
* text should appear within the plotting area.
*
* The text is drawn on a 2D plane that has a normal vector given by the
* current value of the Plot3D's "Norm" attribute.
* Parameters:
* this
* Pointer to the Plot3D.
* text
* Pointer to a null-terminated character string containing the
* text to be drawn. Trailing white space is ignored.
* pos
* An array, with one element for each axis of the Plot3D, giving
* the physical coordinates of the point where the reference
* position of the text string is to be placed.
* up
* An array holding the components of a vector in the "up"
* direction of the text (in graphical coordinates). For
* example, to get horizontal text, the vector {0.0f,1.0f} should
* be supplied. "Up" is taken to be the projection of the positive
* Z axis onto the plane specified by the current value of the
* just
* Pointer to a null-terminated character string identifying the
* reference point for the text being drawn. The first character in
* this string identifies the reference position in the "up" direction
* and may be "B" (baseline), "C" (centre), "T" (top) or "M" (bottom).
* The second character identifies the side-to-side reference position
* and may be "L" (left), "C" (centre) or "R" (right ). The string is
* case-insensitive, and only the first two characters are significant.
*
* For example, a value of "BL" means that the left end of the
* baseline of the original (un-rotated) text is to be drawn at the
* position given by "pos".
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
AstMapping *mapping; /* Pointer to graphics->physical mapping */
AstPlot3D *this; /* Pointer to the Plot3D structure */
AstPointSet *pset1; /* PointSet holding physical positions */
AstPointSet *pset2; /* PointSet holding graphics positions */
char *ltext; /* Local copy of "text" excluding trailing spaces */
char ljust[3]; /* Upper case copy of "just" */
const char *class; /* Object class */
const char *method; /* Current method */
const double **ptr1; /* Pointer to physical positions */
double **ptr2; /* Pointer to graphics positions */
float norm[ 3 ]; /* Single precision normal vector */
float ref[ 3 ]; /* Single precision ref position */
int axis; /* Axis index */
int escs; /* Original astEscapes value */
int naxes; /* No. of axes in the base Frame */
int ncoord; /* No. of axes in the current Frame */
int ulen; /* Length of "text" excluding trailing spaces */
/* Check the global error status. */
if ( !astOK || !text ) return;
/* Store a pointer to the Plot3D structure. */
this = (AstPlot3D *) this_plot;
/* Store the current method and class for inclusion in error messages
generated by lower level functions. */
method = "astText";
class = astClass( this );
/* Check the base Frame of the Plot is 3-D. */
naxes = astGetNin( this );
if( naxes != 3 && astOK ){
astError( AST__NAXIN, "%s(%s): Number of axes (%d) in the base "
"Frame of the supplied %s is invalid - this number should "
"be 3.", status, method, class, naxes, class );
}
/* Ensure AST functions do not included graphical escape sequences in any
returned text strings. This is because the Plot3D implementation of
this method cannot currently handle graphical escape sequences. */
escs = astEscapes( 0 );
/* Establish the correct graphical attributes as defined by attributes
with the supplied Plot. */
astGrfAttrs( this, AST__TEXT_ID, 1, GRF__TEXT, method, class );
/* Get the number of coordinates in the physical coordinate frame. */
ncoord = astGetNout( this );
/* Create a PointSet to hold the supplied physical coordinates. */
pset1 = astPointSet( 1, ncoord, "", status );
/* Allocate memory to hold pointers to the first value on each axis. */
ptr1 = (const double **) astMalloc( sizeof( const double * )*
(size_t)( ncoord ));
/* Check the pointer can be used, then store pointers to the first value
on each axis. */
if( astOK ){
for( axis = 0; axis < ncoord; axis++ ){
ptr1[ axis ] = pos + axis;
}
}
/* Store these pointers in the PointSet. */
astSetPoints( pset1, (double **) ptr1 );
/* Transform the supplied data from the current frame (i.e. physical
coordinates) to the base frame (i.e. graphics coordinates) using
the inverse Mapping defined by the Plot. */
mapping = astGetMapping( this, AST__BASE, AST__CURRENT );
pset2 = astTransform( mapping, pset1, 0, NULL );
mapping = astAnnul( mapping );
/* Get pointers to the graphics coordinates. */
ptr2 = astGetPoints( pset2 );
/* Take a copy of the string excluding any trailing white space. */
ulen = astChrLen( text );
ltext = (char *) astStore( NULL, (void *) text, ulen + 1 );
/* Check the pointers can be used. */
if( astOK ){
/* Terminate the local copy of the text string. */
ltext[ ulen ] = 0;
/* Produce an upper-case copy of the first two characters in "just". */
ljust[0] = (char) toupper( (int) just[0] );
ljust[1] = (char) toupper( (int) just[1] );
ljust[2] = 0;
/* Convert the double precision values to single precision, checking for
bad positions. */
if( ptr2[0][0] != AST__BAD && ptr2[1][0] != AST__BAD &&
ptr2[2][0] != AST__BAD ){
ref[ 0 ] = (float) ptr2[0][0];
ref[ 1 ] = (float) ptr2[1][0];
ref[ 2 ] = (float) ptr2[2][0];
/* If the nornmal vector has non-zero length, draw the text. */
norm[ 0 ] = (float) astGetNorm( this, 0 );
norm[ 1 ] = (float) astGetNorm( this, 1 );
norm[ 2 ] = (float) astGetNorm( this, 2 );
/* Since we are about to call an external function which may not be
thread safe, prevent any other thread from executing the following code
until the current thread has finished executing it. */
LOCK_MUTEX2;
if( norm[ 0 ] != 0.0 || norm[ 1 ] != 0.0 || norm[ 2 ] != 0.0 ){
if( !astG3DText( ltext, ref, ljust, (float *) up, norm ) ) {
astError( AST__GRFER, "%s(%s): Graphics error in astG3DText. ", status,
method, class );
}
} else if( astOK ) {
astError( AST__ATTIN, "%s(%s): The vector specified by the Norm "
"attribute has zero length.", status, method, class );
}
/* Allow the next thread to proceed. */
UNLOCK_MUTEX2;
}
/* Free the local copy of the string. */
ltext = (char *) astFree( (void *) ltext );
}
/* Annul the PointSets. */
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
/* Free the memory holding the pointers to the first value on each axis. */
ptr1 = (const double **) astFree( (void *) ptr1 );
/* Re-establish the original graphical attributes. */
astGrfAttrs( this, AST__TEXT_ID, 0, GRF__TEXT, method, class );
/* Restore the original value of the flag which says whether graphical
escape sequences should be incldued in any returned text strings. */
astEscapes( escs );
/* Return */
return;
}
static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in,
int forward, AstPointSet *out, int *status ) {
/*
* Name:
* Transform
* Purpose:
* Use a Plot to transform a set of points.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* AstPointSet *Transform( AstMapping *this, AstPointSet *in,
* int forward, AstPointSet *out, int *status )
* Class Membership:
* Plot3D member function (over-rides the astTransform protected
* method inherited from the Plot class).
* Description:
* This function takes a Plot3D and a set of points encapsulated in a
* PointSet and transforms the points from graphics coordinates to
* physical coordinates (in the forward direction). No clipping or
* normalisation is applied.
* Parameters:
* this
* Pointer to the Plot3D.
* in
* Pointer to the PointSet holding the input coordinate data.
* forward
* A non-zero value indicates that the forward coordinate
* transformation should be applied while a zero value requests the
* inverse transformation.
* out
* Pointer to a PointSet which will hold the transformed (output)
* coordinate values. A NULL value may also be given, in which case a
* new PointSet will be created by this function.
* status
* Pointer to the inherited status variable.
* Returned Value:
* Pointer to the output (possibly new) PointSet.
* 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.
* - The number of coordinate values per point in the input PointSet must
* match the number of coordinates for the Plot being applied.
* - If an output PointSet is supplied, it must have space for sufficient
* number of points and coordinate values per point to accommodate the
* result. Any excess space will be ignored.
*/
/* Local Variables: */
AstMapping *map; /* Pointer to the mapping */
AstPointSet *result; /* Positions in output Frame */
AstPlot3D *this; /* The Plot3D */
/* Check the global error status. */
if ( !astOK ) return NULL;
/* Get a pointer to the Plot3D. */
this = (AstPlot3D *) this_mapping;
/* Get the Mapping from the base to the current Frame. */
map = astGetMapping( this, AST__BASE, AST__CURRENT );
/* Do the transformation. */
result = astTransform( map, in, forward, out );
/* Annul the mapping. */
map = astAnnul( map );
/* If an error has occurred, annul the result. */
if( !astOK ) result = astAnnul( result );
/* Return a pointer to the output PointSet. */
return result;
}
static void UpdatePlots( AstPlot3D *this, int *status ) {
/*
* Name:
* UpdatePlots
* Purpose:
* Update the three 2D plots stored in the Plot3D.
* Type:
* Private function.
* Synopsis:
* #include "plot3d.h"
* void UpdatePlots( AstPlot3D *this )
* Class Membership:
* Plot3D method.
* Description:
* This function splits the parent FrameSet up into 3 independent 2D
* FrameSets, each describing a 2D plane in the supplied 3D FrameSet.
* It then uses these 2D FrameSets to update the three Plots in the
* Plot3D, by removing the existing current Frame and adding in the
* new current Frame with the associated Mapping.
* Parameters:
* this
* Pointer to the Plot3D.
* Notes:
* - Each of the 3 plots has 3 Frames: Frame 1 is the base (GRAPHICS)
* Frame; Frame 2 is spanned by 2 of the 3 axes in the base Frame of
* the supplied FrameSet; Frame 3 is the current Frame and is spanned
* by 2 of the 3 axes in the current Frame of the supplied FrameSet.
* Any future changes to this function that alter this structure should
* reflected in equivalewnt changes to function CreatePlots.
*/
/* Local Variables: */
AstFrame *frm;
AstFrameSet *fsetxy;
AstFrameSet *fsetxz;
AstFrameSet *fsetyz;
AstFrameSet *fset;
AstMapping *map;
int baseplane;
int labelxy[ 2 ];
int labelxz[ 2 ];
int labelyz[ 2 ];
int wcsxy[ 2 ];
int wcsxz[ 2 ];
int wcsyz[ 2 ];
int rootcorner;
/* Check the inherited status. */
if( !astOK ) return;
/* Return without action if the Plot3D does not contain the three 2D
Plots. This may be the case for instance if this function is called
before the Plot3D is fully constructed. */
if( this->plotxy && this->plotxz && this->plotyz ){
/* We need a FrameSet that is equivalent to the one that was used to
construct the Plot3D (i.e. a FrameSet that does not have the GRAPHICS
Frame that was added by the Plot3D constructor). So take a copy of the
parent FrameSet, remove the GRAPHICS Frame (Frame 1) and set the base
Frame to be the Frame that was the base Frame when the Plot3D was
constructed. */
fset = (AstFrameSet *) astCast( this, dummy_frameset );
astSetBase( fset, this->pix_frame );
astRemoveFrame( fset, 1 );
/* Split the supplied FrameSet up into 3 FrameSets, each with a 2D base
and current Frame. Each of these FrameSets describes one plane of
the 3D cube. */
SplitFrameSet( fset, &fsetxy, labelxy, wcsxy, &fsetxz, labelxz, wcsxz,
&fsetyz, labelyz, wcsyz, &baseplane, status );
/* First do the XY Plot. Extract the current Frame and base->current
Mapping from the new FrameSet. It is assumed that he base Frame in the
new FrameSet is equivalent to Frame 2 in the existing Plot. This
assumption is based on the way the CreatePlots and SplitFrameSet
functions work, and should be reviewed if either of these functions
is changed. */
frm = astGetFrame( fsetxy, 2 );
map = astGetMapping( fsetxy, 1, 2 );
/* Add the new Frame into the existing Plot using the Mapping to connect
it to Frame 2 in the Plot. It becomes the current Frame in the Plot. */
astAddFrame( this->plotxy, 2, map, frm );
/* Delete the original current Frame in the Plot since it is not needed
any more. */
astRemoveFrame( this->plotxy, 3 );
/* Set the Plot attributes. */
SetPlotAttr( this->plotxy, XY, labelxy, status );
/* Free resources */
fsetxy = astAnnul( fsetxy );
map = astAnnul( map );
frm = astAnnul( frm );
/* Do the same for the XZ plot. */
frm = astGetFrame( fsetxz, 2 );
map = astGetMapping( fsetxz, 1, 2 );
astAddFrame( this->plotxz, 2, map, frm );
astRemoveFrame( this->plotxz, 3 );
SetPlotAttr( this->plotxz, XZ, labelxz, status );
fsetxz = astAnnul( fsetxz );
map = astAnnul( map );
frm = astAnnul( frm );
/* Do the same for the YZ plot. */
frm = astGetFrame( fsetyz, 2 );
map = astGetMapping( fsetyz, 1, 2 );
astAddFrame( this->plotyz, 2, map, frm );
astRemoveFrame( this->plotyz, 3 );
SetPlotAttr( this->plotyz, YZ, labelyz, status );
fsetyz = astAnnul( fsetyz );
map = astAnnul( map );
frm = astAnnul( frm );
/* Store information that allows each 3D WCS axis to be associatedf with
a pair of Plots. Also store the WCS axis within each Plot that
corresponds to the 3D WCS axis. */
StoreAxisInfo( this, labelxy, wcsxy, labelxz, wcsxz, labelyz, wcsyz, status );
/* Set up the Edges attributes in the encapsulated Plots to produce labels
on the required edges of the 3D graphics cube. */
rootcorner = astGetRootCorner( this );
ChangeRootCorner( this, rootcorner, rootcorner, status );
/* Store the plane spanned by two connected 3D axes. */
this->baseplot = baseplane;
/* Free remaining resources */
fset = astAnnul( fset );
}
}
static void VSet( AstObject *this_object, const char *settings,
char **text, va_list args, int *status ) {
/*
* Name:
* VSet
* Purpose:
* Set values for a Plot3D's attributes.
* Type:
* Private function.
* Synopsis:
* #include "frameset.h"
* void VSet( AstObject *this, const char *settings, char **text,
* va_list args, int *status )
* Class Membership:
* Plot3D member function (over-rides the protected astVSet
* method inherited from the Object class).
* Description:
* This function assigns a set of attribute values for a Plot3D,
* the attributes and their values being specified by means of a
* string containing a comma-separated list of the form:
*
* "attribute1 = value1, attribute2 = value2, ... "
*
* Here, "attribute" specifies an attribute name and the value to
* the right of each "=" sign should be a suitable textual
* representation of the value to be assigned to that attribute. This
* will be interpreted according to the attribute's data type.
*
* The string supplied may also contain "printf"-style format
* specifiers identified by a "%" sign in the usual way. If
* present, these will be substituted by values supplied as
* optional arguments (as a va_list variable argument list), using
* the normal "printf" rules, before the string is used.
* Parameters:
* this
* Pointer to the Plot3D.
* settings
* Pointer to a null-terminated string containing a
* comma-separated list of attribute settings.
* text
* Pointer to a location at which to return a pointer to dynamic
* memory holding a copy of the expanded setting string. This memory
* should be freed using astFree when no longer needed. If a NULL
* pointer is supplied, no string is created.
* args
* The variable argument list which contains values to be
* substituted for any "printf"-style format specifiers that
* appear in the "settings" string.
* status
* Pointer to the inherited status variable.
* Notes:
* - This function preserves the integrity of the Plot3D (if
* possible) by appropriately modifying the three encapsulated Plots.
*/
/* Invoke the parent astVSet method to set the Plot3D's attribute values. */
(*parent_vset)( this_object, settings, text, args, status );
/* Update the three 2D Plots stored in the Plot3D structure so that they
reflect this modified FrameSet. */
UpdatePlots( (AstPlot3D *) this_object, status );
}
/* Functions which access Plot3D class attributes. */
/* ----------------------------------------------- */
/* RootCorner. */
/* ----------- */
/*
*att++
* Name:
* RootCorner
* Purpose:
* Specifies which edges of the 3D box should be annotated.
* Type:
* Public attribute.
* Synopsis:
* String.
* Description:
* This attribute controls the appearance of an annotated
c coordinate grid (drawn with the astGrid function) by determining
f coordinate grid (drawn with the AST_GRID routine) by determining
* which edges of the cube enclosing the 3D graphics space are used
* for displaying numerical and descriptive axis labels. The attribute
* value identifies one of the eight corners of the cube within
* which graphics are being drawn (i.e. the cube specified by the
c "graphbox" parameter when astPlot3D
f GRAPHBOX argument when AST_PLOT3D
* was called tp create the Plot3D). Axis labels and tick marks will
* be placed on the three cube edges that meet at the given corner.
*
* The attribute value should consist of three character, each of
* which must be either "U" or "L". The first character in the string
* specifies the position of the corner on the first graphics axis.
* If the character is "U" then the corner is at the upper bound on the
* first graphics axis. If it is "L", then the corner is at the lower
* bound on the first axis. Likewise, the second and third characters
* in the string specify the location of the corner on the second and
* third graphics axes.
*
* For instance, corner "LLL" is the corner that is at the lower bound
* on all three graphics axes, and corner "ULU" is at the upper bound
* on axes 1 and 3 but at the lower bound on axis 2.
*
* The default value is "LLL".
* Applicability:
* Plot3D
* All Plot3Ds have this attribute.
*att--
*/
/* Internally, the RootCorner is represented as an integer bit mask, with
bit zero for the X axis, bit 1 for the Y axis and bit 2 for the Z axis.
A bit is set if the corner is at the upper bound on the axis and unset
if it is at the lower bound. A value of -1 indicates that the attribue
has not been assigned a value. */
astMAKE_GET(Plot3D,RootCorner,int,0,( this->rootcorner == -1 ? 0 : this->rootcorner))
astMAKE_TEST(Plot3D,RootCorner,( this->rootcorner != -1 ))
/* Norm(axis). */
/* ----------- */
/*
*att++
* Name:
* Norm(axis)
* Purpose:
* Specifies the plane upon which a Plot3D draws text and markers.
* Type:
* Public attribute.
* Synopsis:
* Floating point.
* Description:
* This attribute controls the appearance of text and markers drawn
* by a Plot3D. It specifies the orientation of the plane upon which
* text and markers will be drawn by all subsequent invocations of the
c astText and astMark functions.
f AST_TEXT and AST_MARK functions.
*
* When setting or getting the Norm attribute, the attribute name must
* be qualified by an axis index in the range 1 to 3. The 3 elements of
* the Norm attribute are together interpreted as a vector in 3D graphics
* coordinates that is normal to the plane upon which text and marks
* should be drawn. When testing or clearing the attribute, the axis
* index is optional. If no index is supplied, then clearing the Norm
* attribute will clear all three elements, and testing the Norm attribute
* will return a non-zero value if any of the three elements are set.
*
* The default value is 1.0 for each of the 3 elements. The length of
* the vector is insignificant, but an error will be reported when
* attempting to draw text or markers if the vector has zero length.
* Applicability:
* Plot
* All Plot3Ds have this attribute.
*att--
*/
MAKE_CLEAR3(Norm,norm,AST__BAD,3)
MAKE_SET3(Norm,double,norm,value,3)
MAKE_TEST3(Norm,( this->norm[axis] != AST__BAD ),3)
MAKE_GET3(Norm,double,AST__BAD,(this->norm[axis]!=AST__BAD?this->norm[axis]:1.0),3)
/* Functions which access Plot class attributes. */
/* --------------------------------------------- */
/* First do axis specific attributes. */
#define MAKE_ALL(attr,type,badval,whichplots) \
MAKE_CLEAR(attr,whichplots) \
MAKE_SET(attr,type,whichplots) \
MAKE_GET(attr,type,badval )
MAKE_ALL(MinTick,int,0,0)
MAKE_ALL(Abbrev,int,0,1)
MAKE_ALL(Gap,double,AST__BAD,1)
MAKE_ALL(LogGap,double,AST__BAD,1)
MAKE_ALL(LogPlot,int,0,0)
MAKE_ALL(LogTicks,int,0,0)
MAKE_ALL(LogLabel,int,0,1)
MAKE_ALL(LabelUp,int,0,1)
MAKE_ALL(DrawAxes,int,0,0)
MAKE_ALL(LabelUnits,int,0,1)
MAKE_ALL(MinTickLen,double,0.0,0)
MAKE_ALL(MajTickLen,double,0.0,0)
MAKE_ALL(NumLab,int,0,1)
MAKE_ALL(NumLabGap,double,AST__BAD,1)
MAKE_ALL(TextLab,int,0,1)
MAKE_ALL(TextLabGap,double,AST__BAD,1)
#undef MAKE_ALL
/* Now do attributes that are not axis specific. */
#define MAKE_ALL(attr,type) \
MAKE_CLEAR1(attr) \
MAKE_SET1(attr,type)
MAKE_ALL(Ink,int)
MAKE_ALL(Tol,double)
MAKE_ALL(Invisible,int)
MAKE_ALL(TickAll,int)
MAKE_ALL(ForceExterior,int)
MAKE_ALL(Border,int)
MAKE_ALL(Clip,int)
MAKE_ALL(ClipOp,int)
MAKE_ALL(Escape,int)
MAKE_ALL(Grid,int)
MAKE_ALL(Labelling,int)
#undef MAKE_ALL
/* First do element-specific attributes. */
#define MAKE_ALL(attr,type) \
MAKE_CLEAR2(attr) \
MAKE_SET2(attr,type)
MAKE_ALL(Style,int)
MAKE_ALL(Font,int)
MAKE_ALL(Colour,int)
MAKE_ALL(Width,double)
MAKE_ALL(Size,double)
#undef MAKE_ALL
/* Copy constructor. */
/* ----------------- */
static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
/*
* Name:
* Copy
* Purpose:
* Copy constructor for Plot3D objects.
* Type:
* Private function.
* Synopsis:
* void Copy( const AstObject *objin, AstObject *objout, int *status )
* Description:
* This function implements the copy constructor for Plot3D 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: */
AstPlot3D *in; /* Pointer to input Plot3D */
AstPlot3D *out; /* Pointer to output Plot3D */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain pointers to the input and output Plot3Ds. */
in = (AstPlot3D *) objin;
out = (AstPlot3D *) objout;
/* Nullify the pointers stored in the output object since these will
currently be pointing at the input data (since the output is a simple
byte-for-byte copy of the input). Otherwise, the input data could be
freed by accidient if the output object is deleted due to an error
occuring in this function. */
out->plotxy = NULL;
out->plotxz = NULL;
out->plotyz = NULL;
/* Copy the encapsulated Plots */
if( in->plotxy ) out->plotxy = astCopy( in->plotxy );
if( in->plotxz ) out->plotxz = astCopy( in->plotxz );
if( in->plotyz ) out->plotyz = astCopy( in->plotyz );
/* If an error has occurred, free the output resources. */
if( !astOK ) Delete( (AstObject *) out, status );
}
/* Destructor. */
/* ----------- */
static void Delete( AstObject *obj, int *status ) {
/*
* Name:
* Delete
* Purpose:
* Destructor for Plot3D objects.
* Type:
* Private function.
* Synopsis:
* void Delete( AstObject *obj, int *status )
* Description:
* This function implements the destructor for Plot3D 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: */
AstPlot3D *this;
/* Release the memory referred to in the Plot3D structure. */
this = (AstPlot3D *) obj;
if( this ) {
this->plotxy = astDelete( this->plotxy );
this->plotxz = astDelete( this->plotxz );
this->plotyz = astDelete( this->plotyz );
}
}
/* Dump function. */
/* -------------- */
static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
/*
* Name:
* Dump
* Purpose:
* Dump function for Plot3D 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 Plot3D class to an output Channel.
* Parameters:
* this
* Pointer to the Plot3D 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 Constants: */
#define KEY_LEN 50 /* Maximum length of a keyword */
/* Local Variables: */
AstPlot3D *this;
double dval;
char key[ KEY_LEN + 1 ];
int axis;
int helpful;
int ival;
int set;
const char *text;
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the Plot3D structure. */
this = (AstPlot3D *) this_object;
/* Write out values representing the instance variables for the
Plot3D class. Accompany these with appropriate comment strings,
possibly depending on the values being written.*/
/* In the case of attributes, we first use the appropriate (private)
Test... member function to see if they are set. If so, we then use
the (private) Get... function to obtain the value to be written
out.
For attributes which are not set, we use the astGet... method to
obtain the value instead. This will supply a default value
(possibly provided by a derived class which over-rides this method)
which is more useful to a human reader as it corresponds to the
actual default attribute value. Since "set" will be zero, these
values are for information only and will not be read back. */
/* Norm. */
/* ----- */
for ( axis = 0; axis < 3; axis++ ) {
dval = astGetNorm( this, axis );
helpful = ( dval != -DBL_MAX );
(void) sprintf( key, "Norm%d", axis + 1 );
astWriteDouble( channel, key, 0, helpful, dval, "Text plane normal vector");
}
/* RootCorner. */
/* ----------- */
set = TestRootCorner( this, status );
ival = set ? GetRootCorner( this, status ) : astGetRootCorner( this );
text = RootCornerString( ival, status );
if( text ) {
astWriteString( channel, "RootCn", set, 1, text, "Corner where labelled axes meet" );
} else if( astOK ) {
astError( AST__INTER, "astDump(Plot3D): Illegal value %d found for "
"RootCorner attribute (interbal AST programming error).", status,
ival );
}
/* Labelled axes */
astWriteInt( channel, "AxPlX1", 1, 1, this->axis_plot1[0],
"Plot used to label the 3D X axis" );
astWriteInt( channel, "AxPlY1", 1, 1, this->axis_plot1[1],
"Plot used to label the 3D Y axis" );
astWriteInt( channel, "AxPlZ1", 1, 1, this->axis_plot1[2],
"Plot used to label the 3D Z axis" );
astWriteInt( channel, "AxInX1", 1, 1, this->axis_index1[0],
"Plot axis index used to label the 3D X axis" );
astWriteInt( channel, "AxInY1", 1, 1, this->axis_index1[1],
"Plot axis index used to label the 3D Y axis" );
astWriteInt( channel, "AxInZ1", 1, 1, this->axis_index1[2],
"Plot axis index used to label the 3D Z axis" );
/* Unlabelled axes */
astWriteInt( channel, "AxPlX2", 1, 1, this->axis_plot2[0],
"Other Plot touching the 3D X axis" );
astWriteInt( channel, "AxPlY2", 1, 1, this->axis_plot2[1],
"Other Plot touching the 3D Y axis" );
astWriteInt( channel, "AxPlZ2", 1, 1, this->axis_plot2[2],
"Other Plot touching the 3D Z axis" );
astWriteInt( channel, "AxInX2", 1, 1, this->axis_index2[0],
"Other Plot axis index touching the 3D X axis" );
astWriteInt( channel, "AxInY2", 1, 1, this->axis_index2[1],
"Other Plot axis index touching the 3D Y axis" );
astWriteInt( channel, "AxInZ2", 1, 1, this->axis_index2[2],
"Other Plot axis index touching the 3D Z axis" );
/* The Plot that spans the two connected axes. */
astWriteInt( channel, "BasePl", 1, 1, this->baseplot,
"Plot spanning two connected 3D axes" );
/* XY Plot */
astWriteObject( channel, "PlotXY", 1, 1, this->plotxy,
"Plot describing the XY plane" );
/* XZ Plot */
astWriteObject( channel, "PlotXZ", 1, 1, this->plotxz,
"Plot describing the XZ plane" );
/* YZ Plot */
astWriteObject( channel, "PlotYZ", 1, 1, this->plotyz,
"Plot describing the YZ plane" );
/* The index within the Plot3D FrameSet, of the original base Frame in
the FrameSet supplied when the Plot3D was constructed. */
astWriteInt( channel, "PixFrm", 1, 0, this->pix_frame,
"Index of original base Frame" );
/* Undefine macros local to this function. */
#undef KEY_LEN
}
/* Standard class functions. */
/* ========================= */
/* Implement the astIsAPlot3D and astCheckPlot3D functions using the
macros defined for this purpose in the "object.h" header file. */
astMAKE_ISA(Plot3D,Plot)
astMAKE_CHECK(Plot3D)
AstPlot3D *astPlot3D_( void *frame_void, const float *graphbox,
const double *basebox, const char *options, int *status, ...) {
/*
*+
* Name:
* astPlot3D
* Purpose:
* Create a Plot3D.
* Type:
* Protected function.
* Synopsis:
* #include "plot3d.h"
* AstPlot3D *astPlot3D( AstFrame *frame, const float *graphbox,
* const double *basebox, const char *options, ..., int *status )
* Class Membership:
* Plot3D constructor.
* Description:
* This function creates a new Plot3D and optionally initialises its
* attributes.
*
* The supplied Frame (or the base frame if a FrameSet was supplied) is
* assumed to be related to the graphics world coordinate system by a
* simple shift and scale along each axis. The mapping between graphics
* world coordinates and this Frame is specified by supplying the
* coordinates in both systems at the lower bounds and upper bounds corners
* of a box on the graphics device. By default, no graphics will be
* produced outside the supplied box, but this default behaviour can be
* changed by setting explicit values for the various clipping attributes.
* Parameters:
* frame
* A pointer to a Frame or FrameSet to be annotated. If a NULL pointer
* is supplied, then a default 3-D Frame will be created to which labels,
* etc, can be attached by setting the relevant Frame attributes.
* graphbox
* A pointer to an array of 6 values giving the graphics world
* coordinates of the lower bounds and upper bound corners of a box on
* the graphics output device. The first triple of values should be the
* coordinates of the lower bounds corner of the box and the second
* triple of values should be the coordinates of the upper bounds corner.
* basebox
* A pointer to an array of 6 values giving the coordinates in the
* supplied Frame, or base frame of the supplied FrameSet, at the
* lower bounds corner and upper bounds corners of the box specified
* by parameter graphbox. These should be supplied in the same order as
* for parameter "graphbox".
* options
* Pointer to a null terminated string containing an optional
* comma-separated list of attribute assignments to be used for
* initialising the new Plot3D. The syntax used is the same as for the
* astSet method and may include "printf" format specifiers identified
* by "%" symbols in the normal way.
* status
* Pointer to the inherited status variable.
* ...
* If the "options" string contains "%" format specifiers, then an
* optional list of 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 method (and for the C "printf"
* function).
* Returned Value:
* A pointer to the new Plot3D.
* 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.
*-
* Implementation Notes:
* - This function implements the basic Plot3D constructor which
* is available via the protected interface to the Plot3D class.
* A public interface is provided by the astPlot3DId_ function.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstFrame *frame; /* Pointer to Frame structure */
AstPlot3D *new; /* Pointer to new Plot3D */
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 variables to avoid "used of uninitialised variable"
messages from dumb compilers. */
new = NULL;
/* Obtain and validate a pointer to any supplied Frame structure. */
if( frame_void ){
frame = astCheckFrame( frame_void );
} else {
frame = NULL;
}
/* Check the pointer can be used. */
if ( astOK ) {
/* Initialise the Plot3D, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitPlot3D( NULL, sizeof( AstPlot3D ), !class_init,
&class_vtab, "Plot3D", frame, graphbox,
basebox );
/* 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
Plot'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 Plot. */
return new;
}
AstPlot3D *astInitPlot3D_( void *mem, size_t size, int init,
AstPlot3DVtab *vtab, const char *name,
AstFrame *frame, const float *graphbox,
const double *basebox, int *status ) {
/*
*+
* Name:
* astInitPlot3D
* Purpose:
* Initialise a Plot3D.
* Type:
* Protected function.
* Synopsis:
* #include "plot3d.h"
* AstPlot3D *astInitPlot3D( void *mem, size_t size, int init,
* AstPlotVtab *vtab, const char *name,
* AstFrame *frame, const float *graphbox,
* const double *basebox )
* Class Membership:
* Plot3D initialiser.
* Description:
* This function is provided for use by class implementations to
* initialise a new Plot3D object. It allocates memory (if
* necessary) to accommodate the Plot3D plus any additional data
* associated with the derived class. It then initialises a
* Plot3D structure at the start of this memory. If the "init"
* flag is set, it also initialises the contents of a virtual function
* table for a Plot3D at the start of the memory passed via the
* "vtab" parameter.
* Parameters:
* mem
* A pointer to the memory in which the Plot3D is to be
* created. This must be of sufficient size to accommodate the
* Plot3D data (sizeof(Plot3D)) 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 Plot3D (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 Plot3D structure, so a valid value must be supplied
* even if not required for allocating memory.
* init
* A logical flag indicating if the Plot3D'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 Plot3D.
* 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).
* fset
* A pointer to the Frame or Frameset to be annotated.
* graphbox
* A pointer to an array of 6 values giving the graphics coordinates
* of the bottom left and top right corners of a box on the graphics
* output device. The first triple of values should be the graphics
* coordinates of the bottom left corner of the box and the second
* triple of values are the graphics coordinates of the top right corner.
* basebox
* A pointer to an array of 6 values giving the coordinates in the
* supplied Frame or base Frame of the supplied FrameSet at the bottom
* left and top right corners of the box specified by parameter graphbox.
* These should be supplied in the same order as for parameter "graphbox".
* Returned Value:
* A pointer to the new Plot3D.
* 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: */
AstFrame *baseframe = NULL; /* Pointer to base frame */
AstFrame *graphicsframe = NULL; /* Pointer to graphics frame */
AstFrameSet *fset0 = NULL; /* The n-D FrameSet to be annotated */
AstFrameSet *fset = NULL; /* The 2-D FrameSet to be annotated */
AstMapping *map = NULL; /* Mapping for converting bbox -> gbox */
AstPlot3D *new = NULL; /* Pointer to new Plot3D */
char *mess = NULL; /* Pointer to a descriptive message */
double djunkbox[ 4 ] = { 0.0, 0.0, 1.0, 1.0 }; /* Dummy 2D basebox */
float fjunkbox[ 4 ] = { 0.0, 0.0, 1.0, 1.0 }; /* Dummy 2D graphbox */
int bi; /* Index of base frame */
int ci; /* Index of current frame */
int i; /* Loop count */
int ii; /* Loop count */
int naxes; /* No. of axes in frame */
int nfrm; /* Number of Frames in frameset */
/* Check the global status. */
if ( !astOK ) return NULL;
/* If necessary, initialise the virtual function table. */
if ( init ) astInitPlot3DVtab( vtab, name );
/* First of all we need to ensure that we have a FrameSet and a base
Frame on which to base the new Plot3D. If a NULL Frame pointer was
supplied, create a default 3-D Frame, and then create a FrameSet
containing just this default Frame. Also store a pointer to a
message which can be used to describe the object within error
messages. */
if( !frame ){
baseframe = astFrame( 3, "", status );
fset = astFrameSet( baseframe, "", status );
mess = "default 3-d Frame";
/* If an object was supplied, report an error if it is not a Frame or
an object derived from a Frame (such as a FrameSet). */
} else if( !astIsAFrame( frame ) ){
if( astOK ){
astError( AST__BDOBJ, "astInitPlot3D(%s): Supplied Object (class '%s') "
"is not a Frame.", status, name, astGetClass( frame ) );
}
/* If the supplied object is a Plot3D or an object derived from a Plot3D
(a Plot3D is a sort of Frame and so will pass the above test), extract a
FrameSet from the Plot3D, and clear the Domain attribute for any existing
Frames which have Domain GRAPHICS. */
} else if( astIsAPlot3D( frame ) ){
fset0 = astFrameSet( frame, "", status );
fset = astCopy( fset0 );
fset0 = astAnnul( fset0 );
for( i = 0; i < astGetNframe( fset ); i++ ) {
graphicsframe = astGetFrame( fset, i );
if( !strcmp( astGetDomain( graphicsframe ), "GRAPHICS" ) ) {
astClearDomain( graphicsframe );
}
graphicsframe = astAnnul( graphicsframe );
}
baseframe = astGetFrame( fset, astGetBase( fset ) );
mess = "base Frame of the supplied Plot3D";
/* If the object is not a FrameSet, create a FrameSet holding the
supplied Frame. If the Frame is not 3D, an extra 3D Frame is
included in the FrameSet derived from axes 1, 2 and 3 of the supplied
Frame. This new Frame becomes the base Frame. */
} else if( !astIsAFrameSet( frame ) ){
fset0 = astFrameSet( frame, "", status );
mess = "supplied Frame";
fset = Fset3D( fset0, AST__BASE, status );
fset0 = astAnnul( fset0 );
baseframe = astGetFrame( fset, astGetBase( fset ) );
/* If a FrameSet was supplied, ensure it has a 3D base Frame.
If the supplied FrameSet is not 3D, then a new base Frame is
inserted into it which is derived from axes 1, 2 and 3 of the
original base Frame. */
} else {
fset = Fset3D( (AstFrameSet *) frame, AST__BASE, status );
baseframe = astGetFrame( fset, astGetBase( fset ) );
mess = "base Frame of the supplied FrameSet";
}
/* Check that there are 3 axes in the base frame of the FrameSet. */
naxes = astGetNaxes( baseframe );
if ( naxes != 3 && astOK ) {
astError( AST__NAXIN, "astInitPlot3D(%s): Number of axes (%d) in the %s "
"is invalid - this number should be 3.", status, name, naxes, mess );
}
/* Check that no dimension of the graphbox is bad. */
if( astISBAD(graphbox[0]) || astISBAD(graphbox[1]) ||
astISBAD(graphbox[2]) || astISBAD(graphbox[3]) ||
astISBAD(graphbox[4]) || astISBAD(graphbox[5]) ) {
astError( AST__BADBX, "astInitPlot3D(%s): The plotting volume has undefined limits "
"in the graphics world coordinate system.", status, name );
}
/* Check that no dimension of the graphbox is zero. */
if( ( graphbox[ 3 ] == graphbox[ 0 ] ||
graphbox[ 4 ] == graphbox[ 1 ] ||
graphbox[ 5 ] == graphbox[ 2 ] ) && astOK ){
astError( AST__BADBX, "astInitPlot3D(%s): The plotting volume has zero size "
"in the graphics world coordinate system.", status, name );
}
/* Check that no dimension of the basebox is bad. */
if( astISBAD(basebox[0]) || astISBAD(basebox[1]) ||
astISBAD(basebox[2]) || astISBAD(basebox[3]) ||
astISBAD(basebox[4]) || astISBAD(basebox[5]) ) {
astError( AST__BADBX, "astInitPlot3D(%s): The limits of "
"the %s are undefined or bad.", status, name, name );
}
/* Create a Frame which describes the graphics world coordinate system. */
graphicsframe = astFrame( 3, "Domain=GRAPHICS,Title=Graphical Coordinates", status );
/* Initialise a 2D Plot structure (the parent class) as the first component
within the Plot3D structure, allocating memory if necessary. We supply
dummy arguments since we will not be using the parent Plot class to
draw anything. We supply a NULL vtab pointer so that methods defined by
the Plot class will be used during the construction of the Plot3D. Once
the Plot3D is fully constructed, we will use astSetVtab to establish
the correct vtab. */
new = (AstPlot3D *) astInitPlot( mem, size, 0, NULL, name, NULL, fjunkbox,
djunkbox );
if ( astOK ) {
/* Initialise the Plot3D data. */
/* ----------------------------- */
/* Remove all Frames from the parent FrameSet except for the base (2D graphics)
Frame (we leave this last Frame since an error is reported if the last
Frame is removed from a FrameSet). We do this by repeatedly removing the
first Frame, causing all remaining Frames to have their index reduced by 1.
When the base Frame arrives at index 1, we skip it and start removing the
second frame instead. */
nfrm = astGetNframe( new );
i = 1;
for( ii = 0; ii < nfrm; ii++ ) {
if( i > 1 || astGetBase( new ) != 1 ) {
astRemoveFrame( new, i );
} else {
i = 2;
}
}
/* Add in the 3D graphics Frame, using a PermMap to connect it to the
2D graphics Frame. */
map = (AstMapping *) astPermMap( 2, NULL, 3, NULL, NULL, "", status );
astAddFrame( new, 1, map, graphicsframe );
map = astAnnul( map );
/* And remove the 2D GRAPHICS Frame, leaving just the 3D GRAPHICS Frame
in the FrameSet, with index 1. */
astRemoveFrame( new, 1 );
/* Get the index of the current (physical) and base (pixel) Frames in
the supplied FrameSet. */
bi = astGetBase( fset );
ci = astGetCurrent( fset );
/* Temporarily set the current Frame to be the pixel frame. */
astSetCurrent( fset, bi );
/* Get a double precision version of "graphbox", and store it in the
Plot3D structure. */
new->gbox[ 0 ] = (double) graphbox[ 0 ];
new->gbox[ 1 ] = (double) graphbox[ 1 ];
new->gbox[ 2 ] = (double) graphbox[ 2 ];
new->gbox[ 3 ] = (double) graphbox[ 3 ];
new->gbox[ 4 ] = (double) graphbox[ 4 ];
new->gbox[ 5 ] = (double) graphbox[ 5 ];
/* The base Frame of the supplied FrameSet is mapped linearly onto the
graphics frame. Create a WinMap that maps the base box (within the
base Frame of the supplied FrameSet) onto the graphics box. */
map = (AstMapping *) astWinMap( 3, new->gbox, new->gbox + 3, basebox,
basebox + 3, "", status );
/* Add the supplied FrameSet into the Plot3D (i.e. FrameSet) created
earlier. This leaves the graphics frame with index 1 in the
returned Plot3D. */
astAddFrame( (AstFrameSet *) new, 1, map, fset );
map = astAnnul( map );
/* Set the current Frame in the Plot to be the physical coordinate Frame
(with index incremented by one because the graphics Frame has been added). */
astSetCurrent( (AstFrameSet *) new, ci + 1 );
/* Note the index of the original base Frame in the Plot3D FrameSet */
new->pix_frame = bi + 1;
/* Re-establish the original current Frame in the supplied FrameSet. */
astSetCurrent( fset, ci );
/* Initialise the Plot pointers. */
new->plotxy = NULL;
new->plotxz = NULL;
new->plotyz = NULL;
/* Initialise other attributes */
new->rootcorner = -1;
/* Initialise the normal vector to the plane used by astText and astMark. */
new->norm[ 0 ] = AST__BAD;
new->norm[ 1 ] = AST__BAD;
new->norm[ 2 ] = AST__BAD;
/* Create three 2D Plots to describe the three planes in the cube. */
CreatePlots( new, fset, graphbox, basebox, status );
/* Ensure that attempts to use the graphics interface of the parent
Plot structure get forwarded to the relevant 3D routines defined in
this class. */
astGrfSet( new, "Attr", (AstGrfFun) Attr3D );
astSetGrf( new, 1 );
/* Change the virtual function table stored in the new Plot3D, from the Plot
vtab (established when astINitPlot was called above), to the supplied
vtab. */
if( vtab ) astSetVtab( new, vtab );
/* Ensure that these Plots use the grf functions defined by this class
(Plot3D). This means that whenever a Plot draws anything, it will use
the appropriate grf function defined in this class to do the drawing.
The grf functions defined in this class, convert the grf call into a
grf3D call apprpriate the plane spanned by the Plot. */
Set3DGrf( new, new->plotxy, XY, status );
Set3DGrf( new, new->plotxz, XZ, status );
Set3DGrf( new, new->plotyz, YZ, status );
/* Set up the Edges attributes in the encapsulated Plots so that labels
appear on the requited edges. Initially, the root corner is "LLL"
(i.e. the lower bound on every axis). */
ChangeRootCorner( new, 0, 0, status );
}
/* Annul the frame. */
graphicsframe = astAnnul( graphicsframe );
/* If an error occurred, clean up by deleting the new object. */
if ( !astOK ) new = astDelete( new );
/* Annul the pointer to the base Frame and FrameSet. */
baseframe = astAnnul( baseframe );
fset = astAnnul( fset );
/* Return a pointer to the new object. */
return new;
}
AstPlot3D *astLoadPlot3D_( void *mem, size_t size, AstPlot3DVtab *vtab,
const char *name, AstChannel *channel, int *status ) {
/*
*+
* Name:
* astLoadPlot3D
* Purpose:
* Load a Plot3D.
* Type:
* Protected function.
* Synopsis:
* #include "plot3d.h"
* AstPlot3D *astLoadPlot3D( void *mem, size_t size,
* AstPlot3DVtab *vtab,
* const char *name, AstChannel *channel )
* Class Membership:
* Plot3D loader.
* Description:
* This function is provided to load a new Plot3D 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
* Plot3D structure in this memory, using data read from the
* input Channel.
* Parameters:
* mem
* A pointer to the memory into which the Plot3D is to be
* loaded. This must be of sufficient size to accommodate the
* Plot3D data (sizeof(Plot3D)) 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 Plot3D (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 Plot3D 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(AstPlot3D) is used instead.
* vtab
* Pointer to the start of the virtual function table to be
* associated with the new Plot3D. If this is NULL, a pointer
* to the (static) virtual function table for the Plot3D 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 "Plot3D" is used instead.
* Returned Value:
* A pointer to the new Plot3D.
* 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 Constants: */
#define KEY_LEN 50 /* Maximum length of a keyword */
/* Local Variables: */
astDECLARE_GLOBALS
AstPlot3D *new;
char key[ KEY_LEN + 1 ];
char *text;
int axis;
int i;
/* 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 Plot3D. In this case the
Plot3D belongs to this class, so supply appropriate values to be
passed to the parent class loader (and its parent, etc.). */
if ( !vtab ) {
size = sizeof( AstPlot3D );
vtab = &class_vtab;
name = "Plot3D";
/* If required, initialise the virtual function table for this class. */
if ( !class_init ) {
astInitPlot3DVtab( vtab, name );
class_init = 1;
}
}
/* Allocate memory to hold the new Object. We allocate it now rather than
waiting for astInitObject to allocate it so that we can pass a NULL
vtab pointer to the Plot loader, thus causing the Plot loader to use the
function implementations provided by the Plot class rather than those
provided by the class being instantiated. */
if( !mem ) mem = astMalloc( size );
/* 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 Plot3D. Pass a NULL vtab pointer so that the "new" object
will be loaded using Plot methods rather than than Plot3D methods.
This is important because the implementations provided by the Plot3D
class for the Plot attribute accessors require the existence of the
encapsulated Plots held within the Plot3D, but these have not yet been
created. */
new = astLoadPlot( mem, size, NULL, name, channel );
if ( astOK ) {
/* Now modify the new object to use the supplied vtab. */
astSetVtab( new, vtab );
/* Read input data. */
/* ================ */
/* Request the input Channel to read all the input data appropriate to
this class into the internal "values list". */
astReadClassData( channel, "Plot3D" );
/* Now read each individual data item from this list and use it to
initialise the appropriate instance variable(s) for this class. */
/* In the case of attributes, we first read the "raw" input value,
supplying the "unset" value as the default. If a "set" value is
obtained, we then use the appropriate (private) Set... member
function to validate and set the value properly. */
/* Norm. */
/* ----- */
for( axis = 0; axis < 3; axis++ ) {
(void) sprintf( key, "norm%d", axis + 1 );
new->norm[ axis ] = astReadDouble( channel, key, AST__BAD );
if( TestNorm( new, axis, status ) ) SetNorm( new, axis, new->norm[ axis ], status );
}
/* RootCorner. */
/* ----------- */
text = astReadString( channel, "rootcn", " " );
if( astOK && strcmp( text, " " ) ) {
new->rootcorner = RootCornerInt( text, status );
if( new->rootcorner < 0 && astOK ) {
astError( AST__INTER, "astRead(Plot3D): Corrupt Plot3D contains "
"invalid RootCorner attribute value (%s).", status, text );
}
} else {
new->rootcorner = -1;
}
text = astFree( text );
/* Labelled axes */
new->axis_plot1[0] = astReadInt( channel, "axplx1", -1 );
new->axis_plot1[1] = astReadInt( channel, "axply1", -1 );
new->axis_plot1[2] = astReadInt( channel, "axplz1", -1 );
new->axis_index1[0] = astReadInt( channel, "axinx1", -1 );
new->axis_index1[1] = astReadInt( channel, "axiny1", -1 );
new->axis_index1[2] = astReadInt( channel, "axinz1", -1 );
/* Unlabelled axes */
new->axis_plot2[0] = astReadInt( channel, "axplx2", -1 );
new->axis_plot2[1] = astReadInt( channel, "axply2", -1 );
new->axis_plot2[2] = astReadInt( channel, "axplz2", -1 );
new->axis_index2[0] = astReadInt( channel, "axinx2", -1 );
new->axis_index2[1] = astReadInt( channel, "axiny2", -1 );
new->axis_index2[2] = astReadInt( channel, "axinz2", -1 );
/* Plot that spans two connected 3D axes. */
new->baseplot = astReadInt( channel, "basepl", -1 );
/* XY Plot */
new->plotxy = astReadObject( channel, "plotxy", NULL );
/* XZ Plot */
new->plotxz = astReadObject( channel, "plotxz", NULL );
/* YZ Plot */
new->plotyz = astReadObject( channel, "plotyz", NULL );
/* The index within the Plot3D FrameSet, of the original base Frame in
the FrameSet supplied when the Plot3D was constructed. */
new->pix_frame = astReadInt( channel, "pixfrm", AST__NOFRAME );
/* Ensure that these Plots use the grf functions defined by this class
(Plot3D). This means that whener a Plot draws anything, it will use
the appropriate grf function defined in this class to do the drawing.
The grf functions defined in this class, convert the grf call into a
grf3D call apprpriate the plane spanned by the Plot. */
Set3DGrf( new, new->plotxy, XY, status );
Set3DGrf( new, new->plotxz, XZ, status );
Set3DGrf( new, new->plotyz, YZ, status );
/* For attributes of the parent Plot class will have been loaded
each attribute that has a set value in the parent Plot structure,
re-set the value so that it gets copied to the copy the to the
encapsulated Plots. First do axis specific attributes. */
#define COPY_ATTR(attr,nval) \
for( i = 0; i < nval; i++ ) { \
if( astTest##attr(new,i) ) astSet##attr(new,i,astGet##attr(new,i)); \
}
COPY_ATTR(MinTick,3)
COPY_ATTR(Abbrev,3)
COPY_ATTR(Gap,3)
COPY_ATTR(LogGap,3)
COPY_ATTR(LogPlot,3)
COPY_ATTR(LogTicks,3)
COPY_ATTR(LogLabel,3)
COPY_ATTR(LabelUp,3)
COPY_ATTR(DrawAxes,3)
COPY_ATTR(LabelUnits,3)
COPY_ATTR(MinTickLen,3)
COPY_ATTR(MajTickLen,3)
COPY_ATTR(NumLab,3)
COPY_ATTR(NumLabGap,3)
COPY_ATTR(TextLab,3)
COPY_ATTR(TextLabGap,3)
COPY_ATTR(Style,AST__NPID)
COPY_ATTR(Font,AST__NPID)
COPY_ATTR(Colour,AST__NPID)
COPY_ATTR(Width,AST__NPID)
COPY_ATTR(Size,AST__NPID)
#undef COPY_ATTR
/* Now do attributes that are not axis specific. */
#define COPY_ATTR(attr) \
if( astTest##attr(new) ) astSet##attr(new,astGet##attr(new));
COPY_ATTR(Ink)
COPY_ATTR(Tol)
COPY_ATTR(Invisible)
COPY_ATTR(TickAll)
COPY_ATTR(ForceExterior)
COPY_ATTR(Border)
COPY_ATTR(Clip)
COPY_ATTR(ClipOp)
COPY_ATTR(Escape)
COPY_ATTR(Grid)
COPY_ATTR(Labelling)
#undef COPY_ATTR
/* If an error occurred, clean up by deleting the new Plot3D. */
if ( !astOK ) new = astDelete( new );
}
/* Return the new Plot3D pointer. */
return new;
/* Undefine macros local to this function. */
#undef KEY_LEN
}
/* 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. */
void astClearRootCorner_( AstPlot3D *this, int *status ) {
if ( !astOK ) return;
(**astMEMBER(this,Plot3D,ClearRootCorner))( this, status );
}
void astSetRootCorner_( AstPlot3D *this, int value, int *status ) {
if ( !astOK ) return;
(**astMEMBER(this,Plot3D,SetRootCorner))( this, value, status );
}
/* Special public interface functions. */
/* =================================== */
/* These provide the public interface to certain special functions
whose public interface cannot be handled using macros (such as
astINVOKE) alone. In general, they are named after the
corresponding protected version of the function, but with "Id"
appended to the name. */
/* Public 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. */
AstPlot3D *astPlot3DId_( void *frame_void, const float graphbox[6],
const double basebox[6], const char *, ... );
/* Special interface function implementations. */
/* ------------------------------------------- */
AstPlot3D *astPlot3DId_( void *frame_void, const float graphbox[6],
const double basebox[6], const char *options, ... ) {
/*
*++
* Name:
c astPlot3D
f AST_PLOT3D
* Purpose:
* Create a Plot3D.
* Type:
* Public function.
* Synopsis:
c #include "plot3d.h"
c AstPlot3D *astPlot3D( AstFrame *frame, const float graphbox[ 6 ],
c const double basebox[ 6 ], const char *options, ... )
f RESULT = AST_PLOT3D( FRAME, GRAPHBOX, BASEBOX, OPTIONS, STATUS )
* Class Membership:
* Plot3D constructor.
* Description:
* This function creates a new Plot3D and optionally initialises
* its attributes.
*
* A Plot3D is a specialised form of Plot that provides facilities
* for producing 3D graphical output.
* Parameters:
c frame
f FRAME = INTEGER (Given)
* Pointer to a Frame describing the physical coordinate system
* in which to plot. A pointer to a FrameSet may also be given,
* in which case its current Frame will be used to define the
* physical coordinate system and its base Frame will be mapped
* on to graphical coordinates (see below).
*
* If a null Object pointer (AST__NULL) is given, a default
* 3-dimensional Frame will be used to describe the physical
* coordinate system. Labels, etc. may then be attached to this
* by setting the appropriate Frame attributes
* (e.g. Label(axis)) for the Plot.
c graphbox
f GRAPHBOX( 6 ) = REAL (Given)
* An array giving the position and extent of the plotting volume
* (within the plotting space of the underlying graphics system)
* in which graphical output is to appear. This must be
* specified using graphical coordinates appropriate to the
* underlying graphics system.
*
* The first triple of values should give the coordinates of the
* bottom left corner of the plotting volume and the second triple
* should give the coordinates of the top right corner. The
* coordinate on the horizontal axis should be given first in
* each pair. Note that the order in which these points are
* given is important because it defines up, down, left and
* right for subsequent graphical operations.
c basebox
f BASEBOX( 6 ) = DOUBLE PRECISION (Given)
* An array giving the coordinates of two points in the supplied
* Frame (or in the base Frame if a FrameSet was supplied) which
* correspond to the bottom left and top right corners of the
* plotting volume, as specified above. This range of coordinates
* will be mapped linearly on to the plotting area. The
* coordinates should be given in the same order as above.
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 Plot3D. 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.
c If no initialisation is required, a zero-length string may be
c supplied.
f A character string containing an optional comma-separated
f list of attribute assignments to be used for initialising the
f new Plot3D. The syntax used is identical to that for the
f AST_SET routine. If no initialisation is required, a blank
f value may be supplied.
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 astPlot3D()
f AST_PLOT3D = INTEGER
* A pointer to the new Plot3D.
* Notes:
* - The base Frame of the returned Plot3D will be a new Frame which
* is created by this function to represent the coordinate system
* of the underlying graphics system (graphical coordinates). It is
* given a Frame index of 1 within the Plot3D. The choice of base
* Frame (Base attribute) should not, in general, be changed once a
* Plot3D has been created (although you could use this as a way of
* moving the plotting area around on the plotting surface).
c - If a Frame is supplied (via the "frame" pointer), then it
f - If a Frame is supplied (via the FRAME pointer), then it
* becomes the current Frame of the new Plot3D and is given a Frame
* index of 2.
c - If a FrameSet is supplied (via the "frame" pointer), then
f - If a FrameSet is supplied (via the FRAME pointer), then
* all the Frames within this FrameSet become part of the new Plot3D
* (where their Frame indices are increased by 1), with the
* FrameSet's current Frame becoming the current Frame of the Plot3D.
* - At least one of the three axes of the current Frame must be
* independent of the other two current Frame axes.
* - If a null Object pointer (AST__NULL) is supplied (via the
c "frame" pointer), then the returned Plot3D will contain two
f FRAME pointer), then the returned Plot3D will contain two
* Frames, both created by this function. The base Frame will
* describe graphics coordinates (as above) and the current Frame
* will be a basic Frame with no attributes set (this will
* therefore give default values for such things as the Plot3D Title
* and the Label on each axis). Physical coordinates will be mapped
* linearly on to graphical coordinates.
* - An error will result if the Frame supplied (or the base Frame
* if a FrameSet was supplied) is not 3-dimensional.
* - A null Object pointer (AST__NULL) will be returned if this
c function is invoked with the AST error status set, or if it
f function is invoked with STATUS set to an error value, or if it
* should fail for any reason.
*--
* Implementation Notes:
* - This function implements the external (public) interface to
* the astPlot3D constructor function. It returns an ID value
* (instead of a true C pointer) to external users, and must be
* provided because astPlot3D_ has a variable argument list which
* cannot be encapsulated in a macro (where this conversion would
* otherwise occur).
* - Because no checking or casting of arguments is performed
* before the function is invoked, the "frame" parameter is of type
* (void *) and is converted from an ID value to a pointer and
* validated within the function itself.
* - The variable argument list also prevents this function from
* invoking astPlot3D_ 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.
*/
/* Local Variables: */
astDECLARE_GLOBALS /* Pointer to thread-specific global data */
AstFrame *frame; /* Pointer to Frame structure */
AstPlot3D *new; /* Pointer to new Plot3D */
va_list args; /* Variable argument list */
int *status; /* Pointer to inherited status value */
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(NULL);
/* Get a pointer to the inherited status value. */
status = astGetStatusPtr;
/* Check the global status. */
if ( !astOK ) return NULL;
/* Initialise variables to avoid "used of uninitialised variable"
messages from dumb compilers. */
new = NULL;
/* Obtain a Frame pointer from any ID supplied and validate the
pointer to ensure it identifies a valid Frame. */
if( frame_void ){
frame = astVerifyFrame( astMakePointer( frame_void ) );
} else {
frame = NULL;
}
/* Check the pointer can be used. */
if ( astOK ) {
/* Initialise the Plot3D, allocating memory and initialising the
virtual function table as well if necessary. */
new = astInitPlot3D( NULL, sizeof( AstPlot3D ), !class_init,
&class_vtab, "Plot3D", frame, graphbox,
basebox );
/* 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
Plot3D'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 Plot3D. */
return astMakeId( new );
}
/* Macros that define method to override the methods of the Plot class
that are not currently implemented by this class. They just report an
error if called. */
#define METHOD1(name) \
static void name(ARGLIST,int *status){ \
if( !astOK ) return; \
astError( AST__INTER, "ast##name(%s): The ast##name " \
"method cannot be used with a %s (programming error).", status, \
astGetClass( this ), astGetClass( this ) ); \
}
#define METHOD2(name,rettype,retval) \
static rettype name(ARGLIST,int *status){ \
if( !astOK ) return retval; \
astError( AST__INTER, "ast##name(%s): The ast##name " \
"method cannot be used with a %s (programming error).", status, \
astGetClass( this ), astGetClass( this ) ); \
return retval; \
}
#define ARGLIST AstPlot *this
METHOD2(GetGrfContext,AstKeyMap *,NULL)
#undef ARGLIST
#define ARGLIST AstPlot *this
METHOD1(GrfPop)
#undef ARGLIST
#define ARGLIST AstPlot *this
METHOD1(GrfPush)
#undef ARGLIST
#define ARGLIST AstPlot *this, const char *name, AstGrfFun fun
METHOD1(GrfSet)
#undef ARGLIST
#define ARGLIST AstPlot *this, int axis, const double start[], double length
METHOD1(GridLine)
#undef ARGLIST
#define ARGLIST AstPlot *this, float lbnd[2], float ubnd[2]
METHOD1(BoundingBox)
#undef ARGLIST
#define ARGLIST AstPlot *this, int iframe, const double lbnd[], const double ubnd[]
METHOD1(Clip)
#undef ARGLIST
#define ARGLIST AstPlot *this, const double start[], const double finish[]
METHOD1(Curve)
#undef ARGLIST
#define ARGLIST AstPlot *this, AstMapping *map
METHOD1(GenCurve)
#undef ARGLIST
ast-8.0.7/mapping.c 0000664 0001750 0001750 00003717541 12610415012 011051 0000000 0000000 /*
*class++
* Name:
* Mapping
* Purpose:
* Inter-relate two coordinate systems.
* Constructor Function:
* None.
* Description:
* This class provides the basic facilities for transforming a set
* of coordinates (representing "input" points) to give a new set
* of coordinates (representing "output" points). It is used to
* describe the relationship which exists between two different
* coordinate systems and to implement operations which make use of
* this (such as transforming coordinates and resampling grids of
* data). However, the Mapping class does not have a constructor
* function of its own, as it is simply a container class for a
* family of specialised Mappings which implement particular types
* of coordinate transformation.
* Inheritance:
* The Mapping class inherits from the Object class.
* Attributes:
* In addition to those attributes common to all Objects, every
* Mapping also has the following attributes:
*
* - Invert: Mapping inversion flag
* - IsLinear: Is the Mapping linear?
* - IsSimple: Has the Mapping been simplified?
* - Nin: Number of input coordinates for a Mapping
* - Nout: Number of output coordinates for a Mapping
* - Report: Report transformed coordinates?
* - TranForward: Forward transformation defined?
* - TranInverse: Inverse transformation defined?
* Functions:
c In addition to those functions applicable to all Objects, the
c following functions may also be applied to all Mappings:
f In addition to those routines applicable to all Objects, the
f following routines may also be applied to all Mappings:
*
c - astDecompose: Decompose a Mapping into two component Mappings
c - astTranGrid: Transform a grid of positions
c - astInvert: Invert a Mapping
c - astLinearApprox: Calculate a linear approximation to a Mapping
c - astMapBox: Find a bounding box for a Mapping
c - astMapSplit: Split a Mapping up into parallel component Mappings
c - astQuadApprox: Calculate a quadratic approximation to a 2D Mapping
c - astRate: Calculate the rate of change of a Mapping output
c - astRebin: Rebin a region of a data grid
c - astRebinSeq: Rebin a region of a sequence of data grids
c - astResample: Resample a region of a data grid
c - astRemoveRegions: Remove any Regions from a Mapping
c - astSimplify: Simplify a Mapping
c - astTran1: Transform 1-dimensional coordinates
c - astTran2: Transform 2-dimensional coordinates
c - astTranN: Transform N-dimensional coordinates
c - astTranP: Transform N-dimensional coordinates held in separate arrays
f - AST_DECOMPOSE: Decompose a Mapping into two component Mappings
f - AST_TRANGRID: Transform a grid of positions
f - AST_INVERT: Invert a Mapping
f - AST_LINEARAPPROX: Calculate a linear approximation to a Mapping
f - AST_QUADAPPROX: Calculate a quadratic approximation to a 2D Mapping
f - AST_MAPBOX: Find a bounding box for a Mapping
f - AST_MAPSPLIT: Split a Mapping up into parallel component Mappings
f - AST_RATE: Calculate the rate of change of a Mapping output
f - AST_REBIN: Rebin a region of a data grid
f - AST_REBINSEQ: Rebin a region of a sequence of data grids
f - AST_REMOVEREGIONS: Remove any Regions from a Mapping
f - AST_RESAMPLE: Resample a region of a data grid
f - AST_SIMPLIFY: Simplify a Mapping
f - AST_TRAN1: Transform 1-dimensional coordinates
f - AST_TRAN2: Transform 2-dimensional coordinates
f - AST_TRANN: Transform N-dimensional coordinates
* Copyright:
* Copyright (C) 1997-2006 Council for the Central Laboratory of the
* Research Councils
* 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
* .
* Authors:
* RFWS: R.F. Warren-Smith (Starlink)
* MBT: Mark Taylor (Starlink)
* DSB: David S. Berry (Starlink)
* History:
* 1-FEB-1996 (RFWS):
* Original version.
* 29-FEB-1996 (RFWS):
* Minor improvements to error messages.
* 15-JUL-1996 (RFWS):
* Support external interface.
* 13-DEC-1996 (RFWS):
* Added the astMapMerge method.
* 13-DEC-1996 (RFWS):
* Added the astSimplify method.
* 27-MAY-1997 (RFWS):
* Improved the astSimplify method to use astMapMerge to
* simplify a single Mapping where possible.
* 29-MAY-1998 (RFWS):
* Added the MapBox method.
* 13-NOV-1998 (RFWS):
* Made default MapBox convergence accuracy larger (i.e. less
* accurate).
* 10-DEC-1998 (RFWS):
* First useful implementation of astResample.
* 16-AUG-1999 (RFWS):
* Fixed bug in SpecialBounds - wrong number of coordinates being used
* when checking for bad output coordinate values.
* 17-AUG-1999 (RFWS):
* Improved the convergence security of MapBox (return to older but
* less efficient setting).
* 24-NOV-2000 (MBT):
* Fixed bug (function being invoked as wrong type) in AST__UINTERP
* scheme, and added new AST__BLOCKAVE scheme, in astResample.
* 9-JAN-2001 (DSB):
* Changed in and out arguments for TranN from type "double (*)[]"
* to "double *".
* 8-JAN-2003 (DSB):
* Changed private InitVtab method to protected astInitMappingVtab
* method.
* 10-JUL-2003 (DSB):
* Added method astRate.
* 2-SEP-2004 (DSB):
* Free resources before leaving astRate.
* 31-AUG-2004 (DSB):
* Make the LinearApprox function protected rather than private,
* rename it to astLinearApprox, and make the bounds parameters of
* type double rather than int. Also, correct the size of the fit
* coefficient array (was "(nin+1)*nout", now is "(nout+1)*nin").
* Also correct the index of the first gradient coefficient from
* "fit+nout" to "fit+nin". These errors have probably never been
* noticed because they make no difference if nin=nout, which is
* usually the case.
* 6-SEP-2004 (DSB):
* Make astRate more robust by adding checks for unusal conditions.
* 20-SEP-2004 (DSB):
* Make the LinearApprox function public and change its interface
* to be more appropriate for public use. This involved swapping the
* direction of the fit (the original astLinearApprox fitted the
* inverse transformation, but the public version now fits the forwrd
* transformation).
* 4-OCT-2004 (DSB):
* Modify astMapList to return flag indicating presence of inverted
* CmpMaps in supplied Mapping.
* 9-NOV-2004 (DSB):
* Override astEqual method.
* 6-DEC-2004 (DSB):
* Remove the second derivative estimate from the astRate function
* since CmpMap has trouble calculating it.
* 17-DEC-2004 (DSB):
* Added astMapSplit
* 22-APR-2005 (DSB):
* Modified SpecialBounds to handle cases where some irrelevant
* output always produces bad values (e.g. a PermMap may do this).
* 30-JUN-2005 (DSB):
* Added astRebin.
* 7-JUL-2005 (DSB):
* Make MapSplit public rather than protected.
* 11-AUG-2005 (DSB):
* Added the AST__CONSERVEFLUX flag (used by astResampleX).
* 17-AUG-2005 (DSB):
* Added the AST__SOMBCOS kernel.
* 31-AUG-2005 (DSB):
* Added astRebinSeq.
* 9-SEP-2005 (DSB):
* Corrected axis indices returned by public interface for astMapSplit.
* 31-JAN-2006 (DSB):
* Added IsSimple attribute.
* 2-FEB-2006 (DSB):
* Corrections to prologue of astLinearApprox.
* 16-FEB-2006 (DSB):
* Some speed optimisations to rebinning code.
* 2-MAR-2006 (DSB):
* Use HAVE_LONG_DOUBLE in place of AST_LONG_DOUBLE
* 7-MAR-2006 (DSB):
* Added astTranGrid.
* 14-MAR-2006 (DSB):
* - The constructor no longer reports an error if the resulting
* Mapping cannot transform points in either direction. This is
* because it may be possible to simplify such a Mapping and the
* simplified Mapping may have defined transformations. E.g. if a
* Mapping which has only a forward transformation is combined in
* series with its own inverse, the combination CmpMap will simplify
* to a UnitMap (usually).
* - Reset the "issimple" flag when the Invert flag is changed.
* 9-MAY-2006 (DSB):
* Correct upper bounds for idim in RebinWithblocking. Also, remove
* the single precision "F" instantiation of the MAKE_REBINSEQ macro.
* Also correct the "nout = astGetNin" line in the MAKE_REBINSEQ
* macro to "nout = astGetNout".
* 12-MAY-2006 (DSB):
* Modify SpecialBounds to include points slightly inside the
* corners. This is because some Mappings may have singularies at
* the the edges.
* 17-MAY-2006 (DSB):
* Correct the "nout = astGetNin" line in the MAKE_RESAMPLE
* and MAKE_REBIN macros to "nout = astGetNout".
* 7-JUL-2006 (DSB):
* Change -CHAR_MAX value (used as a "not set" value for boolean
* attributes) to +CHAR_MAX, since some compilers do not allow
* chars to have negative values.
* 23-AUG-2006 (DSB):
* Change the Equal function so that it reports an error when
* called, rather than using astSimplify to determine if two Mappings
* are equal. All concrete Mapping classes should now provide
* their own implementation of astEqual, avoiding the use of
* astSimplify. This is so that astSimplify can use astEqual safely
* (i.e. without danger of entering an infinite loop).
* 24-NOV-2006 (DSB):
* Allow astRebinSeq to be called with a NULL pointer for the input
* data array.
* 14-MAR-2007 (DSB):
* Modify astRebinSeq to allow input variances to be used as weights.
* 19-MAR-2007 (DSB):
* Fix bug in LINEAR_2D macro that caused bad input pixel values to be
* treated as good.
* 16-APR-2007 (DSB):
* Account for reduction in number of degrees of freedom when
* calculating output variances on the basis of spread of input values in
* astReinSeq.
* 28-APR-2007 (DSB):
* Correct code within Rebin... and Resample... functions that provides
* optimal handling for 1- and 2- dimensional mappings. Previously, the
* check for whether or not to use these optimisations was based only on
* the dimensionality of either input (Rebin) or output (Resample). This
* could cause the optimised code to be used at inappropriate times,
* leading to an incorrect effective Mapping between input and output. The
* checks now check both input and output dimensionality in all cases.
* 3-MAY-2007 (DSB):
* An extra parameter ("nused") has been added to astRebinSeq, and
* all the rebinning stuff has been modified to keep "nused" up to date.
* This is needed to correct a fault in the generation of GENVAR
* variances.
* 12-DEC-2007 (DSB):
* Some rebinning kernels (e.g. SINCSINC) have negative values and
* can result in overall negative output weights. Therefore do not
* set output pixels with negative weights bad.
* 6-MAR-2008 (DSB):
* Add an option for astResample to leave unchanged any output pixels
* for which an interpolated value cannot be obtained. This is
* controlled by the new AST__NOBAD flag.
* 7-MAY-2008 (DSB):
* Clarified meaning of AST__GENVAR, AST__USEVAR and AST__VARWGT flags
* in astRebinSeq.
* 9-MAY-2008 (DSB):
* Prevent memory over-run in RebinSeq.
* 5-MAY-2009 (DSB):
* Added astRemoveRegions.
* 11-NOV-2009 (DSB):
* In astRebinSeq initialise "*nused" to zero (as documented) if the
* AST__REBININIT flag is supplied.
* 17-NOV-2009 (DSB):
* Added AST_DISVAR flag for use with astRebinSeq.
* 15-DEC-2009 (DSB):
* Ensure that all axes span at least one pixel when calling
* astLinearApprox.
* 18-DEC-2009 (DSB):
* When using a 1D spreading kernel (in astRebin(Seq)), if the kernel
* is not contained completely within the output array, reflect the
* section of the kernel that falls outside the output array back into
* the output array so that no flux is lost. Also discovered that the
* n-D code (i.e. the KERNEL_ND macro) incorrectly uses the first
* user-supplied parameter as the full kernel width rather than the
* half-width. This has been fixed.
* 26-FEB-2010 (DSB):
* Add astQuadApprox.
* 27-FEB-2010 (DSB):
* - Make astQuadApprox faster, and fix a bug in the calculation of
* the matrix.
* 7-JUN-2010 (DSB):
* In the KERNEL_D rebinning macros, correct the test for the
* central point being outside the bounds of the output image.
* 13-AUG-2010 (DSB):
* In astRebinSeq, scale WLIM to take account of weighting by
* input variances.
* 13-DEC-2010 (DSB):
* Ensure that astMapSplit returns a Mapping that is independent of
* the supplied Mapping (i.e. return a deep copy). This means that
* subsequent changes to the supplied Mapping cannot affect the returned
* Mapping.
* 10-FEB-2011 (DSB):
* When rebinning (in macros NEAR_1/2/ND, KERNEL_1/2/ND, LINEAR_1/2/ND),
* do not treat a zero variance as bad unless the reciprocals of the
* variances are being used as weights.
* 16-JUN-2011 (DSB):
* Allow a check for NaNs to be performed as a debugging tool after
* every invocation of astTransform. This is controlled by the
* AST_REPLACE_NAN environment variable: if unset, no check is
* performed, if set to "1" NaNs are changed to AST__BAD but no
* error is reported, if set to anything else NaNs are changed to
* AST__BAD and an error is reported.
* 6-JUL-2012 (DSB):
* The astRebinSeq family was normalising the returned data and
* variances values incorrectly, when the AST__REBINEND flag was
* supplied. The exact size of the error depended on the nature of
* the Mapping and the spreading method, and so is hard to predict.
* 20-JUL-2012 (DSB):
* Major re-structuring of astRebinSeq to add further
* corrections to the normalisation. The model is now that each
* input array is first rebinned and then scaled to preserve the
* total data sum, and then each final output pixel is the weighed
* mean of all the aligned rebinned pixels.
* 13-AUG-2012 (DSB):
* Added AST__NONORM flag for asstRebuinSeq.
* 30-AUG_2012 (DSB):
* Added AST__CONSERVEFLUX flag for astRebinSeq.
* 10-SEP-2012 (DSB):
* Cater for Mappings that have different numbers of inputs and
* outputs when finding the flux conservation factor within
* astRebinSeq and astResample.
* 1-OCT-2012 (DSB):
* Ensure astRebinSeq does not create any negative output
* variances.
* 2-OCT-2012 (DSB):
* - Check for Infs as well as NaNs.
* - In Rate, break out of the loop if the RMS is very small, not
* just if it is exactly zero.
* 5-OCT-2012 (DSB):
* Complete re-write of Rate. It's now much simpler, faster and
* more reliable.
* 16-OCT-2012 (DSB):
* In MatrixDet, ignore rows/columns filled with AST_BAD as well as
* rows/columns filled with zeros.
* 26-APR-2013 (DSB):
* Change the "nused" parameter of astRebinSeq from "int *" to
* "size_t *" to allow greater amounts of data to be pasted into
* the output array.
* 29-APR-2013 (DSB):
* No sot simplify Mappings that have a set value for their Ident
* attribute. If an Ident value has been set then it means that we
* should be trying to preserve the identify of the Mapping. This
* is implemented via a new protected method (astDoNotSimplify) which
* is overridden by the Frame class so that this restriction applies
* only to genuine Mappings, not Frames.
* 9-MAY-2013 (DSB):
* Change the "nused" parameter of astRebinSeq from "size_t *" to
* "int64_t *" to cater for systems where "size_t" is only 32 bits long.
* 20-MAY-2013 (DSB):
* Always perform a linear fit in RebinAdaptively if flux
* conservation is requested.
* 18-JUL-2013 (DSB):
* Correct logic for determining whether to divide or not in
* RebinAdaptively. The old logic could lead to infinite recursion.
* 1-SEP-2014 (DSB):
* Modify astLinearAPprox to avoid using regularly placed
* test points, as such regular placement may result in
* non-representative behaviour.
* 25-SEP-2014 (DSB):
* Add support for B and UB data types to astRebin and astRebinSeq.
* 23-OCT-2014 (DSB):
* Report an error if arrays have too many pixels to count in a 32
* bit int (astTranGrid, astResample, astRebin and astRebinSeq).
* 23-APR-2015 (DSB):
* Use one bit of this->flags to store the "IsSimple" attribute
* rather using a whole char (this->issimple).
*class--
*/
/* Module Macros. */
/* ============== */
/* Set the name of the class we are implementing. This indicates to the header
files that define class interfaces that they should make "protected"
symbols available. */
#define astCLASS Mapping
/* Define numerical constants for use in thie module. */
#define GETATTRIB_BUFF_LEN 50
#define RATEFUN_MAX_CACHE 5
#define RATE_ORDER 8
/* Include files. */
/* ============== */
/* Configuration results */
/* ---------------------- */
#if HAVE_CONFIG_H
#include
#endif
/* 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 "pointset.h" /* Sets of points/coordinates */
#include "channel.h" /* I/O channels */
#include "mapping.h" /* Interface definition for this class */
#include "cmpmap.h" /* Compund Mappings */
#include "unitmap.h" /* Unit Mappings */
#include "permmap.h" /* Axis permutations */
#include "winmap.h" /* Window scalings */
#include "pal.h" /* SLALIB interface */
#include "globals.h" /* Thread-safe global data access */
/* Error code definitions. */
/* ----------------------- */
#include "ast_err.h" /* AST error codes */
/* C header files. */
/* --------------- */
#include
#include
#include
#include
#include
#include
#include
/* Module type definitions. */
/* ======================== */
/* Enum to represent the data type when resampling a grid of data. */
typedef enum DataType {
#if HAVE_LONG_DOUBLE /* Not normally implemented */
TYPE_LD,
#endif
TYPE_D,
TYPE_F,
TYPE_L,
TYPE_UL,
TYPE_K,
TYPE_UK,
TYPE_I,
TYPE_UI,
TYPE_S,
TYPE_US,
TYPE_B,
TYPE_UB
} DataType;
/* Data structure to hold information about a Mapping for use by
optimisation algorithms. */
typedef struct MapData {
AstMapping *mapping; /* Pointer to the Mapping */
AstPointSet *pset_in; /* Pointer to input PointSet */
AstPointSet *pset_out; /* Pointer to output PointSet */
double *lbnd; /* Pointer to lower constraints on input */
double *ubnd; /* Pointer to upper constraints on input */
double **ptr_in; /* Pointer to input PointSet coordinates */
double **ptr_out; /* Pointer to output PointSet coordinates */
int coord; /* Index of output coordinate to optimise */
int forward; /* Use forward transformation? */
int negate; /* Negate the output value? */
int nin; /* Number of input coordinates per point */
int nout; /* Number of output coordinates per point */
} MapData;
/* Convert from floating point to floating point or integer */
#define CONV(IntType,val) ( ( IntType ) ? (int) ( (val) + (((val)>0)?0.5:-0.5) ) : (val) )
/* 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_equal)( AstObject *, AstObject *, int * );
/* 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->GetAttrib_Buff[ 0 ] = 0; \
globals->Unsimplified_Mapping = NULL; \
globals->Rate_Disabled = 0;
/* Create the function that initialises global data for this module. */
astMAKE_INITGLOBALS(Mapping)
/* Define macros for accessing each item of thread specific global data. */
#define class_init astGLOBAL(Mapping,Class_Init)
#define class_vtab astGLOBAL(Mapping,Class_Vtab)
#define getattrib_buff astGLOBAL(Mapping,GetAttrib_Buff)
#define unsimplified_mapping astGLOBAL(Mapping,Unsimplified_Mapping)
#define rate_disabled astGLOBAL(Mapping,Rate_Disabled)
#define ratefun_pset1_cache astGLOBAL(Mapping,RateFun_Pset1_Cache)
#define ratefun_pset2_cache astGLOBAL(Mapping,RateFun_Pset2_Cache)
#define ratefun_next_slot astGLOBAL(Mapping,RateFun_Next_Slot)
#define ratefun_pset_size astGLOBAL(Mapping,RateFun_Pset_Size)
/* If thread safety is not needed, declare and initialise globals at static
variables. */
#else
/* Buffer returned by GetAttrib. */
static char getattrib_buff[ GETATTRIB_BUFF_LEN + 1 ];
/* Pointer to origin (unsimplified) Mapping, only used for reporting
error messages. */
static AstMapping *unsimplified_mapping = NULL;
/* A flag which indicates if the astRate method should be disabled in
order to improve algorithm speed in cases where the rate value is not
significant. If astRate is disabled then it always returns a constant
value of 1.0. */
static int rate_disabled = 0;
/* static values used in function "RateFun". */
static AstPointSet *ratefun_pset1_cache[ RATEFUN_MAX_CACHE ];
static AstPointSet *ratefun_pset2_cache[ RATEFUN_MAX_CACHE ];
static int ratefun_next_slot;
static int ratefun_pset_size[ RATEFUN_MAX_CACHE ];
/* Define the class virtual function table and its initialisation flag
as static variables. */
static AstMappingVtab class_vtab; /* Virtual function table */
static int class_init = 0; /* Virtual function table initialised? */
#endif
/* Prototypes for private member functions. */
/* ======================================== */
#define DECLARE_GENERIC(X,Xtype) \
static int InterpolateKernel1##X( AstMapping *, int, const int *, const int *, \
const Xtype *, const Xtype *, int, \
const int *, const double *const *, \
void (*)( double, const double *, int, \
double *, int * ), \
void (*)( double, const double *, int, \
double * ), \
int, const double *, int, Xtype, \
Xtype *, Xtype *, int * );\
\
static int InterpolateLinear##X( int, const int *, const int *, const Xtype *, \
const Xtype *, int, const int *, \
const double *const *, int, Xtype, Xtype *, \
Xtype *, int * ); \
\
static int InterpolateNearest##X( int, const int *, const int *, const Xtype *, \
const Xtype *, int, const int *, \
const double *const *, int, Xtype, Xtype *, \
Xtype *, int * ); \
\
static int Resample##X( AstMapping *, int, const int [], const int [], \
const Xtype [], const Xtype [], int, \
void (*)( void ), const double [], int, double, int, \
Xtype, int, const int [], const int [], \
const int [], const int [], Xtype [], Xtype [], int * ); \
\
static void ConserveFlux##X( double, int, const int *, Xtype, Xtype *, Xtype *, \
int * ); \
\
static void InterpolateBlockAverage##X( int, const int[], const int[], \
const Xtype [], const Xtype [], int, const int[], \
const double *const[], const double[], int, \
Xtype, Xtype *, Xtype *, int * );
DECLARE_GENERIC(B,signed char)
DECLARE_GENERIC(D,double)
DECLARE_GENERIC(F,float)
DECLARE_GENERIC(I,int)
DECLARE_GENERIC(K,INT_BIG)
DECLARE_GENERIC(L,long int)
DECLARE_GENERIC(S,short int)
DECLARE_GENERIC(UB,unsigned char)
DECLARE_GENERIC(UI,unsigned int)
DECLARE_GENERIC(UK,UINT_BIG)
DECLARE_GENERIC(UL,unsigned long int)
DECLARE_GENERIC(US,unsigned short int)
#if HAVE_LONG_DOUBLE /* Not normally implemented */
DECLARE_GENERIC(LD,long double)
#endif
#undef DECLARE_GENERIC
#define DECLARE_GENERIC(X,Xtype) \
static void Rebin##X( AstMapping *, double, int, const int [], const int [], \
const Xtype [], const Xtype [], int, const double [], int, \
double, int, Xtype, int, const int [], const int [], \
const int [], const int [], Xtype [], Xtype [], int * ); \
\
static void RebinSeq##X( AstMapping *, double, int, const int [], const int [], \
const Xtype [], const Xtype [], int, const double [], \
int, double, int, Xtype, int, const int [], \
const int [], const int [], const int [], Xtype [], \
Xtype [], double [], int64_t *, int * ); \
\
static void SpreadKernel1##X( AstMapping *, int, const int *, const int *, \
const Xtype *, const Xtype *, double, int, const int *, \
const double *const *, \
void (*)( double, const double *, int, double *, int * ), \
int, const double *, int, Xtype, int, Xtype *, \
Xtype *, double *, int64_t *, int * ); \
\
static void SpreadLinear##X( int, const int *, const int *, const Xtype *, \
const Xtype *, double, int, const int *, const double *const *, \
int, Xtype, int, Xtype *, Xtype *, double *, int64_t *, \
int * ); \
\
static void SpreadNearest##X( int, const int *, const int *, const Xtype *, \
const Xtype *, double, int, const int *, const double *const *, \
int, Xtype, int, Xtype *, Xtype *, double *, \
int64_t *, int * );
DECLARE_GENERIC(D,double)
DECLARE_GENERIC(F,float)
DECLARE_GENERIC(I,int)
DECLARE_GENERIC(UB,unsigned char)
DECLARE_GENERIC(B,signed char)
#if HAVE_LONG_DOUBLE /* Not normally implemented */
DECLARE_GENERIC(LD,long double)
#endif
#undef DECLARE_GENERIC
static AstMapping *RemoveRegions( AstMapping *, int * );
static AstMapping *Simplify( AstMapping *, int * );
static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
static const char *GetAttrib( AstObject *, const char *, int * );
static double FindGradient( AstMapping *, double *, int, int, double, double, double *, int * );
static double J1Bessel( double, int * );
static double LocalMaximum( const MapData *, double, double, double [], int * );
static double MapFunction( const MapData *, const double [], int *, int * );
static double MatrixDet( int, int, const double *, int * );
static double MaxD( double, double, int * );
static double NewVertex( const MapData *, int, double, double [], double [], int *, double [], int * );
static double Random( long int *, int * );
static double Rate( AstMapping *, double *, int, int, int * );
static double UphillSimplex( const MapData *, double, int, const double [], double [], double *, int *, int * );
static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * );
static int Equal( AstObject *, AstObject *, int * );
static int GetInvert( AstMapping *, int * );
static int GetIsLinear( AstMapping *, int * );
static int GetIsSimple( AstMapping *, int * );
static int GetNin( AstMapping *, int * );
static int GetNout( AstMapping *, int * );
static int GetReport( AstMapping *, int * );
static int GetTranForward( AstMapping *, int * );
static int GetTranInverse( AstMapping *, int * );
static int LinearApprox( AstMapping *, const double *, const double *, double, double *, int * );
static int MapList( AstMapping *, int, int, int *, AstMapping ***, int **, int * );
static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * );
static int MaxI( int, int, int * );
static int MinI( int, int, int * );
static int DoNotSimplify( AstMapping *, int * );
static int QuadApprox( AstMapping *, const double[2], const double[2], int, int, double *, double *, int * );
static int RebinAdaptively( AstMapping *, int, const int *, const int *, const void *, const void *, DataType, int, const double *, int, double, int, const void *, int, const int *, const int *, const int *, const int *, int, void *, void *, double *, int64_t *, int * );
static int RebinWithBlocking( AstMapping *, const double *, int, const int *, const int *, const void *, const void *, DataType, int, const double *, int, const void *, int, const int *, const int *, const int *, const int *, int, void *, void *, double *, int64_t *, int * );
static int ResampleAdaptively( AstMapping *, int, const int *, const int *, const void *, const void *, DataType, int, void (*)( void ), const double *, int, double, int, const void *, int, const int *, const int *, const int *, const int *, void *, void *, int * );
static int ResampleSection( AstMapping *, const double *, int, const int *, const int *, const void *, const void *, DataType, int, void (*)( void ), const double *, double, int, const void *, int, const int *, const int *, const int *, const int *, void *, void *, int * );
static int ResampleWithBlocking( AstMapping *, const double *, int, const int *, const int *, const void *, const void *, DataType, int, void (*)( void ), const double *, int, const void *, int, const int *, const int *, const int *, const int *, void *, void *, int * );
static int SpecialBounds( const MapData *, double *, double *, double [], double [], int * );
static int TestAttrib( AstObject *, const char *, int * );
static int TestInvert( AstMapping *, int * );
static int TestReport( AstMapping *, int * );
static void ClearAttrib( AstObject *, const char *, int * );
static void ClearInvert( AstMapping *, int * );
static void ClearReport( AstMapping *, int * );
static void Copy( const AstObject *, AstObject *, int * );
static void Decompose( AstMapping *, AstMapping **, AstMapping **, int *, int *, int *, int * );
static void Delete( AstObject *, int * );
static void Dump( AstObject *, AstChannel *, int * );
static void Gauss( double, const double [], int, double *, int * );
static void GlobalBounds( MapData *, double *, double *, double [], double [], int * );
static void Invert( AstMapping *, int * );
static void MapBox( AstMapping *, const double [], const double [], int, int, double *, double *, double [], double [], int * );
static void RateFun( AstMapping *, double *, int, int, int, double *, double *, int * );
static void RebinSection( AstMapping *, const double *, int, const int *, const int *, const void *, const void *, double, DataType, int, const double *, int, const void *, int, const int *, const int *, const int *, const int *, int, void *, void *, double *, int64_t *, int * );
static void ReportPoints( AstMapping *, int, AstPointSet *, AstPointSet *, int * );
static void SetAttrib( AstObject *, const char *, int * );
static void SetInvert( AstMapping *, int, int * );
static void SetReport( AstMapping *, int, int * );
static void Sinc( double, const double [], int, double *, int * );
static void SincCos( double, const double [], int, double *, int * );
static void SincGauss( double, const double [], int, double *, int * );
static void SincSinc( double, const double [], int, double *, int * );
static void Somb( double, const double [], int, double *, int * );
static void SombCos( double, const double [], int, double *, int * );
static void Tran1( AstMapping *, int, const double [], int, double [], int * );
static void Tran2( AstMapping *, int, const double [], const double [], int, double [], double [], int * );
static void TranGrid( AstMapping *, int, const int[], const int[], double, int, int, int, int, double *, int * );
static void TranGridAdaptively( AstMapping *, int, const int[], const int[], const int[], const int[], double, int, int, double *[], int * );
static void TranGridSection( AstMapping *, const double *, int, const int *, const int *, const int *, const int *, int, double *[], int * );
static void TranGridWithBlocking( AstMapping *, const double *, int, const int *, const int *, const int *, const int *, int, double *[], int * );
static void TranN( AstMapping *, int, int, int, const double *, int, int, int, double *, int * );
static void TranP( AstMapping *, int, int, const double *[], int, int, double *[], int * );
static void ValidateMapping( AstMapping *, int, int, int, int, const char *, int * );
/* Member functions. */
/* ================= */
static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
* Name:
* ClearAttrib
* Purpose:
* Clear an attribute value for a Mapping.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* void ClearAttrib( AstObject *this, const char *attrib, int *status )
* Class Membership:
* Mapping member function (over-rides the astClearAttrib protected
* method inherited from the Object class).
* Description:
* This function clears the value of a specified attribute for a
* Mapping, so that the default value will subsequently be used.
* Parameters:
* this
* Pointer to the Mapping.
* 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: */
AstMapping *this; /* Pointer to the Mapping structure */
/* Check the global error status. */
if ( !astOK ) return;
/* Obtain a pointer to the Mapping structure. */
this = (AstMapping *) this_object;
/* Check the attribute name and clear the appropriate attribute. */
/* Invert. */
/* ------- */
if ( !strcmp( attrib, "invert" ) ) {
astClearInvert( this );
/* Report. */
/* ------- */
} else if ( !strcmp( attrib, "report" ) ) {
astClearReport( this );
/* If the name was not recognised, test if it matches any of the
read-only attributes of this class. If it does, then report an
error. */
} else if ( !strcmp( attrib, "nin" ) ||
!strcmp( attrib, "nout" ) ||
!strcmp( attrib, "issimple" ) ||
!strcmp( attrib, "islinear" ) ||
!strcmp( attrib, "tranforward" ) ||
!strcmp( attrib, "traninverse" ) ) {
astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" "
"value for a %s.", status, attrib, astGetClass( this ) );
astError( AST__NOWRT, "This is a read-only attribute." , status);
/* If the attribute is still not recognised, pass it on to the parent
method for further interpretation. */
} else {
(*parent_clearattrib)( this_object, attrib, status );
}
}
/*
* Name:
* ConserveFlux
* Purpose:
* Scale the output data and variance values produced by ResampleSection
* by the given flux conservation factor.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* void ConserveFlux( double factor, int npoint, const int *offset,
* badval, *out,
* *out_var )
* Class Membership:
* Mapping member function.
* Description:
* This is a set of functions which scale the supplied resampled data
* values by the given flux conservation factor. It also scales any
* variances by the square of the factor.
* Parameters:
* factor
* The flux conservation factor. This should be the ratio of the
* output pixel size to the input pixel size, in the locality of
* the supplied data values.
* npoint
* The number of points at which the input grid was resampled.
* offset
* Pointer to an array of integers with "npoint" elements. For
* each output point, this array should contain the zero-based
* offset in the output array(s) (i.e. the "out" and,
* optionally, the "out_var" arrays) at which the resampled
* output value(s) is stored.
* badval
* This parameter specifies the value which is used to identify
* bad data and/or variance values in the output array(s).
* out
* Pointer to an array in which the resampled data is supplied. Note
* that details of how the output grid maps on to this array
* (e.g. the storage order, number of dimensions, etc.) is
* arbitrary and is specified entirely by means of the "offset"
* array. The "out" array should therefore contain sufficient
* elements to accommodate the "offset" values supplied. There
* is no requirement that all elements of the "out" array should
* be assigned values, and any which are not addressed by the
* contents of the "offset" array will be left unchanged.
* out_var
* An optional pointer to an array with the same data type and
* size as the "out" array, in which variance estimates for
* the resampled values are supplied. If no output variance estimates
* are available, a NULL pointer should be given.
* Notes:
* - There is a separate function for each numerical type of
* gridded data, distinguished by replacing the in the function
* name by the appropriate 1- or 2-character suffix.
*/
/* Define a macro to implement the function for a specific data
type. */
#define MAKE_CONSERVEFLUX(X,Xtype) \
static void ConserveFlux##X( double factor, int npoint, const int *offset, \
Xtype badval, Xtype *out, Xtype *out_var, int *status ) { \
\
/* Local Variables: */ \
int off_out; /* Pixel offset into output array */ \
int point; /* Loop counter for output points */ \
\
\
/* Check the global error status. */ \
if ( !astOK ) return; \
\
for ( point = 0; point < npoint; point++ ) { \
off_out = offset[ point ]; \
if( out[ off_out ] != badval ) out[ off_out ] *= factor; \
} \
\
if( out_var ) { \
factor *= factor; \
for ( point = 0; point < npoint; point++ ) { \
off_out = offset[ point ]; \
if( out_var[ off_out ] != badval ) out_var[ off_out ] *= factor; \
} \
} \
}
/* Expand the macro above to generate a function for each required
data type. */
#if HAVE_LONG_DOUBLE /* Not normally implemented */
MAKE_CONSERVEFLUX(LD,long double)
#endif
MAKE_CONSERVEFLUX(D,double)
MAKE_CONSERVEFLUX(F,float)
MAKE_CONSERVEFLUX(K,INT_BIG)
MAKE_CONSERVEFLUX(L,long int)
MAKE_CONSERVEFLUX(I,int)
MAKE_CONSERVEFLUX(S,short int)
MAKE_CONSERVEFLUX(B,signed char)
MAKE_CONSERVEFLUX(UL,unsigned long int)
MAKE_CONSERVEFLUX(UI,unsigned int)
MAKE_CONSERVEFLUX(UK,UINT_BIG)
MAKE_CONSERVEFLUX(US,unsigned short int)
MAKE_CONSERVEFLUX(UB,unsigned char)
/* Undefine the macros used above. */
#undef MAKE_CONSERVEFLUX
static void Decompose( AstMapping *this, AstMapping **map1, AstMapping **map2,
int *series, int *invert1, int *invert2, int *status ) {
/*
*+
* Name:
* astDecompose
* Purpose:
* Decompose a Mapping into two component Mappings.
* Type:
* Protected virtual function.
* Synopsis:
* #include "mapping.h"
* void astDecompose( AstMapping *this, AstMapping **map1,
* AstMapping **map2, int *series, int *invert1,
* int *invert2 )
* Class Membership:
* Mapping method.
* Description:
* This function returns pointers to two Mappings which, when applied
* either in series or parallel, are equivalent to the supplied Mapping.
*
* Since the Frame class inherits from the Mapping class, Frames can
* be considered as special types of Mappings and so this method can
* be used to decompose CmpMaps, CmpFrames, CmpRegions or Prisms.
* Parameters:
* this
* Pointer to the Mapping.
* map1
* Address of a location to receive a pointer to first component
* Mapping.
* map2
* Address of a location to receive a pointer to second component
* Mapping.
* series
* Address of a location to receive a value indicating if the
* component Mappings are applied in series or parallel. A non-zero
* value means that the supplied Mapping is equivalent to applying map1
* followed by map2 in series. A zero value means that the supplied
* Mapping is equivalent to applying map1 to the lower numbered axes
* and map2 to the higher numbered axes, in parallel.
* invert1
* The value of the Invert attribute to be used with map1.
* invert2
* The value of the Invert attribute to be used with map2.
* Applicability:
* CmpMap
* If the supplied Mapping is a CmpMap, then map1 and map2 will be
* returned holding pointers to the component Mappings used to
* create the CmpMap, either in series or parallel.
* Mapping
* For any class of Mapping other than a CmpMap, map1 will be
* returned holding a clone of the supplied Mapping pointer, and map2
* will be returned holding a NULL pointer.
* CmpFrame
* If the supplied Mapping is a CmpFrame, then map1 and map2 will be
* returned holding pointers to the component Frames used to
* create the CmpFrame. The component Frames are considered to be in
* applied in parallel.
* Frame
* For any class of Frame other than a CmpFrame, map1 will be
* returned holding a clone of the supplied Frame pointer, and map2
* will be returned holding a NULL pointer.
* Notes:
* - Any changes made to the component Mappings using the returned
* pointers will be reflected in the supplied Mapping.
* - The returned Invert values should be used in preference to the
* current values of the Invert attribute in map1 and map2. This is
* because the attributes may have changed value since the Mappings
* were combined.
* Implementation Notes:
* - This function implements the basic astDecompose method
* available via the protected interface to the Frame class. The
* public interface to this method is provided by the
* astDecomposeId_ function.
*-
*/
/* Check the global error status. */
if ( !astOK ) return;
/* The basic Mapping class returns a clone of the supplied Mapping as
map1 and a NULL pointer as map2. */
if( map1 ) *map1 = astClone( this );
if( map2 ) *map2 = NULL;
if( series ) *series = 1;
if( invert1 ) *invert1 = astGetInvert( this );
if( invert2 ) *invert2 = 0;
}
static int DoNotSimplify( AstMapping *this, int *status ) {
/*
*+
* Name:
* astMapMerge
* Purpose:
* Check if a Mapping is appropriate for simplification.
* Type:
* Protected virtual function.
* Synopsis:
* #include "mapping.h"
* int astDoNotSImplify( AstMapping *this );
* Class Membership:
* Mapping method.
* Description:
* This function returns a flag indivating if the supplied Mapping is
* appropriate for simplification.
* Parameters:
* this
* Pointer to the Mapping.
* Returned Value:
* Non-zero if the supplied Mapping is not appropriate for
* simplification, and zero otherwise.
* Notes:
* - A value of 0 will be returned if this function is invoked
* with the global error status set, or if it should fail for any
* reason.
*-
*/
/* Check inherited status. */
if( !astOK ) return 0;
/* Mappings that have a set value for the Ident attribute should not be
simplified since we want to preserve their individual identify (otherwise
why would the user have given them an Ident value?). */
return astTestIdent( this );
}
int astRateState_( int disabled, int *status ) {
/*
*+
* Name:
* astRateState
* Purpose:
* Control whether the astRate method is disabled or not.
* Type:
* Protected function.
* Synopsis:
* #include "mapping.h"
* int astRateState( int disabled )
* Class Membership:
* Mapping member function
* Description:
* Some algorithms which use use the astRate method do not actually need
* to know what the Rate value is. For instance, when the Plot class draws
* a border it evaluates the GRAPHICS->Current Mapping hundreds of time.
* If the Mapping includes a RateMap then this can be very very slow
* (depending on how the astRate method is implemented). In fact the
* border drawing algorithm onlyneeds to know if the result is bad or
* not - the actual value produced by the Mappign does not matter.
*
* Such algorithms can be speeded up by forcing the astRate method to
* return a constant value rather than actually doing the numerical
* differentiation. This can be accomplised by calling this method prior
* to implementing the algorithm. It should be called at the end in
* order to re-instate the original disabled flag.
* Parameters:
* disabled
* The new value for the astRate disabled flag.
* Returned Value:
* The original value of the astRate disabled flag.
*-
*/
astDECLARE_GLOBALS
int result;
astGET_GLOBALS(NULL);
result = rate_disabled;
rate_disabled = disabled;
return result;
}
static int Equal( AstObject *this_object, AstObject *that_object, int *status ) {
/*
* Name:
* Equal
* Purpose:
* Test if two Mappings are equivalent.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* int Equal( AstObject *this, AstObject *that, int *status )
* Class Membership:
* Mapping member function (over-rides the astEqual protected
* method inherited from the Object class).
* Description:
* This function returns a boolean result (0 or 1) to indicate whether
* two Mappings are equivalent.
*
* The implementation provided by this class (the base Mapping class)
* simply reports an error when called, since all concrete Mapping
* subclasses should provide their own implementation.
*
* Note, sub-class implementations should not use astSimplify (e.g.
* combining the two Mapping and then simplifying it), since the
* astSimplify method for certain classes (e.g. CmpMap) may use
* astEqual. Consequently, if astEqual called astSimplify, there would
* be possibilities for infinite loops.
* Parameters:
* this
* Pointer to the first Object (a Mapping).
* that
* Pointer to the second Object.
* status
* Pointer to the inherited status variable.
* Returned Value:
* One if the Frames are equivalent, zero otherwise.
* Notes:
* - The two Mappings are considered equivalent if the combination of
* the first in series with the inverse of the second simplifies to a
* UnitMap.
* - 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: */
int result; /* Result value to return */
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Invoke the Equal method inherited from the parent Object class. This checks
that the Objects are both of the same class (amongst other things). */
if( (*parent_equal)( this_object, that_object, status ) ) {
/* Report an error since the concrete sub-class should have over-riden
this method. */
astError( AST__INTER, "astEqual(Mapping): The %s class does "
"not override the abstract astEqual method inherited "
"from the base Mapping class (internal AST programming "
"error).", status, astGetClass( this_object ) );
}
/* If an error occurred, clear the result value. */
if ( !astOK ) result = 0;
/* Return the result, */
return result;
}
static double FindGradient( AstMapping *map, double *at, int ax1, int ax2,
double x0, double h, double *range, int *status ){
/*
* Name:
* FindGradient
* Purpose:
* Find the mean gradient in an interval, and the range of gradients
* within the interval.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* double FindGradient( AstMapping *map, double *at, int ax1, int ax2,
* double x0, double h, double *range, int *status )
* Class Membership:
* Mapping method.
* Description:
* This function finds the mean gradient in an interval, and the range
* of gradients within the interval.
* Parameters:
* map
* Pointer to a Mapping which yields the value of the function at x.
* The Mapping may have any number of inputs and outputs; the specific
* output representing the function value, f, is specified by ax1 and
* the specific input representing the argument, x, is specified by ax2.
* at
* A pointer to an array holding axis values at the position at which
* the function is to be evaluated. The number of values supplied
* must equal the number of inputs to the Mapping. The value supplied
* for axis "ax2" is ignored (the value of "x" is used for axis "ax2").
* ax1
* The zero-based index of the Mapping output which is to be
* differentiated. Set this to -1 to allocate, or -2 to release,
* the static resources used by this function.
* ax2
* The zero-based index of the Mapping input which is to be varied.
* x0
* The central axis value at which the function is to be evaluated.
* h
* The interval over which the fitting is to be performed.
* range
* A pointer to a location at which to return the range of
* gradients found within the interval.
* status
* Pointer to the inherited status variable.
* Returns:
* The mean gradient, or AST__BAD if the mean gradient cannot be
* calculated.
*/
/* Local Variables: */
double dh;
double g;
double gmax;
double gmin;
double ret;
double x1;
double x2;
double x[ RATE_ORDER + 2 ];
double y1;
double y2;
double y[ RATE_ORDER + 2 ];
int i0;
int i;
int ngood;
/* Initialise */
ret = AST__BAD;
/* Check the global error status. */
if ( !astOK ) return ret;
/* Store the x values at (RATE_ORDER+1) evenly spaced points over the interval
"h" centred on "x0". */
i0 = RATE_ORDER/2;
dh = h/RATE_ORDER;
for( i = 0; i <= RATE_ORDER; i++ ) {
x[ i ] = x0 + ( i - i0 )*dh;
}
/* Get the function values at these positions. */
RateFun( map, at, ax1, ax2, RATE_ORDER + 1, x, y, status );
/* Find the maximum and minimum mean gradient within any sub-interval, and
note the (x,y) values at the first and last good point within the
interval. */
y1 = AST__BAD;
y2 = AST__BAD;
gmax = AST__BAD;
gmin = AST__BAD;
ngood = 0;
for( i = 0; i < RATE_ORDER; i++ ) {
if( y[ i + 1 ] !=AST__BAD && y[ i ] != AST__BAD &&
x[ i + 1 ] != x[ i ] ) {
ngood++;
g = ( y[ i + 1 ] - y[ i ] )/( x[ i + 1 ] - x[ i ] );
if( ngood == 1 ) {
gmax = gmin = g;
} else if( g < gmin ) {
gmin = g;
} else if( g > gmax) {
gmax = g;
}
if( y1 == AST__BAD ) {
y1 = y[ i ];
x1 = x[ i ];
}
y2 = y[ i + 1 ];
x2 = x[ i + 1 ];
}
}
/* If two or more sub-intervals were usable, return the range of
gradients found, and the mean gradient. */
if( ngood > 1 ) {
ret = ( y2 - y1 )/( x2 - x1 );
if( range ) *range = ( gmax - gmin );
}
return ret;
}
static void Gauss( double offset, const double params[], int flags,
double *value, int *status ) {
/*
* Name:
* Gauss
* Purpose:
* 1-dimensional Gaussian spreading kernel.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* void Gauss( double offset, const double params[], int flags,
* double *value, int *status )
* Class Membership:
* Mapping member function.
* Description:
* This function calculates the value of a 1-dimensional sub-pixel
* spreading kernel. The function used is exp(-k*x*x).
* Parameters:
* offset
* The offset of a pixel from the central output point, measured
* in pixels.
* params
* The first element of this array should give a value for "k"
* in the exp(-k*x*x) term.
* flags
* Not used.
* value
* Pointer to a double to receive the calculated kernel value.
* status
* Pointer to the inherited status variable.
* Notes:
* - This function does not perform error checking and does not
* generate errors.
*/
/* Calculate the result. */
*value = exp( -params[ 0 ] * offset * offset );
}
static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) {
/*
* Name:
* GetAttrib
* Purpose:
* Get the value of a specified attribute for a Mapping.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* const char *GetAttrib( AstObject *this, const char *attrib, int *status )
* Class Membership:
* Mapping member function (over-rides the protected astGetAttrib
* method inherited from the Object class).
* Description:
* This function returns a pointer to the value of a specified
* attribute for a Mapping, formatted as a character string.
* Parameters:
* this
* Pointer to the Mapping.
* 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 Mapping, 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 Mapping. 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 /* Pointer to thread-specific global data */
AstMapping *this; /* Pointer to the Mapping structure */
const char *result; /* Pointer value to return */
int invert; /* Invert attribute value */
int islinear; /* IsLinear attribute value */
int issimple; /* IsSimple attribute value */
int nin; /* Nin attribute value */
int nout; /* Nout attribute value */
int report; /* Report attribute value */
int tran_forward; /* TranForward attribute value */
int tran_inverse; /* TranInverse attribute value */
/* Initialise. */
result = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(this_object);
/* Obtain a pointer to the Mapping structure. */
this = (AstMapping *) 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. */
/* Invert. */
/* ------- */
if ( !strcmp( attrib, "invert" ) ) {
invert = astGetInvert( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", invert );
result = getattrib_buff;
}
/* IsLinear. */
/* --------- */
} else if ( !strcmp( attrib, "islinear" ) ) {
islinear = astGetIsLinear( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", islinear );
result = getattrib_buff;
}
/* IsSimple. */
/* --------- */
} else if ( !strcmp( attrib, "issimple" ) ) {
issimple = astGetIsSimple( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", issimple );
result = getattrib_buff;
}
/* Nin. */
/* ---- */
} else if ( !strcmp( attrib, "nin" ) ) {
nin = astGetNin( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", nin );
result = getattrib_buff;
}
/* Nout. */
/* ----- */
} else if ( !strcmp( attrib, "nout" ) ) {
nout = astGetNout( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", nout );
result = getattrib_buff;
}
/* Report. */
/* ------- */
} else if ( !strcmp( attrib, "report" ) ) {
report = astGetReport( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", report );
result = getattrib_buff;
}
/* TranForward. */
/* ------------ */
} else if ( !strcmp( attrib, "tranforward" ) ) {
tran_forward = astGetTranForward( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", tran_forward );
result = getattrib_buff;
}
/* TranInverse. */
/* ------------ */
} else if ( !strcmp( attrib, "traninverse" ) ) {
tran_inverse = astGetTranInverse( this );
if ( astOK ) {
(void) sprintf( getattrib_buff, "%d", tran_inverse );
result = getattrib_buff;
}
/* If the attribute name was not recognised, pass it on to the parent
method for further interpretation. */
} else {
result = (*parent_getattrib)( this_object, attrib, status );
}
/* Return the result. */
return result;
}
static int GetIsLinear( AstMapping *this, int *status ) {
/*
*+
* Name:
* astGetIsLinear
* Purpose:
* Determine if a Mapping is an instance of a linear Mapping class.
* Type:
* Protected virtual function.
* Synopsis:
* #include "mapping.h"
* int astGetIsLinear( AstMapping *this )
* Class Membership:
* Mapping method.
* Description:
* This function returns a value indicating whether a Mapping is
* a member of a class of linear Mappings. The base Mapping class
* returns a value of zero. Linear Mapping classes should over-ride
* this function to return a non-zero value.
* Parameters:
* this
* Pointer to the Mapping.
* Returned Value:
* One if the Mapping is a member of a linear Mapping class. Zero
* otherwise.
* 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.
*-
*/
return 0;
}
static int GetNin( AstMapping *this, int *status ) {
/*
*+
* Name:
* astGetNin
* Purpose:
* Get the number of input coordinates for a Mapping.
* Type:
* Protected virtual function.
* Synopsis:
* #include "mapping.h"
* int astGetNin( AstMapping *this )
* Class Membership:
* Mapping method.
* Description:
* This function returns the number of input coordinate values
* required per point by a Mapping (i.e. the number of dimensions
* of the space in which input points reside).
* Parameters:
* this
* Pointer to the Mapping.
* Returned Value:
* Number of coordinate values required.
* 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 invert; /* Invert attribute value */
int result; /* Result value to return */
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Determine if the Mapping has been inverted. */
invert = astGetInvert( this );
/* Obtain the Nin value. */
if ( astOK ) result = invert ? this->nout : this->nin;
/* Return the result. */
return result;
}
static int GetNout( AstMapping *this, int *status ) {
/*
*+
* Name:
* astGetNout
* Purpose:
* Get the number of output coordinates for a Mapping.
* Type:
* Protected virtual function.
* Synopsis:
* #include "mapping.h"
* int astGetNout( AstMapping *this )
* Class Membership:
* Mapping method.
* Description:
* This function returns the number of output coordinate values
* generated per point by a Mapping (i.e. the number of dimensions
* of the space in which output points reside).
* Parameters:
* this
* Pointer to the Mapping.
* Returned Value:
* Number of coordinate values generated.
* 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 invert; /* Invert attribute value */
int result; /* Result value to return */
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Determine if the Mapping has been inverted. */
invert = astGetInvert( this );
/* Obtain the Nout value. */
if ( astOK ) result = invert ? this->nin : this->nout;
/* Return the result. */
return result;
}
static int GetTranForward( AstMapping *this, int *status ) {
/*
*+
* Name:
* astGetTranForward
* Purpose:
* Determine if a Mapping defines a forward coordinate transformation.
* Type:
* Protected virtual function.
* Synopsis:
* #include "mapping.h"
* int astGetTranForward( AstMapping *this )
* Class Membership:
* Mapping method.
* Description:
* This function returns a value indicating whether a Mapping is
* able to perform a coordinate transformation in the "forward"
* direction.
* Parameters:
* this
* Pointer to the Mapping.
* Returned Value:
* Zero if the forward coordinate transformation is not defined, or
* 1 if it is.
* 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 invert; /* Mapping inverted? */
int result; /* Result value to return */
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Determine if the Mapping has been inverted. */
invert = astGetInvert( this );
/* If OK, obtain the result. */
if ( astOK ) result = invert ? this->tran_inverse : this->tran_forward;
/* Return the result. */
return result;
}
static int GetTranInverse( AstMapping *this, int *status ) {
/*
*+
* Name:
* astGetTranInverse
* Purpose:
* Determine if a Mapping defines an inverse coordinate transformation.
* Type:
* Protected virtual function.
* Synopsis:
* #include "mapping.h"
* int astGetTranInverse( AstMapping *this )
* Class Membership:
* Mapping method.
* Description:
* This function returns a value indicating whether a Mapping is
* able to perform a coordinate transformation in the "inverse"
* direction.
* Parameters:
* this
* Pointer to the Mapping.
* Returned Value:
* Zero if the inverse coordinate transformation is not defined, or
* 1 if it is.
* 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 invert; /* Mapping inverted? */
int result; /* Result value to return */
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Determine if the Mapping has been inverted. */
invert = astGetInvert( this );
/* If OK, obtain the result. */
if ( astOK ) result = invert ? this->tran_forward : this->tran_inverse;
/* Return the result. */
return result;
}
static void GlobalBounds( MapData *mapdata, double *lbnd, double *ubnd,
double xl[], double xu[], int *status ) {
/*
* Name:
* GlobalBounds
* Purpose:
* Estimate global coordinate bounds for a Mapping.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* void GlobalBounds( MapData *mapdata, double *lbnd, double *ubnd,
* double xl[], double xu[], int *status );
* Class Membership:
* Mapping member function.
* Description:
* This function estimates the global lower and upper bounds of a
* Mapping function within a constrained region of its input
* coordinate space. It uses a robust global optimisation algorithm
* based on the selection of pseudo-random starting positions,
* followed by the location of local minima and maxima using the
* downhill (or uphill) simplex method. The algorithm will cope
* with the case where there are several competing minima (or
* maxima) with nearly equal values. It attempts to locate the
* global bounds to full machine precision when possible.
* Parameters:
* mapdata
* Pointer to a MapData structure describing the Mapping
* function, its coordinate constraints, etc.
* lbnd
* Pointer to a double. On entry, this should contain a
* previously-obtained upper limit on the global lower bound, or
* AST__BAD if no such limit is available. On exit, it will be
* updated with a new estimate of the global lower bound, if a
* better one has been found.
* ubnd
* Pointer to a double. On entry, this should contain a
* previously-obtained lower limit on the global upper bound, or
* AST__BAD if no such limit is available. On exit, it will be
* updated with a new estimate of the global upper bound, if a
* better one has been found.
* xl
* Pointer to an array of double, with one element for each
* input coordinate. On entry, if *lbnd is not equal to AST__OK,
* this should contain the input coordinates of a point at which
* the Mapping function takes the value *lbnd. On exit, this
* function returns the position of a (not necessarily unique)
* input point at which the Mapping function takes the value of
* the new global lower bound. This array is not altered if an
* improved estimate of the global lower bound cannot be found.
* xu
* Pointer to an array of double, with one element for each
* input coordinate. On entry, if *ubnd is not equal to AST__OK,
* this should contain the input coordinates of a point at which
* the Mapping function takes the value *ubnd. On exit, this
* function returns the position of a (not necessarily unique)
* input point at which the Mapping function takes the value of
* the new global upper bound. This array is not altered if an
* improved estimate of the global upper bound cannot be found.
* status
* Pointer to the inherited status variable.
* Notes:
* - The efficiency of this function will usually be improved if
* previously-obtained estimates of the extrema and their locations
* are provided.
* - The values returned via "lbnd", "ubnd", "xl" and "xu" will be
* set to the value AST__BAD if this function should fail for any
* reason. Their initial values on entry will not be altered if the
* function is invoked with the global error status set.
*/
/* Local Constants: */
const double default_acc = 3.0e-5; /* Default convergence accuracy */
const int maxiter = 10000; /* Maximum number of iterations */
const int minsame = 5; /* Minimum no. consistent extrema required */
const int nbatch = 32; /* No. function samples obtained per batch */
/* Local Variables: */
AstPointSet *pset_in; /* Input PointSet for batch transformation */
AstPointSet *pset_out; /* Output PointSet for batch transformation */
double **ptr_in; /* Pointer to batch input coordinates */
double **ptr_out; /* Pointer to batch output coordinates */
double *active_hi; /* Estimated upper limits of active region */
double *active_lo; /* Estimated lower limits of active region */
double *sample_hi; /* Upper limits of sampled region */
double *sample_lo; /* Lower limits of sampled region */
double *sample_width; /* Nominal widths of sampled region */
double *x; /* Pointer to array of coordinates */
double acc; /* Convergence accuracy for finding maximum */
double active_width; /* Estimated width of active region */
double new_max; /* Value of new local maximum */
double new_min; /* Value of new local minimum */
double oversize; /* Over-size factor for sampled region */
double random; /* Pseudo-random number */
int bad; /* Transformed position is bad? */
int batch; /* Next element to use in position batch */
int coord; /* Loop counter for coordinates */
int done_max; /* Satisfactory global maximum found? */
int done_min; /* Satisfactory global minimum found? */
int iter; /* Loop counter for iterations */
int ncoord; /* Number of coordinates in search space */
int nmax; /* Number of local maxima found */
int nmin; /* Number of local minima found */
int nsame_max; /* Number of equivalent local maxima found */
int nsame_min; /* Number of equivalent local minima found */
long int seed = 1776655449; /* Arbitrary pseudo-random number seed */
/* Check the global error status */
if ( !astOK ) return;
/* Initialise. */
done_max = 0;
done_min = 0;
nmax = 0;
nmin = 0;
nsame_max = 0;
nsame_min = 0;
pset_in = NULL;
pset_out = NULL;
ptr_in = NULL;
ptr_out = NULL;
oversize = 0;
bad = 0;
/* Extract the number of input coordinates for the Mapping function
and allocate workspace. */
ncoord = mapdata->nin;
active_hi = astMalloc( sizeof( double ) * (size_t) ncoord );
active_lo = astMalloc( sizeof( double ) * (size_t) ncoord );
sample_hi = astMalloc( sizeof( double ) * (size_t) ncoord );
sample_lo = astMalloc( sizeof( double ) * (size_t) ncoord );
sample_width = astMalloc( sizeof( double ) * (size_t) ncoord );
x = astMalloc( sizeof( double ) * (size_t) ncoord );
if ( astOK ) {
/* Calculate the factor by which the size of the region we sample will
exceed the size of the Mapping function's active region (the region
where the transformed coordinates are non-bad) in each
dimension. This is chosen so that the volume ratio will be 2. */
oversize = pow( 2.0, 1.0 / (double) ncoord );
/* Initialise the limits of the active region to unknown. */
for ( coord = 0; coord < ncoord; coord++ ) {
active_lo[ coord ] = DBL_MAX;;
active_hi[ coord ] = -DBL_MAX;
/* Initialise the nominal widths of the sampled region to be the
actual widths of the search region times the over-size factor. */
sample_width[ coord ] = ( mapdata->ubnd[ coord ] -
mapdata->lbnd[ coord ] ) * oversize;
/* Initialise the sampled region to match the search region. */
sample_lo[ coord ] = mapdata->lbnd[ coord ];
sample_hi[ coord ] = mapdata->ubnd[ coord ];
}
/* Set up position buffer. */
/* ======================= */
/* Create two PointSets to act as buffers to hold a complete batch of
input and output coordinates. Obtain pointers to their coordinate
arrays. */
pset_in = astPointSet( nbatch, ncoord, "", status );
pset_out = astPointSet( nbatch, mapdata->nout, "", status );
ptr_in = astGetPoints( pset_in );
ptr_out = astGetPoints( pset_out );
/* Initialise the next element to be used in the position buffer to
indicate that the buffer is initially empty. */
batch = nbatch;
}
/* Define a macro to fill the position buffer with a set of
pseudo-random positions and to transform them. */
#define FILL_POSITION_BUFFER {\
\
/* We first generate a suitable volume over which to distribute the\
batch of pseudo-random positions. Initially, this will be the\
entire search volume, but if we find that the only non-bad\
transformed coordinates we obtain are restricted to a small\
sub-region of this input volume, then we reduce the sampled volume\
so as to concentrate more on the active region. */\
\
/* Loop through each input coordinate, checking that at least one\
non-bad transformed point has been obtained. If not, we do not\
adjust the sampled volume, as we do not yet know where the active\
region lies. */\
for ( coord = 0; coord < ncoord; coord++ ) {\
if ( active_hi[ coord ] >= active_lo[ coord ] ) {\
\
/* Estimate the width of the active region from the range of input\
coordinates that have so far produced non-bad transformed\
coordinates. */\
active_width = active_hi[ coord ] - active_lo[ coord ];\
\
/* If the current width of the sampled volume exceeds this estimate by\
more than the required factor, then reduce the width of the sampled\
volume. The rate of reduction is set so that the volume of the\
sampled region can halve with every fourth batch of positions. */\
if ( ( active_width * oversize ) < sample_width[ coord ] ) {\
sample_width[ coord ] /= pow( oversize, 0.25 );\
\
/* If the width of the sampled volume does not exceed that of the\
known active region by the required factor, then adjust it so that\
it does. Note that we must continue to sample some points outside\
the known active region in case we have missed any (in which case\
the sampled region will expand again to include them). */\
} else if ( ( active_width * oversize ) > sample_width[ coord ] ) {\
sample_width[ coord ] = active_width * oversize;\
}\
\
/* Calculate the lower and upper bounds on the sampled volume, using\
the new width calculated above and centring it on the active\
region, as currently known. */\
sample_lo[ coord ] = ( active_lo[ coord ] + active_hi[ coord ] -\
sample_width[ coord ] ) * 0.5;\
sample_hi[ coord ] = ( active_lo[ coord ] + active_hi[ coord ] +\
sample_width[ coord ] ) * 0.5;\
\
/* Ensure that the sampled region does not extend beyond the original\
search region. */\
if ( sample_lo[ coord ] < mapdata->lbnd[ coord ] ) {\
sample_lo[ coord ] = mapdata->lbnd[ coord ];\
}\
if ( sample_hi[ coord ] > mapdata->ubnd[ coord ] ) {\
sample_hi[ coord ] = mapdata->ubnd[ coord ];\
}\
}\
}\
\
/* Having determined the size of the sampled volume, create a batch of\
pseudo-random positions uniformly distributed within it. */\
for ( batch = 0; batch < nbatch; batch++ ) {\
for ( coord = 0; coord < ncoord; coord++ ) {\
random = Random( &seed, status );\
ptr_in[ coord ][ batch ] = sample_lo[ coord ] * random +\
sample_hi[ coord ] * ( 1.0 - random );\
}\
}\
\
/* Transform these positions. We process them in a single batch in\
order to minimise the overheads in doing this. */\
(void) astTransform( mapdata->mapping, pset_in, mapdata->forward,\
pset_out );\
\
/* Indicate that the position buffer is now full. */\
batch = 0;\
}
/* Fill the position buffer using the above macro. (Note that because
we do not yet have an estimate of the size of the active region,
this does not change the sampled region size from our earlier
initialised values. */
FILL_POSITION_BUFFER;
/* Iterate. */
/* ======== */
/* Loop to perform up to "maxiter" iterations to estimate the global
minimum and maximum. */
for ( iter = 0; astOK && ( iter < maxiter ); iter++ ) {
/* Determine the search accuracy. */
/* ============================== */
/* Decide the accuracy to which local extrema should be found. The
intention here is to optimise performance, especially where one
extremum lies near zero and so could potentially be found to
unnecessarily high precision. If we make a mis-assumption (the code
below is not fool-proof), we will slow things down for this
iteration, but the error will be corrected in future iterations
once better estimates are available. */
/* If we have no current estimate of either global extremum, we assume
the values we eventually obtain will be of order unity and required
to the default accuracy. */
acc = default_acc;
/* If we already have an estimate of both global extrema, we set the
accuracy level so that the difference between them will be known to
the default accuracy. */
if ( ( *lbnd != AST__BAD ) && ( *ubnd != AST__BAD ) ) {
acc = fabs( *ubnd - *lbnd ) * default_acc;
/* If we have an estimate of only one global extremum, we assume that
the difference between the two global extrema will eventually be of
the same order as the estimate we currently have, so long as this
is not less than unity. */
} else if ( *lbnd != AST__BAD ) {
if ( fabs( *lbnd ) > 1.0 ) acc = fabs( *lbnd) * default_acc;
} else if ( *ubnd != AST__BAD ) {
if ( fabs( *ubnd ) > 1.0 ) acc = fabs( *ubnd) * default_acc;
}
/* Search for a new local minimum. */
/* =============================== */
/* If we are still searching for the global minimum, then obtain a set
of starting coordinates from which to find a new local minimum. */
if ( !done_min ) {
/* On the first iteration, start searching at the position where the
best estimate of the global minimum (if any) has previously been
found. We know that this produces non-bad transformed
coordinates. */
bad = 0;
if ( !iter && ( *lbnd != AST__BAD ) ) {
for ( coord = 0; coord < ncoord; coord++ ) {
x[ coord ] = xl[ coord ];
}
/* Otherwise, if no estimate of the global minimum is available, then
start searching at the position where the best estimate of the
global maximum (if any) has been found. This may be a long way from
a local minimum, but at least it will yield a non-bad value for the
Mapping function, so some sort of estimate of the global minimum
will be obtained. This is important in cases where finding the
active region of the function is the main problem. Note that this
condition can only occur once, since the global minimum will have
an estimate on the next iteration. */
} else if ( ( *lbnd == AST__BAD ) && ( *ubnd != AST__BAD ) ) {
for ( coord = 0; coord < ncoord; coord++ ) {
x[ coord ] = xu[ coord ];
}
/* Having exhausted the above possibilities, we use pseudo-random
starting positions which are uniformly distributed throughout the
search volume. First check to see if the buffer containing such
positions is empty and refill it if necessary. */
} else {
if ( batch >= nbatch ) FILL_POSITION_BUFFER;
/* Test the next available set of output (transformed) coordinates in
the position buffer to see if they are bad. */
if ( astOK ) {
for ( coord = 0; coord < mapdata->nout; coord++ ) {
bad = ( ptr_out[ coord ][ batch ] == AST__BAD );
if ( bad ) break;
}
/* If not, we have a good starting position for finding a local
minimum, so extract the corresponding input coordinates. */
if ( !bad ) {
for ( coord = 0; coord < ncoord; coord++ ) {
x[ coord ] = ptr_in[ coord ][ batch ];
}
}
/* Increment the position buffer location. */
batch++;
}
}
/* If we do not have a good starting position, we can't do anything
more on this iteration. A new position will be obtained and tested
on the next iteration and this (we hope) will eventually identify a
suitable starting point. */
if ( astOK && !bad ) {
/* Form estimates of the lower and upper limits of the active region
from the starting positions used. */
for ( coord = 0; coord < ncoord; coord++ ) {
if ( x[ coord ] < active_lo[ coord ] ) {
active_lo[ coord ] = x[ coord ];
}
if ( x[ coord ] > active_hi[ coord ] ) {
active_hi[ coord ] = x[ coord ];
}
}
/* Indicate that the Mapping function should be negated (because we
want a local minimum) and then search for a local maximum in this
negated function. If the result is non-bad (as it should always be,
barring an error), then negate it to obtain the value of the local
minimum found. */
mapdata->negate = 1;
new_min = LocalMaximum( mapdata, acc, 0.01, x, status );
if ( new_min != AST__BAD ) {
new_min = -new_min;
/* Update the estimates of the lower and upper bounds of the active
region to take account of where the minimum was found. */
for ( coord = 0; coord < ncoord; coord++ ) {
if ( x[ coord ] < active_lo[ coord ] ) {
active_lo[ coord ] = x[ coord ];
}
if ( x[ coord ] > active_hi[ coord ] ) {
active_hi[ coord ] = x[ coord ];
}
}
/* Count the number of times we successfully locate a local minimum
(ignoring the fact they might all be the same one). */
nmin++;
/* Update the global minimum. */
/* ========================== */
/* If this is the first estimate of the global minimum, then set to
one the count of the number of consecutive iterations where this
estimate remains unchanged. Store the minimum value and its
position. */
if ( *lbnd == AST__BAD ) {
nsame_min = 1;
*lbnd = new_min;
for ( coord = 0; coord < ncoord; coord++ ) {
xl[ coord ] = x[ coord ];
}
/* Otherwise, test if this local minimum is lower than the previous
estimate of the global minimum. If so, then reset the count of
unchanged estimates of the global mimimum to one if the difference
exceeds the accuracy with which the minimum was found (i.e. if we
have found a significantly different minimum). Otherwise, just
increment this count (because we have found the same minimum but by
chance with slightly improved accuracy). Store the new minimum and
its position. */
} else if ( new_min < *lbnd ) {
nsame_min = ( ( *lbnd - new_min ) > acc ) ? 1 :
nsame_min + 1;
*lbnd = new_min;
for ( coord = 0; coord < ncoord; coord++ ) {
xl[ coord ] = x[ coord ];
}
/* If the latest local minimum is no improvement on previous estimates
of the global minimum, then increment the count of unchanged
estimates of the global mimimum, but do not save the new one. */
} else {
nsame_min++;
}
/* Determine if a satisfactory estimate of the global minimum has been
obtained. It has if the number of consecutive local minima which
have not significantly improved the estimate is at least equal to
"minsame", and at least 30% of the total number of local minima
found. */
if ( ( nsame_min >= minsame ) &&
( nsame_min >= (int) ( 0.3f * (float) nmin + 0.5f ) ) ) {
done_min = 1;
}
}
}
}
/* Search for a new local maximum. */
/* =============================== */
/* Now repeat all of the above to find a new local maximum which
estimates the global maximum. */
if ( !done_max ) {
/* Choose a suitable starting position, based on one already available
if appropriate. */
if ( !iter && ( *ubnd != AST__BAD ) ) {
for ( coord = 0; coord < ncoord; coord++ ) {
x[ coord ] = xu[ coord ];
}
} else if ( ( *ubnd == AST__BAD ) && ( *lbnd != AST__BAD ) ) {
for ( coord = 0; coord < ncoord; coord++ ) {
x[ coord ] = xl[ coord ];
}
/* Otherwise use a pseudo-random position, refilling the position
buffer if necessary. Check if the transformed coordinates are
bad. */
} else {
if ( batch >= nbatch ) FILL_POSITION_BUFFER;
if ( astOK ) {
for ( coord = 0; coord < mapdata->nout; coord++ ) {
bad = ( ptr_out[ coord ][ batch ] == AST__BAD );
if ( bad ) break;
}
if ( !bad ) {
for ( coord = 0; coord < ncoord; coord++ ) {
x[ coord ] = ptr_in[ coord ][ batch ];
}
}
batch++;
}
}
/* If the coordinates are OK, update the active region limits. */
if ( astOK && !bad ) {
for ( coord = 0; coord < ncoord; coord++ ) {
if ( x[ coord ] < active_lo[ coord ] ) {
active_lo[ coord ] = x[ coord ];
}
if ( x[ coord ] > active_hi[ coord ] ) {
active_hi[ coord ] = x[ coord ];
}
}
/* Find a local maximum in the Mapping function. */
mapdata->negate = 0;
new_max = LocalMaximum( mapdata, acc, 0.01, x, status );
if ( new_max != AST__BAD ) {
/* Use the result to further update the active region limits. */
for ( coord = 0; coord < ncoord; coord++ ) {
if ( x[ coord ] < active_lo[ coord ] ) {
active_lo[ coord ] = x[ coord ];
}
if ( x[ coord ] > active_hi[ coord ] ) {
active_hi[ coord ] = x[ coord ];
}
}
/* Count the number of local maxima found. */
nmax++;
/* Update the estimate of the global maximum. */
if ( *ubnd == AST__BAD ) {
nsame_max = 1;
*ubnd = new_max;
for ( coord = 0; coord < ncoord; coord++ ) {
xu[ coord ] = x[ coord ];
}
} else if ( new_max > *ubnd ) {
nsame_max = ( ( new_max - *ubnd ) > acc ) ? 1 :
nsame_max + 1;
*ubnd = new_max;
for ( coord = 0; coord < ncoord; coord++ ) {
xu[ coord ] = x[ coord ];
}
} else {
nsame_max++;
}
/* Test for a satisfactory global maximum estimate. */
if ( ( nsame_max >= minsame ) &&
( nsame_max >= (int) ( 0.3f * (float) nmax + 0.5 ) ) ) {
done_max = 1;
}
}
}
}
/* Quit iterating once both the global minimum and the global maximum
have been found. */
if ( done_min && done_max ) break;
}
/* Free workspace. */
active_hi = astFree( active_hi );
active_lo = astFree( active_lo );
sample_hi = astFree( sample_hi );
sample_lo = astFree( sample_lo );
sample_width = astFree( sample_width );
x = astFree( x );
/* Annul temporary PointSets. */
pset_in = astAnnul( pset_in );
pset_out = astAnnul( pset_out );
/* If the global minimum has been found, attempt to polish the result
to machine precision by requesting that it be found with an
accuracy tolerance of zero (subject to the maximum number of
iterations that LocalMaximum will perform,). */
if ( astOK ) {
if ( *lbnd != AST__BAD ) {
mapdata->negate = 1;
*lbnd = LocalMaximum( mapdata, 0.0, sqrt( DBL_EPSILON ), xl, status );
if ( *lbnd != AST__BAD ) *lbnd = - *lbnd;
}
/* Similarly polish the estimate of the global maximum. */
if ( *ubnd != AST__BAD ) {
mapdata->negate = 0;
*ubnd = LocalMaximum( mapdata, 0.0, sqrt( DBL_EPSILON ), xu, status );
}
/* If either extremum could not be found, then report an error. */
if ( ( *lbnd == AST__BAD ) || ( *ubnd == AST__BAD ) ) {
astError( AST__MBBNF, "astMapBox(%s): No valid output coordinates "
"(after %d test points).", status, astGetClass( mapdata->mapping ),
2 * maxiter );
}
/* If an error occurred, then return bad extremum values and
coordinates. */
if ( !astOK ) {
*lbnd = AST__BAD;
*ubnd = AST__BAD;
for ( coord = 0; coord < ncoord; coord++ ) {
xl[ coord ] = AST__BAD;
xu[ coord ] = AST__BAD;
}
}
}
/* Undefine macros local to this function. */
#undef FILL_POSITION_BUFFER
}
void astInitMappingVtab_( AstMappingVtab *vtab, const char *name, int *status ) {
/*
*+
* Name:
* astInitMappingVtab
* Purpose:
* Initialise a virtual function table for a Mapping.
* Type:
* Protected function.
* Synopsis:
* #include "mapping.h"
* void astInitMappingVtab( AstMappingVtab *vtab, const char *name )
* Class Membership:
* Mapping vtab initialiser.
* Description:
* This function initialises the component of a virtual function
* table which is used by the Mapping 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 */
/* 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. */
astInitObjectVtab( (AstObjectVtab *) vtab, name );
/* Store a unique "magic" value in the virtual function table. This
will be used (by astIsAMapping) 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 = &(((AstObjectVtab *) vtab)->id);
/* Initialise member function pointers. */
/* ------------------------------------ */
/* Store pointers to the member functions (implemented here) that provide
virtual methods for this class. */
#define VTAB_GENERIC(X) \
vtab->Resample##X = Resample##X;
VTAB_GENERIC(B)
VTAB_GENERIC(D)
VTAB_GENERIC(F)
VTAB_GENERIC(I)
VTAB_GENERIC(K)
VTAB_GENERIC(L)
VTAB_GENERIC(S)
VTAB_GENERIC(UB)
VTAB_GENERIC(UI)
VTAB_GENERIC(UK)
VTAB_GENERIC(UL)
VTAB_GENERIC(US)
#if HAVE_LONG_DOUBLE /* Not normally implemented */
VTAB_GENERIC(LD)
#endif
#undef VTAB_GENERIC
#define VTAB_GENERIC(X) \
vtab->Rebin##X = Rebin##X; \
vtab->RebinSeq##X = RebinSeq##X;
VTAB_GENERIC(D)
VTAB_GENERIC(F)
VTAB_GENERIC(I)
VTAB_GENERIC(B)
VTAB_GENERIC(UB)
#if HAVE_LONG_DOUBLE /* Not normally implemented */
VTAB_GENERIC(LD)
#endif
#undef VTAB_GENERIC
vtab->ClearInvert = ClearInvert;
vtab->ClearReport = ClearReport;
vtab->Decompose = Decompose;
vtab->DoNotSimplify = DoNotSimplify;
vtab->GetInvert = GetInvert;
vtab->GetIsLinear = GetIsLinear;
vtab->GetIsSimple = GetIsSimple;
vtab->GetNin = GetNin;
vtab->GetNout = GetNout;
vtab->GetReport = GetReport;
vtab->GetTranForward = GetTranForward;
vtab->GetTranInverse = GetTranInverse;
vtab->Invert = Invert;
vtab->LinearApprox = LinearApprox;
vtab->MapBox = MapBox;
vtab->MapList = MapList;
vtab->MapMerge = MapMerge;
vtab->MapSplit = MapSplit;
vtab->QuadApprox = QuadApprox;
vtab->Rate = Rate;
vtab->ReportPoints = ReportPoints;
vtab->RemoveRegions = RemoveRegions;
vtab->SetInvert = SetInvert;
vtab->SetReport = SetReport;
vtab->Simplify = Simplify;
vtab->TestInvert = TestInvert;
vtab->TestReport = TestReport;
vtab->Tran1 = Tran1;
vtab->Tran2 = Tran2;
vtab->TranGrid = TranGrid;
vtab->TranN = TranN;
vtab->TranP = TranP;
vtab->Transform = Transform;
/* Save the inherited pointers to methods that will be extended, and
replace them with pointers to the new member functions. */
object = (AstObjectVtab *) vtab;
parent_clearattrib = object->ClearAttrib;
object->ClearAttrib = ClearAttrib;
parent_getattrib = object->GetAttrib;
object->GetAttrib = GetAttrib;
parent_setattrib = object->SetAttrib;
object->SetAttrib = SetAttrib;
parent_testattrib = object->TestAttrib;
object->TestAttrib = TestAttrib;
parent_equal = object->Equal;
object->Equal = Equal;
/* Declare the destructor, copy constructor and dump function. */
astSetDelete( vtab, Delete );
astSetCopy( vtab, Copy );
astSetDump( vtab, Dump, "Mapping", "Mapping between coordinate systems" );
/* 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) );
}
}
/*
* Name:
* InterpolateKernel1
* Purpose:
* Resample a data grid, using a 1-d interpolation kernel.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* int InterpolateKernel1( AstMapping *this, int ndim_in,
* const int *lbnd_in, const int *ubnd_in,
* const *in, const *in_var,
* int npoint, const int *offset,
* const double *const *coords,
* void (* kernel)( double, const double [], int,
* double *, int * ),
* void (* fkernel)( double, const double [], int,
* double * ),
* int neighb, const double *params, int flags,
* badval,
* *out, *out_var )
* Class Membership:
* Mapping member function.
* Description:
* This is a set of functions which resample a rectangular input
* grid of data (and, optionally, associated statistical variance
* values) so as to place them into a new output grid. Each output
* grid point may be mapped on to a position in the input grid in
* an arbitrary way. The input and output grids may have any number
* of dimensions, not necessarily equal.
*
* Where the positions given do not correspond with a pixel centre
* in the input grid, interpolation is performed using a weighted
* sum of the surrounding pixel values. The weights are determined
* by a separable kernel which is the product of a 1-dimensional
* kernel function evaluated along each input dimension. A pointer
* should be supplied to the 1-dimensional kernel function to be
* used.
* Parameters:
* this
* Pointer to the Mapping being used in the resampling operation
* (this is only used for constructing error messages).
* ndim_in
* The number of dimensions in the input grid. This should be at
* least one.
* lbnd_in
* Pointer to an array of integers, with "ndim_in" elements.
* This should give the coordinates of the centre of the first
* pixel in the input grid along each dimension.
* ubnd_in
* Pointer to an array of integers, with "ndim_in" elements.
* This should give the coordinates of the centre of the last
* pixel in the input grid along each dimension.
*
* Note that "lbnd_in" and "ubnd_in" together define the shape
* and size of the input grid, its extent along a particular
* (i'th) dimension being ubnd_in[i]-lbnd_in[i]+1 (assuming "i"
* is zero-based). They also define the input grid's coordinate
* system, with each pixel being of unit extent along each
* dimension with integral coordinate values at its centre.
* in
* Pointer to the array of data to be resampled (with an element
* for each pixel in the input grid). The numerical type of
* these data should match the function used, as given by the
* suffix on the function name. The storage order should be such
* that the index of the first grid dimension varies most
* rapidly and that of the final dimension least rapidly
* (i.e. Fortran array storage order).
* in_var
* An optional pointer to a second array of positive numerical
* values (with the same size and type as the "in" array), which
* represent estimates of the statistical variance associated
* with each element of the "in" array. If this second array is
* given (along with the corresponding "out_var" array), then
* estimates of the variance of the resampled data will also be
* returned.
*
* If no variance estimates are required, a NULL pointer should
* be given.
* npoint
* The number of points at which the input grid is to be
* resampled.
* offset
* Pointer to an array of integers with "npoint" elements. For
* each output point, this array should contain the zero-based
* offset in the output array(s) (i.e. the "out" and,
* optionally, the "out_var" arrays) at which the resampled
* output value(s) should be stored.
* coords
* An array of pointers to double, with "ndim_in"
* elements. Element "coords[coord]" should point at the first
* element of an array of double (with "npoint" elements) which
* contains the values of coordinate number "coord" for each
* interpolation point. The value of coordinate number "coord"
* for interpolation point number "point" is therefore given by
* "coords[coord][point]" (assuming both indices to be
* zero-based). If any point has a coordinate value of AST__BAD
* associated with it, then the corresponding output data (and
* variance) will be set to the value given by "badval" (unles the
* AST__NOBAD flag is specified).
* kernel
* Pointer to the 1-dimensional kernel function to be used.
* fkernel
* Pointer to the 1-dimensional kernel function to be used with no
* trailing status argument. This is only used if "kernel" is NULL.
* neighb
* The number of neighbouring pixels in each dimension (on each
* side of the interpolation position) which are to contribute
* to the interpolated value. This value should be at least 1.
* params
* Pointer to an optional array of parameter values to be passed
* to the interpolation kernel function. If no parameters are
* required by this function, then a NULL pointer may be
* supplied.
* flags
* The bitwise OR of a set of flag values which provide
* additional control over the resampling operation.
* badval
* If the AST__USEBAD flag is set in the "flags" value (above),
* this parameter specifies the value which is used to identify
* bad data and/or variance values in the input array(s). Its
* numerical type must match that of the "in" (and "in_var")
* arrays. Unles the AST__NOBAD flag is specified in "flags", the
* same value will also be used to flag any output array elements
* for which resampled values could not be obtained. The output
* arrays(s) may be flagged with this value whether or not the
* AST__USEBAD flag is set (the function return value indicates
* whether any such values have been produced).
* out
* Pointer to an array with the same data type as the "in"
* array, into which the resampled data will be returned. Note
* that details of how the output grid maps on to this array
* (e.g. the storage order, number of dimensions, etc.) is
* arbitrary and is specified entirely by means of the "offset"
* array. The "out" array should therefore contain sufficient
* elements to accommodate the "offset" values supplied. There
* is no requirement that all elements of the "out" array should
* be assigned values, and any which are not addressed by the
* contents of the "offset" array will be left unchanged.
* out_var
* An optional pointer to an array with the same data type and
* size as the "out" array, into which variance estimates for
* the resampled values may be returned. This array will only be
* used if the "in_var" array has been given. It is addressed in
* exactly the same way (via the "offset" array) as the "out"
* array. The values returned are estimates of the statistical
* variance of the corresponding values in the "out" array, on
* the assumption that all errors in input grid values (in the
* "in" array) are statistically independent and that their
* variance estimates (in the "in_var" array) may simply be
* summed (with appropriate weighting factors).
*
* If no output variance estimates are required, a NULL pointer
* should be given.
* Returned Value:
* The number of output grid points for which no valid output value
* could be obtained.
* Notes:
* - There is a separate function for each numerical type of
* gridded data, distinguished by replacing the in the function
* name by the appropriate 1- or 2-character suffix.
* - A value of zero will be returned if any of these functions is
* invoked with the global error status set, or if it should fail
* for any reason.
*/
/* Define macros to implement the function for a specific data
type. */
#define MAKE_INTERPOLATE_KERNEL1(X,Xtype,Xfloating,Xfloattype,Xsigned) \
static int InterpolateKernel1##X( AstMapping *this, int ndim_in, \
const int *lbnd_in, const int *ubnd_in, \
const Xtype *in, const Xtype *in_var, \
int npoint, const int *offset, \
const double *const *coords, \
void (* kernel)( double, const double [], \
int, double *, int * ), \
void (* fkernel)( double, const double [], \
int, double * ), \
int neighb, const double *params, \
int flags, Xtype badval, \
Xtype *out, Xtype *out_var, int *status ) { \
\
/* Local Variables: */ \
astDECLARE_GLOBALS /* Thread-specific data */ \
Xfloattype hi_lim; /* Upper limit on output values */ \
Xfloattype lo_lim; /* Lower limit on output values */ \
Xfloattype sum; /* Weighted sum of pixel data values */ \
Xfloattype sum_var; /* Weighted sum of pixel variance values */ \
Xfloattype val; /* Data value to be assigned to output */ \
Xfloattype val_var; /* Variance to be assigned to output */ \
Xfloattype wtsum; /* Sum of weight values */ \
Xfloattype wtsum_sq; /* Square of sum of weights */ \
Xtype var; /* Variance value */ \
double **wtptr; /* Pointer to array of weight pointers */ \
double **wtptr_last; /* Array of highest weight pointer values */ \
double *kval; /* Pointer to array of kernel values */ \
double *wtprod; /* Accumulated weight value array pointer */ \
double *xn_max; /* Pointer to upper limits array (n-d) */ \
double *xn_min; /* Pointer to lower limits array (n-d) */ \
double pixwt; /* Weight to apply to individual pixel */ \
double wt_y; /* Value of y-dependent pixel weight */ \
double x; /* x coordinate value */ \
double xmax; /* x upper limit */ \
double xmin; /* x lower limit */ \
double xn; /* Coordinate value (n-d) */ \
double y; /* y coordinate value */ \
double ymax; /* y upper limit */ \
double ymin; /* y lower limit */ \
int *hi; /* Pointer to array of upper indices */ \
int *lo; /* Pointer to array of lower indices */ \
int *stride; /* Pointer to array of dimension strides */ \
int bad; /* Output pixel bad? */ \
int bad_var; /* Output variance bad? */ \
int done; /* All pixel indices done? */ \
int hi_x; /* Upper pixel index (x dimension) */ \
int hi_y; /* Upper pixel index (y dimension) */ \
int idim; /* Loop counter for dimensions */ \
int ii; /* Loop counter for dimensions */ \
int ix; /* Pixel index in input grid x dimension */ \
int ixn; /* Pixel index in input grid (n-d) */ \
int iy; /* Pixel index in input grid y dimension */ \
int kerror; /* Error signalled by kernel function? */ \
int lo_x; /* Lower pixel index (x dimension) */ \
int lo_y; /* Lower pixel index (y dimension) */ \
int nobad; /* Was the AST__NOBAD flag set? */ \
int off1; /* Input pixel offset due to y index */ \
int off_in; /* Offset to input pixel */ \
int off_out; /* Offset to output pixel */ \
int pixel; /* Offset to input pixel containing point */ \
int point; /* Loop counter for output points */ \
int result; /* Result value to return */ \
int s; /* Temporary variable for strides */ \
int usebad; /* Use "bad" input pixel values? */ \
int usevar; /* Process variance array? */ \
int ystride; /* Stride along input grid y dimension */ \
\
/* Initialise. */ \
result = 0; \
\
/* Check the global error status. */ \
if ( !astOK ) return result; \
\
/* Get a pointer to a structure holding thread-specific global data values */ \
astGET_GLOBALS(this); \
\
/* Further initialisation. */ \
kerror = 0; \
sum_var = 0; \
val = 0; \
val_var = 0; \
wtsum = 0; \
bad = 0; \
bad_var = 0; \
sum = 0.0; \
\
/* Determine if we are processing bad pixels or variances. */ \
nobad = flags & AST__NOBAD; \
usebad = flags & AST__USEBAD; \
usevar = in_var && out_var; \
\
/* Set up limits for checking output values to ensure that they do not \
overflow the range of the data type being used. */ \
lo_lim = LO_##X; \
hi_lim = HI_##X; \
\
/* Handle the 1-dimensional case optimally. */ \
/* ---------------------------------------- */ \
if ( ndim_in == 1 ) { \
\
/* Calculate the coordinate limits of the input grid. */ \
xmin = (double) lbnd_in[ 0 ] - 0.5; \
xmax = (double) ubnd_in[ 0 ] + 0.5; \
\
/* Identify four cases, according to whether bad pixels and/or \
variances are being processed. In each case, loop through all the \
output points to (a) assemble the input data needed to form the \
interpolated value, and (b) calculate the result and assign it to \
the output arrays(s). In each case we assign constant values (0 or \
1) to the "Usebad" and "Usevar" flags so that code for handling bad \
pixels and variances can be eliminated when not required. */ \
if ( nobad ) { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \
} \
} \
} \
\
/* Four more cases as above, but this time with the AST__NOBAD flag \
un-set. */ \
} else { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \
} \
} \
} \
} \
\
/* Exit point on error in kernel function */ \
Kernel_Error_1d: ; \
\
/* Handle the 2-dimensional case optimally. */ \
/* ---------------------------------------- */ \
} else if ( ndim_in == 2 ) { \
\
/* Allocate workspace. */ \
kval = astMalloc( sizeof( double ) * (size_t) ( 2 * neighb ) ); \
if ( astOK ) { \
\
/* Calculate the stride along the y dimension of the input grid. */ \
ystride = ubnd_in[ 0 ] - lbnd_in[ 0 ] + 1; \
\
/* Calculate the coordinate limits of the input grid in each \
dimension. */ \
xmin = (double) lbnd_in[ 0 ] - 0.5; \
xmax = (double) ubnd_in[ 0 ] + 0.5; \
ymin = (double) lbnd_in[ 1 ] - 0.5; \
ymax = (double) ubnd_in[ 1 ] + 0.5; \
\
/* Identify four cases, according to whether bad pixels and/or \
variances are being processed. In each case, loop through all the \
output points to (a) assemble the input data needed to form the \
interpolated value, and (b) calculate the result and assign it to \
the output arrays(s). In each case we assign constant values (0 or \
1) to the "Usebad" and "Usevar" flags so that code for handling bad \
pixels and variances can be eliminated when not required. */ \
if ( nobad ) { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \
} \
} \
} \
\
/* Another four cases, as above, but this time without the AST__NOBAD \
flag. */ \
} else { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \
} \
} \
} \
} \
\
/* Exit point on error in kernel function */ \
Kernel_Error_2d: ; \
} \
\
/* Free the workspace. */ \
kval = astFree( kval ); \
\
/* Handle other numbers of dimensions. */ \
/* ----------------------------------- */ \
} else { \
\
/* Allocate workspace. */ \
hi = astMalloc( sizeof( int ) * (size_t) ndim_in ); \
lo = astMalloc( sizeof( int ) * (size_t) ndim_in ); \
stride = astMalloc( sizeof( int ) * (size_t) ndim_in ); \
xn_max = astMalloc( sizeof( double ) * (size_t) ndim_in ); \
xn_min = astMalloc( sizeof( double ) * (size_t) ndim_in ); \
kval = astMalloc( sizeof( double ) * (size_t) \
( 2 * neighb * ndim_in ) ); \
wtprod = astMalloc( sizeof( double ) * (size_t) ndim_in ); \
wtptr = astMalloc( sizeof( double * ) * (size_t) ndim_in ); \
wtptr_last = astMalloc( sizeof( double * ) * (size_t) ndim_in ); \
if ( astOK ) { \
\
/* Calculate the stride along each dimension of the input grid. */ \
for ( s = 1, idim = 0; idim < ndim_in; idim++ ) { \
stride[ idim ] = s; \
s *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \
\
/* Calculate the coordinate limits of the input grid in each \
dimension. */ \
xn_min[ idim ] = (double) lbnd_in[ idim ] - 0.5; \
xn_max[ idim ] = (double) ubnd_in[ idim ] + 0.5; \
} \
\
/* Identify four cases, according to whether bad pixels and/or \
variances are being processed. In each case, loop through all the \
output points to (a) assemble the input data needed to form the \
interpolated value, and (b) calculate the result and assign it to \
the output arrays(s). In each case we assign constant values (0 or \
1) to the "Usebad" and "Usevar" flags so that code for handling bad \
pixels and variances can be eliminated when not required. */ \
if( nobad ) { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \
} \
} \
} \
\
/* Another 4 cases as above, but this time with the AST__NOBAD flag \
un-set. */ \
} else { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \
} \
} \
} \
} \
\
/* Exit point on error in kernel function */ \
Kernel_Error_Nd: ;\
} \
\
/* Free the workspace. */ \
hi = astFree( hi ); \
lo = astFree( lo ); \
stride = astFree( stride ); \
xn_max = astFree( xn_max ); \
xn_min = astFree( xn_min ); \
kval = astFree( kval ); \
wtprod = astFree( wtprod ); \
wtptr = astFree( wtptr ); \
wtptr_last = astFree( wtptr_last ); \
} \
\
/* If an error occurred in the kernel function, then report a \
contextual error message. */ \
if ( kerror ) { \
astError( astStatus, "astResample"#X"(%s): Error signalled by " \
"user-supplied 1-d interpolation kernel.", status, \
astGetClass( unsimplified_mapping ) ); \
} \
\
/* If an error has occurred, clear the returned result. */ \
if ( !astOK ) result = 0; \
\
/* Return the result. */ \
return result; \
}
/* This subsidiary macro assembles the input data needed in
preparation for forming the interpolated value in the 1-dimensional
case. */
#define ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \
\
/* Obtain the x coordinate of the current point and test if it lies \
outside the input grid, or is bad. */ \
x = coords[ 0 ][ point ]; \
bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \
if ( !bad ) { \
\
/* If input bad pixels must be detected, then obtain the offset along \
the input grid x dimension of the input pixel which contains the \
current coordinate, and calculate this pixel's offset from the \
start of the input array. */ \
if ( Usebad ) { \
pixel = (int) floor( x + 0.5 ) - lbnd_in[ 0 ]; \
\
/* Test if the pixel is bad. */ \
bad = ( in[ pixel ] == badval ); \
} \
\
/* If OK, calculate the lowest and highest indices (in the x \
dimension) of the region of neighbouring pixels that will \
contribute to the interpolated result. Constrain these values to \
lie within the input grid. */ \
if ( !bad ) { \
ix = (int) floor( x ); \
lo_x = MaxI( ix - neighb + 1, lbnd_in[ 0 ], status ); \
hi_x = MinI( ix + neighb, ubnd_in[ 0 ], status ); \
\
/* Initialise sums for forming the interpolated result. */ \
sum = (Xfloattype) 0.0; \
wtsum = (Xfloattype) 0.0; \
if ( Usevar ) { \
sum_var = (Xfloattype) 0.0; \
bad_var = 0; \
} \
\
/* Loop to inspect all the contributing pixels, calculating the offset \
of each pixel from the start of the input array. */ \
off_in = lo_x - lbnd_in[ 0 ]; \
for ( ix = lo_x; ix <= hi_x; ix++, off_in++ ) { \
\
/* If necessary, test if the input pixel is bad. If not, calculate its \
weight by evaluating the kernel function. */ \
if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \
if( kernel ) { \
( *kernel )( (double) ix - x, params, flags, &pixwt, status ); \
} else { \
( *fkernel )( (double) ix - x, params, flags, &pixwt ); \
} \
\
/* Check for errors arising in the kernel function. */ \
if ( !astOK ) { \
kerror = 1; \
goto Kernel_Error_1d; \
} \
\
/* Form the weighted sums required for finding the interpolated \
value. */ \
sum += ( (Xfloattype) pixwt ) * ( (Xfloattype) in[ off_in ] ); \
wtsum += (Xfloattype) pixwt; \
\
/* If a variance estimate is required and it still seems possible to \
obtain one, then obtain the variance value associated with the \
current input pixel. */ \
if ( Usevar ) { \
if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \
var = in_var[ off_in ]; \
\
/* If necessary, test if this value is bad (if the data type is \
signed, also check that it is not negative). */ \
if ( Usebad ) bad_var = ( var == badval ); \
CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \
\
/* If any bad input variance value is obtained, we cannot generate a \
valid output variance estimate. Otherwise, form the sum needed to \
calculate this estimate. */ \
if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \
sum_var += ( (Xfloattype) ( pixwt * pixwt ) ) * \
( (Xfloattype) var ); \
} \
} \
} \
} \
} \
} \
}
/* This subsidiary macro assembles the input data needed in
preparation for forming the interpolated value in the 2-dimensional
case. */
#define ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \
\
/* Obtain the x coordinate of the current point and test if it lies \
outside the input grid, or is bad. */ \
x = coords[ 0 ][ point ]; \
bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \
if ( !bad ) { \
\
/* If not, then similarly obtain and test the y coordinate. */ \
y = coords[ 1 ][ point ]; \
bad = ( y < ymin ) || ( y >= ymax ) || ( y == AST__BAD ); \
if ( !bad ) { \
\
/* If input bad pixels must be detected, then obtain the offsets along \
each input grid dimension of the input pixel which contains the \
current coordinates, and calculate this pixel's offset from the \
start of the input array. */ \
if ( Usebad ) { \
ix = (int) floor( x + 0.5 ); \
iy = (int) floor( y + 0.5 ); \
pixel = ix - lbnd_in[ 0 ] + ystride * ( iy - lbnd_in[ 1 ] ); \
\
/* Test if the pixel is bad. */ \
bad = ( in[ pixel ] == badval ); \
} \
\
/* If OK, calculate the lowest and highest indices (in each dimension) \
of the region of neighbouring pixels that will contribute to the \
interpolated result. Constrain these values to lie within the input \
grid. */ \
if ( !bad ) { \
ix = (int) floor( x ); \
lo_x = MaxI( ix - neighb + 1, lbnd_in[ 0 ], status ); \
hi_x = MinI( ix + neighb, ubnd_in[ 0 ], status ); \
iy = (int) floor( y ); \
lo_y = MaxI( iy - neighb + 1, lbnd_in[ 1 ], status ); \
hi_y = MinI( iy + neighb, ubnd_in[ 1 ], status ); \
\
/* Loop to evaluate the kernel function along the x dimension, storing \
the resulting values. The function's argument is the offset of the \
contributing pixel (along this dimension) from the input \
position. */ \
for ( ix = lo_x; ix <= hi_x; ix++ ) { \
if( kernel ) { \
( *kernel )( (double) ix - x, params, flags, \
kval + ix - lo_x, status ); \
} else { \
( *fkernel )( (double) ix - x, params, flags, \
kval + ix - lo_x ); \
} \
\
/* Check for errors arising in the kernel function. */ \
if ( !astOK ) { \
kerror = 1; \
goto Kernel_Error_2d; \
} \
} \
\
/* Initialise sums for forming the interpolated result. */ \
sum = (Xfloattype) 0.0; \
wtsum = (Xfloattype) 0.0; \
if ( Usevar ) { \
sum_var = (Xfloattype) 0.0; \
bad_var = 0; \
} \
\
/* Loop over the y index to inspect all the contributing pixels, while \
keeping track of their offset within the input array. Evaluate the \
kernel function for each y index value. */ \
off1 = lo_x - lbnd_in[ 0 ] + ystride * ( lo_y - lbnd_in[ 1 ] ); \
for ( iy = lo_y; iy <= hi_y; iy++, off1 += ystride ) { \
if( kernel ) { \
( *kernel )( (double) iy - y, params, flags, &wt_y, status ); \
} else { \
( *fkernel )( (double) iy - y, params, flags, &wt_y ); \
} \
\
/* Check for errors arising in the kernel function. */ \
if ( !astOK ) { \
kerror = 1; \
goto Kernel_Error_2d; \
} \
\
/* Loop over the x index, calculating the pixel offset in the input \
array. */ \
off_in = off1; \
for ( ix = lo_x; ix <= hi_x; ix++, off_in++ ) { \
\
/* If necessary, test if the input pixel is bad. If not, calculate its \
weight as the product of the kernel function's value for the x and \
y dimensions. */ \
if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \
pixwt = kval[ ix - lo_x ] * wt_y; \
\
/* Form the weighted sums required for finding the interpolated \
value. */ \
sum += ( (Xfloattype) pixwt ) * \
( (Xfloattype) in[ off_in ] ); \
wtsum += (Xfloattype) pixwt; \
\
/* If a variance estimate is required and it still seems possible to \
obtain one, then obtain the variance value associated with the \
current input pixel. */ \
if ( Usevar ) { \
if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \
var = in_var[ off_in ]; \
\
/* If necessary, test if this value is bad (if the data type is \
signed, also check that it is not negative). */ \
if ( Usebad ) bad_var = ( var == badval ); \
CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \
\
/* If any bad input variance value is obtained, we cannot generate a \
valid output variance estimate. Otherwise, form the sum needed to \
calculate this estimate. */ \
if ( !( ( Xsigned ) || ( Usebad ) ) || \
!bad_var ) { \
sum_var += ( (Xfloattype) ( pixwt * pixwt ) ) * \
( (Xfloattype) var ); \
} \
} \
} \
} \
} \
} \
} \
} \
}
/* This subsidiary macro assembles the input data needed in
preparation for forming the interpolated value in the n-dimensional
case. */
#define ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \
\
/* Initialise offsets into the input array. Then loop to obtain each \
coordinate associated with the current output point. */ \
pixel = 0; \
off_in = 0; \
for ( idim = 0; idim < ndim_in; idim++ ) { \
xn = coords[ idim ][ point ]; \
\
/* Test if the coordinate lies outside the input grid, or is bad. If \
either is true, the corresponding output pixel value will be bad, \
so give up on this point. */ \
bad = ( xn < xn_min[ idim ] ) || ( xn >= xn_max[ idim ] ) || \
( xn == AST__BAD ); \
if ( bad ) break; \
\
/* If input bad pixels must be detected, then obtain the index along \
the current input grid dimension of the pixel which contains this \
coordinate and accumulate the pixel's offset from the start of the \
input array. */ \
if ( Usebad ) { \
pixel += stride[ idim ] * \
( (int) floor( xn + 0.5 ) - lbnd_in[ idim ] ); \
} \
\
/* Calculate the lowest and highest indices (in the current dimension) \
of the region of neighbouring pixels that will contribute to the \
interpolated result. Constrain these values to lie within the input \
grid. */ \
ixn = (int) floor( xn ); \
lo[ idim ] = MaxI( ixn - neighb + 1, lbnd_in[ idim ], status ); \
hi[ idim ] = MinI( ixn + neighb, ubnd_in[ idim ], status ); \
\
/* Accumulate the offset (from the start of the input array) of the \
contributing pixel which has the lowest index in each dimension. */ \
off_in += stride[ idim ] * ( lo[ idim ] - lbnd_in[ idim ] ); \
} \
\
/* Once the input pixel which contains the required coordinates has \
been identified, test if it is bad, if necessary. */ \
if ( Usebad ) bad = bad || ( in[ pixel ] == badval ); \
\
/* If OK, loop to evaluate the kernel function which will be used to \
weight the contributions from surrounding pixels. */ \
if ( !bad ) { \
for ( idim = 0; idim < ndim_in; idim++ ) { \
\
/* Set up an array of pointers to locate kernel values (stored in the \
"kval" array) for each dimension. Initially, each of these pointers \
locates the first weight value which applies to the contributing \
pixel with the lowest index in each dimension. */ \
wtptr[ idim ] = kval + ( 2 * neighb * idim ); \
\
/* Also set up pointers to the last weight value in each dimension. */ \
wtptr_last[ idim ] = wtptr[ idim ] + ( hi[ idim ] - lo[ idim ] ); \
\
/* Loop to evaluate the kernel function along each dimension, storing \
the resulting values. The function's argument is the offset of the \
contributing pixel (along the relevant dimension) from the input \
point. */ \
xn = coords[ idim ][ point ]; \
for ( ixn = lo[ idim ]; ixn <= hi[ idim ]; ixn++ ) { \
if( kernel ) { \
( *kernel )( (double) ixn - xn, params, flags, \
wtptr[ idim ] + ixn - lo[ idim ], status ); \
} else { \
( *fkernel )( (double) ixn - xn, params, flags, \
wtptr[ idim ] + ixn - lo[ idim ] ); \
} \
\
/* Check for errors arising in the kernel function. */ \
if ( !astOK ) { \
kerror = 1; \
goto Kernel_Error_Nd; \
} \
} \
} \
\
/* Initialise, and loop over the neighbouring input pixels to obtain \
an interpolated value. */ \
sum = (Xfloattype) 0.0; \
wtsum = (Xfloattype) 0.0; \
if ( Usevar ) { \
sum_var = (Xfloattype) 0.0; \
bad_var = 0; \
} \
idim = ndim_in - 1; \
wtprod[ idim ] = 1.0; \
done = 0; \
do { \
\
/* Each contributing pixel is weighted by the product of the kernel \
weight factors evaluated along each input dimension. However, since \
we typically only change the index of one dimension at a time, we \
can avoid forming this product repeatedly by retaining an array of \
accumulated products for all higher dimensions. We need then only \
update the lower elements in this array, corresponding to those \
dimensions whose index has changed. We do this here, "idim" being \
the index of the most significant dimension to have changed. Note \
that on the first pass, all dimensions are considered changed, \
causing this array to be initialised. */ \
for ( ii = idim; ii >= 1; ii-- ) { \
wtprod[ ii - 1 ] = wtprod[ ii ] * *( wtptr[ ii ] ); \
} \
\
/* If necessary, test each pixel which may contribute to the result to \
see if it is bad. If not, obtain its weight from the accumulated \
product of weights. Also multiply by the weight for dimension zero, \
which is not included in the "wtprod" array). */ \
if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \
pixwt = wtprod[ 0 ] * *( wtptr[ 0 ] ); \
\
/* Form the weighted sums required for finding the interpolated \
value. */ \
sum += ( (Xfloattype) pixwt ) * ( (Xfloattype) in[ off_in ] ); \
wtsum += (Xfloattype) pixwt; \
\
/* If a variance estimate is required and it still seems possible to \
obtain one, then obtain the variance value associated with the \
current input pixel. */ \
if ( Usevar ) { \
if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \
var = in_var[ off_in ]; \
\
/* If necessary, test if this value is bad (if the data type is \
signed, also check that it is not negative). */ \
if ( Usebad ) bad_var = ( var == badval ); \
CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \
\
/* If any bad input variance value is obtained, we cannot generate a \
valid output variance estimate. Otherwise, form the sum needed to \
calculate this estimate. */ \
if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \
sum_var += ( (Xfloattype) ( pixwt * pixwt ) ) * \
( (Xfloattype) var ); \
} \
} \
} \
} \
\
/* Now update the weight value pointers and pixel offset to refer to \
the next input pixel to be considered. */ \
idim = 0; \
do { \
\
/* The first input dimension whose weight value pointer has not yet \
reached its final value has this pointer incremented, and the pixel \
offset into the input array is updated accordingly. */ \
if ( wtptr[ idim ] != wtptr_last[ idim ] ) { \
wtptr[ idim ]++; \
off_in += stride[ idim ]; \
break; \
\
/* Any earlier dimensions (which have reached the final pointer value) \
have this pointer returned to its lowest value. Again, the pixel \
offset into the input image is updated accordingly. */ \
} else { \
wtptr[ idim ] -= ( hi[ idim ] - lo[ idim ] ); \
off_in -= stride[ idim ] * \
( hi[ idim ] - lo[ idim ] ); \
done = ( ++idim == ndim_in ); \
} \
} while ( !done ); \
} while ( !done ); \
}
/* This subsidiary macro calculates the interpolated output value (and
variance) from the sums over contributing pixels, checks the
results for validity, and assigns them to the output array(s). */
#define CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Usebad,Usevar,Nobad) \
\
/* If the output data value has not yet been flagged as bad, then \
check that an interpolated value can actually be produced. First \
check that the sum of weights is not zero. */ \
if ( !bad ) { \
bad = ( wtsum == (Xfloattype) 0.0 ); \
\
/* If OK, calculate the interpolated value. Then, if the output data \
type is not floating point, check that this value will not overflow \
the available output range. */ \
if ( !bad ) { \
val = sum / wtsum; \
if ( !( Xfloating ) ) { \
bad = ( val <= lo_lim ) || ( val >= hi_lim ); \
} \
} \
\
/* If no interpolated data value can be produced, then no associated \
variance will be required either. */ \
if ( ( Usevar ) && bad ) bad_var = 1; \
} \
\
/* Now perform similar checks on the output variance value (if \
required). This time we check that the square of the sum of \
weights is not zero (since this might underflow before the sum of \
weights). Again we also check to prevent the result overflowing the \
output data type. */ \
if ( ( Usevar ) && !bad_var ) { \
wtsum_sq = wtsum * wtsum; \
bad_var = ( wtsum_sq == (Xfloattype) 0.0 ); \
if ( !bad_var ) { \
val_var = sum_var / wtsum_sq; \
if ( !( Xfloating ) ) { \
bad_var = ( val_var <= lo_lim ) || ( val_var >= hi_lim ); \
} \
} \
} \
\
/* Obtain the pixel offset into the output array. */ \
off_out = offset[ point ]; \
\
/* Assign a bad output value (and variance) if required and count it. */ \
if ( bad ) { \
if( !Nobad ) { \
out[ off_out ] = badval; \
if ( Usevar ) out_var[ off_out ] = badval; \
} \
result++; \
\
/* Otherwise, assign the interpolated value. If the output data type \
is floating point, the result can be stored directly, otherwise we \
must round to the nearest integer. */ \
} else { \
if ( Xfloating ) { \
out[ off_out ] = (Xtype) val; \
} else { \
out[ off_out ] = (Xtype) ( val + ( ( val >= (Xfloattype) 0.0 ) ? \
( (Xfloattype) 0.5 ) : \
( (Xfloattype) -0.5 ) ) ); \
} \
\
/* If a variance estimate is required but none can be obtained, then \
store a bad output variance value and count it. */ \
if ( Usevar ) { \
if ( bad_var ) { \
if( !Nobad ) { \
out_var[ off_out ] = badval; \
} \
result++; \
\
/* Otherwise, store the variance estimate, rounding to the nearest \
integer if necessary. */ \
} else { \
if ( Xfloating ) { \
out_var[ off_out ] = (Xtype) val_var; \
} else { \
out_var[ off_out ] = (Xtype) ( val_var + \
( ( val_var >= (Xfloattype) 0.0 ) ? \
( (Xfloattype) 0.5 ) : \
( (Xfloattype) -0.5 ) ) ); \
} \
} \
} \
}
/* These subsidiary macros define limits for range checking of results
before conversion to the final data type. For each data type code
, HI_ gives the least positive floating point value which
just overflows that data type towards plus infinity, while LO_
gives the least negative floating point value which just overflows
that data type towards minus infinity. Thus, a floating point value
must satisfy LO is a floating point type, the limits are not actually used,
but must be present to permit error-free compilation. */
#if HAVE_LONG_DOUBLE /* Not normally implemented */
#define HI_LD ( 0.0L )
#define LO_LD ( 0.0L )
#endif
#define HI_D ( 0.0 )
#define LO_D ( 0.0 )
#define HI_F ( 0.0f )
#define LO_F ( 0.0f )
#if HAVE_LONG_DOUBLE /* Not normally implemented */
#define HI_K ( 0.5L + (long double) LONG_MAX )
#define LO_K ( -0.5L + (long double) LONG_MIN )
#define HI_UK ( 0.5L + (long double) ULONG_MAX )
#define LO_UK ( -0.5L )
#define HI_L ( 0.5L + (long double) LONG_MAX )
#define LO_L ( -0.5L + (long double) LONG_MIN )
#define HI_UL ( 0.5L + (long double) ULONG_MAX )
#define LO_UL ( -0.5L )
#else
#define HI_K ( 0.5 + (double) LONG_MAX )
#define LO_K ( -0.5 + (double) LONG_MIN )
#define HI_UK ( 0.5 + (double) ULONG_MAX )
#define LO_UK ( -0.5 )
#define HI_L ( 0.5 + (double) LONG_MAX )
#define LO_L ( -0.5 + (double) LONG_MIN )
#define HI_UL ( 0.5 + (double) ULONG_MAX )
#define LO_UL ( -0.5 )
#endif
#define HI_I ( 0.5 + (double) INT_MAX )
#define LO_I ( -0.5 + (double) INT_MIN )
#define HI_UI ( 0.5 + (double) UINT_MAX )
#define LO_UI ( -0.5 )
#define HI_S ( 0.5f + (float) SHRT_MAX )
#define LO_S ( -0.5f + (float) SHRT_MIN )
#define HI_US ( 0.5f + (float) USHRT_MAX )
#define LO_US ( -0.5f )
#define HI_B ( 0.5f + (float) SCHAR_MAX )
#define LO_B ( -0.5f + (float) SCHAR_MIN )
#define HI_UB ( 0.5f + (float) UCHAR_MAX )
#define LO_UB ( -0.5f )
/* This subsidiary macro tests for negative variance values. This
check is required only for signed data types. */
#define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \
bad_var = bad_var || ( var < ( (Xtype) 0 ) );
/* Expand the main macro above to generate a function for each
required signed data type. */
#if HAVE_LONG_DOUBLE /* Not normally implemented */
MAKE_INTERPOLATE_KERNEL1(LD,long double,1,long double,1)
MAKE_INTERPOLATE_KERNEL1(L,long int,0,long double,1)
MAKE_INTERPOLATE_KERNEL1(K,INT_BIG,0,long double,1)
#else
MAKE_INTERPOLATE_KERNEL1(L,long int,0,double,1)
MAKE_INTERPOLATE_KERNEL1(K,INT_BIG,0,double,1)
#endif
MAKE_INTERPOLATE_KERNEL1(D,double,1,double,1)
MAKE_INTERPOLATE_KERNEL1(F,float,1,float,1)
MAKE_INTERPOLATE_KERNEL1(I,int,0,double,1)
MAKE_INTERPOLATE_KERNEL1(S,short int,0,float,1)
MAKE_INTERPOLATE_KERNEL1(B,signed char,0,float,1)
/* Re-define the macro for testing for negative variances to do
nothing. */
#undef CHECK_FOR_NEGATIVE_VARIANCE
#define CHECK_FOR_NEGATIVE_VARIANCE(Xtype)
/* Expand the main macro above to generate a function for each
required unsigned data type. */
#if HAVE_LONG_DOUBLE /* Not normally implemented */
MAKE_INTERPOLATE_KERNEL1(UL,unsigned long int,0,long double,0)
MAKE_INTERPOLATE_KERNEL1(UK,UINT_BIG,0,long double,0)
#else
MAKE_INTERPOLATE_KERNEL1(UL,unsigned long int,0,double,0)
MAKE_INTERPOLATE_KERNEL1(UK,UINT_BIG,0,double,0)
#endif
MAKE_INTERPOLATE_KERNEL1(UI,unsigned int,0,double,0)
MAKE_INTERPOLATE_KERNEL1(US,unsigned short int,0,float,0)
MAKE_INTERPOLATE_KERNEL1(UB,unsigned char,0,float,0)
/* Undefine the macros used above. */
#undef CHECK_FOR_NEGATIVE_VARIANCE
#if HAVE_LONG_DOUBLE /* Not normally implemented */
#undef HI_LD
#undef LO_LD
#endif
#undef HI_D
#undef LO_D
#undef HI_F
#undef LO_F
#undef HI_L
#undef LO_L
#undef HI_UL
#undef LO_UL
#undef HI_K
#undef LO_K
#undef HI_UK
#undef LO_UK
#undef HI_I
#undef LO_I
#undef HI_UI
#undef LO_UI
#undef HI_S
#undef LO_S
#undef HI_US
#undef LO_US
#undef HI_B
#undef LO_B
#undef HI_UB
#undef LO_UB
#undef CALC_AND_ASSIGN_OUTPUT
#undef ASSEMBLE_INPUT_ND
#undef ASSEMBLE_INPUT_2D
#undef ASSEMBLE_INPUT_1D
#undef MAKE_INTERPOLATE_KERNEL1
/*
* Name:
* InterpolateLinear
* Purpose:
* Resample a data grid, using the linear interpolation scheme.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* int InterpolateLinear( int ndim_in,
* const int *lbnd_in, const int *ubnd_in,
* const *in, const *in_var,
* int npoint, const int *offset,
* const double *const *coords,
* int flags, badval,
* *out, *out_var )
* Class Membership:
* Mapping member function.
* Description:
* This is a set of functions which resample a rectangular input
* grid of data (and, optionally, associated statistical variance
* values) so as to place them into a new output grid. Each output
* grid point may be mapped on to a position in the input grid in
* an arbitrary way. Where the positions given do not correspond
* with a pixel centre in the input grid, the interpolation scheme
* used is linear interpolation between the centres of the nearest
* neighbouring pixels in each dimension (there are 2 nearest
* neighbours in 1 dimension, 4 in 2 dimensions, 8 in 3 dimensions,
* etc.).
* Parameters:
* ndim_in
* The number of dimensions in the input grid. This should be at
* least one.
* lbnd_in
* Pointer to an array of integers, with "ndim_in" elements.
* This should give the coordinates of the centre of the first
* pixel in the input grid along each dimension.
* ubnd_in
* Pointer to an array of integers, with "ndim_in" elements.
* This should give the coordinates of the centre of the last
* pixel in the input grid along each dimension.
*
* Note that "lbnd_in" and "ubnd_in" together define the shape
* and size of the input grid, its extent along a particular
* (i'th) dimension being ubnd_in[i]-lbnd_in[i]+1 (assuming "i"
* is zero-based). They also define the input grid's coordinate
* system, with each pixel being of unit extent along each
* dimension with integral coordinate values at its centre.
* in
* Pointer to the array of data to be resampled (with an element
* for each pixel in the input grid). The numerical type of
* these data should match the function used, as given by the
* suffix on the function name. The storage order should be such
* that the index of the first grid dimension varies most
* rapidly and that of the final dimension least rapidly
* (i.e. Fortran array storage order).
* in_var
* An optional pointer to a second array of positive numerical
* values (with the same size and type as the "in" array), which
* represent estimates of the statistical variance associated
* with each element of the "in" array. If this second array is
* given (along with the corresponding "out_var" array), then
* estimates of the variance of the resampled data will also be
* returned.
*
* If no variance estimates are required, a NULL pointer should
* be given.
* npoint
* The number of points at which the input grid is to be
* resampled.
* offset
* Pointer to an array of integers with "npoint" elements. For
* each output point, this array should contain the zero-based
* offset in the output array(s) (i.e. the "out" and,
* optionally, the "out_var" arrays) at which the resampled
* output value(s) should be stored.
* coords
* An array of pointers to double, with "ndim_in"
* elements. Element "coords[coord]" should point at the first
* element of an array of double (with "npoint" elements) which
* contains the values of coordinate number "coord" for each
* interpolation point. The value of coordinate number "coord"
* for interpolation point number "point" is therefore given by
* "coords[coord][point]" (assuming both indices to be
* zero-based). If any point has a coordinate value of AST__BAD
* associated with it, then the corresponding output data (and
* variance) will be set to the value given by "badval" (unles the
* AST__NOBAD flag is specified).
* flags
* The bitwise OR of a set of flag values which control the
* operation of the function. Currently, only the flag
* AST__USEBAD is significant and indicates whether there are
* "bad" (i.e. missing) data in the input array(s) which must be
* recognised and propagated to the output array(s). If this
* flag is not set, all input values are treated literally.
* badval
* If the AST__USEBAD flag is set in the "flags" value (above),
* this parameter specifies the value which is used to identify
* bad data and/or variance values in the input array(s). Its
* numerical type must match that of the "in" (and "in_var")
* arrays. Unles the AST__NOBAD flag is specified in "flags", the
* same value will also be used to flag any output array elements
* for which resampled values could not be obtained. The output
* arrays(s) may be flagged with this value whether or not the
* AST__USEBAD flag is set (the function return value indicates
* whether any such values have been produced).
* out
* Pointer to an array with the same data type as the "in"
* array, into which the resampled data will be returned. Note
* that details of how the output grid maps on to this array
* (e.g. the storage order, number of dimensions, etc.) is
* arbitrary and is specified entirely by means of the "offset"
* array. The "out" array should therefore contain sufficient
* elements to accommodate the "offset" values supplied. There
* is no requirement that all elements of the "out" array should
* be assigned values, and any which are not addressed by the
* contents of the "offset" array will be left unchanged.
* out_var
* An optional pointer to an array with the same data type and
* size as the "out" array, into which variance estimates for
* the resampled values may be returned. This array will only be
* used if the "in_var" array has been given. It is addressed in
* exactly the same way (via the "offset" array) as the "out"
* array. The values returned are estimates of the statistical
* variance of the corresponding values in the "out" array, on
* the assumption that all errors in input grid values (in the
* "in" array) are statistically independent and that their
* variance estimates (in the "in_var" array) may simply be
* summed (with appropriate weighting factors).
*
* If no output variance estimates are required, a NULL pointer
* should be given.
* Returned Value:
* The number of output grid points to which a data value (or a
* variance value if relevant) equal to "badval" has been assigned
* because no valid output value could be obtained.
* Notes:
* - There is a separate function for each numerical type of
* gridded data, distinguished by replacing the in the function
* name by the appropriate 1- or 2-character suffix.
* - A value of zero will be returned if any of these functions is
* invoked with the global error status set, or if it should fail
* for any reason.
*/
/* Define macros to implement the function for a specific data
type. */
#define MAKE_INTERPOLATE_LINEAR(X,Xtype,Xfloating,Xfloattype,Xsigned) \
static int InterpolateLinear##X( int ndim_in, \
const int *lbnd_in, const int *ubnd_in, \
const Xtype *in, const Xtype *in_var, \
int npoint, const int *offset, \
const double *const *coords, \
int flags, Xtype badval, \
Xtype *out, Xtype *out_var, int *status ) { \
\
/* Local Variables: */ \
Xfloattype sum; /* Weighted sum of pixel data values */ \
Xfloattype sum_var; /* Weighted sum of pixel variance values */ \
Xfloattype val; /* Value to be asigned to output pixel */ \
Xfloattype wtsum; /* Sum of weight values */ \
Xtype var; /* Variance value */ \
double *frac_hi; /* Pointer to array of weights */ \
double *frac_lo; /* Pointer to array of weights */ \
double *wt; /* Pointer to array of weights */ \
double *wtprod; /* Array of accumulated weights pointer */ \
double *xn_max; /* Pointer to upper limits array (n-d) */ \
double *xn_min; /* Pointer to lower limits array (n-d) */ \
double frac_hi_x; /* Pixel weight (x dimension) */ \
double frac_hi_y; /* Pixel weight (y dimension) */ \
double frac_lo_x; /* Pixel weight (x dimension) */ \
double frac_lo_y; /* Pixel weight (y dimension) */ \
double pixwt; /* Weight to apply to individual pixel */ \
double x; /* x coordinate value */ \
double xmax; /* x upper limit */ \
double xmin; /* x lower limit */ \
double xn; /* Coordinate value (n-d) */ \
double y; /* y coordinate value */ \
double ymax; /* y upper limit */ \
double ymin; /* y lower limit */ \
int *dim; /* Pointer to array of pixel indices */ \
int *hi; /* Pointer to array of upper indices */ \
int *lo; /* Pointer to array of lower indices */ \
int *stride; /* Pointer to array of dimension strides */ \
int bad; /* Output pixel bad? */ \
int bad_var; /* Output variance bad? */ \
int done; /* All pixel indices done? */ \
int hi_x; /* Upper pixel index (x dimension) */ \
int hi_y; /* Upper pixel index (y dimension) */ \
int idim; /* Loop counter for dimensions */ \
int ii; /* Loop counter for weights */ \
int ix; /* Pixel index in input grid x dimension */ \
int ixn; /* Pixel index (n-d) */ \
int iy; /* Pixel index in input grid y dimension */ \
int lo_x; /* Lower pixel index (x dimension) */ \
int lo_y; /* Lower pixel index (y dimension) */ \
int nobad; /* Was the AST__NOBAD flag set? */ \
int off_in; /* Offset to input pixel */ \
int off_lo; /* Offset to "first" input pixel */ \
int off_out; /* Offset to output pixel */ \
int pixel; /* Offset to input pixel containing point */ \
int point; /* Loop counter for output points */ \
int result; /* Result value to return */ \
int s; /* Temporary variable for strides */ \
int usebad; /* Use "bad" input pixel values? */ \
int usevar; /* Process variance array? */ \
int ystride; /* Stride along input grid y dimension */ \
\
/* Initialise. */ \
result = 0; \
\
/* Check the global error status. */ \
if ( !astOK ) return result; \
\
/* Initialise variables to avoid "used of uninitialised variable" \
messages from dumb compilers. */ \
sum = 0; \
sum_var = 0; \
wtsum = 0; \
bad = 0; \
bad_var = 0; \
\
/* Determine if we are processing bad pixels or variances. */ \
nobad = flags & AST__NOBAD; \
usebad = flags & AST__USEBAD; \
usevar = in_var && out_var; \
\
/* Handle the 1-dimensional case optimally. */ \
/* ---------------------------------------- */ \
if ( ndim_in == 1 ) { \
\
/* Calculate the coordinate limits of the input grid. */ \
xmin = (double) lbnd_in[ 0 ] - 0.5; \
xmax = (double) ubnd_in[ 0 ] + 0.5; \
\
/* Identify four cases, according to whether bad pixels and/or \
variances are being processed. In each case, loop through all the \
output points to (a) assemble the input data needed to form the \
interpolated value, and (b) calculate the result and assign it to \
the output arrays(s). In each case we assign constant values (0 or \
1) to the "Usebad" and "Usevar" flags so that code for handling bad \
pixels and variances can be eliminated when not required. */ \
if ( nobad ) { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
1,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
1,0,1) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
0,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
0,0,1) \
} \
} \
} \
\
/* Four more cases as above, but this time with the AST__NOBAD flag \
un-set. */ \
} else { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
1,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
1,0,0) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
0,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
0,0,0) \
} \
} \
} \
} \
\
/* Handle the 2-dimensional case optimally. */ \
/* ---------------------------------------- */ \
} else if ( ndim_in == 2 ) { \
\
/* Calculate the stride along the y dimension of the input grid. */ \
ystride = ubnd_in[ 0 ] - lbnd_in[ 0 ] + 1; \
\
/* Calculate the coordinate limits of the input grid in each \
dimension. */ \
xmin = (double) lbnd_in[ 0 ] - 0.5; \
xmax = (double) ubnd_in[ 0 ] + 0.5; \
ymin = (double) lbnd_in[ 1 ] - 0.5; \
ymax = (double) ubnd_in[ 1 ] + 0.5; \
\
/* Identify four cases, according to whether bad pixels and/or \
variances are being processed. In each case, loop through all the \
output points to (a) assemble the input data needed to form the \
interpolated value, and (b) calculate the result and assign it to \
the output arrays(s). In each case we assign constant values (0 or \
1) to the "Usebad" and "Usevar" flags so that code for handling bad \
pixels and variances can be eliminated when not required. */ \
if ( nobad ) { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
1,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
1,0,1) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
0,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
0,0,1) \
} \
} \
} \
\
/* Four more case as above, but without the AST__NOBAD flag. */ \
} else { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
1,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
1,0,0) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
0,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
0,0,0) \
} \
} \
} \
} \
\
/* Handle other numbers of dimensions. */ \
/* ----------------------------------- */ \
} else { \
\
/* Allocate workspace. */ \
dim = astMalloc( sizeof( int ) * (size_t) ndim_in ); \
frac_hi = astMalloc( sizeof( double ) * (size_t) ndim_in ); \
frac_lo = astMalloc( sizeof( double ) * (size_t) ndim_in ); \
hi = astMalloc( sizeof( int ) * (size_t) ndim_in ); \
lo = astMalloc( sizeof( int ) * (size_t) ndim_in ); \
stride = astMalloc( sizeof( int ) * (size_t) ndim_in ); \
wt = astMalloc( sizeof( double ) * (size_t) ndim_in ); \
wtprod = astMalloc( sizeof( double ) * (size_t) ndim_in ); \
xn_max = astMalloc( sizeof( double ) * (size_t) ndim_in ); \
xn_min = astMalloc( sizeof( double ) * (size_t) ndim_in ); \
if ( astOK ) { \
\
/* Calculate the stride along each dimension of the input grid. */ \
for ( s = 1, idim = 0; idim < ndim_in; idim++ ) { \
stride[ idim ] = s; \
s *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \
\
/* Calculate the coordinate limits of the input grid in each \
dimension. */ \
xn_min[ idim ] = (double) lbnd_in[ idim ] - 0.5; \
xn_max[ idim ] = (double) ubnd_in[ idim ] + 0.5; \
} \
\
/* Identify four cases, according to whether bad pixels and/or \
variances are being processed. In each case, loop through all the \
output points to (a) assemble the input data needed to form the \
interpolated value, and (b) calculate the result and assign it to \
the output arrays(s). In each case we assign constant values (0 or \
1) to the "Usebad" and "Usevar" flags so that code for handling bad \
pixels and variances can be eliminated when not required. */ \
if ( nobad ) { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \
Xsigned,1,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \
Xsigned,1,0,1) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \
Xsigned,0,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \
Xsigned,0,0,1) \
} \
} \
} \
\
/* Four more case as above, but without the AST__NOBAD flag. */ \
} else { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \
Xsigned,1,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \
Xsigned,1,0,0) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \
Xsigned,0,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \
Xsigned,0,0,0) \
} \
} \
} \
} \
} \
\
/* Free the workspace. */ \
dim = astFree( dim ); \
frac_hi = astFree( frac_hi ); \
frac_lo = astFree( frac_lo ); \
hi = astFree( hi ); \
lo = astFree( lo ); \
stride = astFree( stride ); \
wt = astFree( wt ); \
wtprod = astFree( wtprod ); \
xn_max = astFree( xn_max ); \
xn_min = astFree( xn_min ); \
} \
\
/* If an error has occurred, clear the returned result. */ \
if ( !astOK ) result = 0; \
\
/* Return the result. */ \
return result; \
}
/* This subsidiary macro assembles the input data needed in
preparation for forming the interpolated value in the 1-dimensional
case. */
#define ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \
\
/* Obtain the x coordinate of the current point and test if it lies \
outside the input grid. Also test if it is bad. */ \
x = coords[ 0 ][ point ]; \
bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \
if ( !bad ) { \
\
/* If input bad pixels must be detected, then obtain the offset along \
the input grid x dimension of the input pixel which contains the \
current coordinate and calculate this pixel's offset from the start \
of the input array. */ \
if ( Usebad ) { \
pixel = (int) floor( x + 0.5 ) - lbnd_in[ 0 ]; \
\
/* Test if the pixel is bad. */ \
bad = ( in[ pixel ] == badval ); \
} \
\
/* If OK, obtain the indices along the input grid x dimension of the \
two adjacent pixels which will contribute to the interpolated \
result. Also obtain the fractional weight to be applied to each of \
these pixels. */ \
if ( !bad ) { \
lo_x = (int) floor( x ); \
hi_x = lo_x + 1; \
frac_lo_x = (double) hi_x - x; \
frac_hi_x = 1.0 - frac_lo_x; \
\
/* Obtain the offset within the input array of the first pixel to be \
used for interpolation (the one with the smaller index). */ \
off_lo = lo_x - lbnd_in[ 0 ]; \
\
/* Initialise sums for forming the interpolated result. */ \
sum = (Xfloattype) 0.0; \
wtsum = (Xfloattype) 0.0; \
if ( Usevar ) { \
sum_var = (Xfloattype) 0.0; \
if ( ( Xsigned ) || ( Usebad ) ) bad_var = 0; \
} \
\
/* For each of the two pixels which may contribute to the result, \
test if the pixel index lies within the input grid. Where it does, \
accumulate the sums required for forming the interpolated \
result. In each case, we supply the pixel's offset within the input \
array and the weight to be applied to it. */ \
if ( lo_x >= lbnd_in[ 0 ] ) { \
FORM_LINEAR_INTERPOLATION_SUM(off_lo,frac_lo_x,Xtype, \
Xfloattype,Xsigned,Usebad,Usevar) \
} \
if ( hi_x <= ubnd_in[ 0 ] ) { \
FORM_LINEAR_INTERPOLATION_SUM(off_lo + 1,frac_hi_x,Xtype, \
Xfloattype,Xsigned,Usebad,Usevar) \
} \
} \
}
/* This subsidiary macro assembles the input data needed in
preparation for forming the interpolated value in the 2-dimensional
case. */
#define ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \
\
/* Obtain the x coordinate of the current point and test if it lies \
outside the input grid. Also test if it is bad. */ \
x = coords[ 0 ][ point ]; \
bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \
if ( !bad ) { \
\
/* If OK, then similarly obtain and test the y coordinate. */ \
y = coords[ 1 ][ point ]; \
bad = ( y < ymin ) || ( y >= ymax ) || ( y == AST__BAD ); \
if ( !bad ) { \
\
/* If input bad pixels must be detected, then obtain the offsets along \
each input grid dimension of the input pixel which contains the \
current coordinates. */ \
if ( Usebad ) { \
ix = (int) floor( x + 0.5 ); \
iy = (int) floor( y + 0.5 ); \
\
/* Calculate this pixel's offset from the start of the input array. */ \
pixel = ix - lbnd_in[ 0 ] + ystride * ( iy - lbnd_in[ 1 ] ); \
\
/* Test if the pixel is bad. */ \
bad = ( in[ pixel ] == badval ); \
} \
\
/* If OK, obtain the indices along the input grid x dimension of the \
two adjacent pixels which will contribute to the interpolated \
result. Also obtain the fractional weight to be applied to each of \
these pixels. */ \
if ( !bad ) { \
lo_x = (int) floor( x ); \
hi_x = lo_x + 1; \
frac_lo_x = (double) hi_x - x; \
frac_hi_x = 1.0 - frac_lo_x; \
\
/* Repeat this process for the y dimension. */ \
lo_y = (int) floor( y ); \
hi_y = lo_y + 1; \
frac_lo_y = (double) hi_y - y; \
frac_hi_y = 1.0 - frac_lo_y; \
\
/* Obtain the offset within the input array of the first pixel to be \
used for interpolation (the one with the smaller index along both \
dimensions). */ \
off_lo = lo_x - lbnd_in[ 0 ] + ystride * ( lo_y - lbnd_in[ 1 ] ); \
\
/* Initialise sums for forming the interpolated result. */ \
sum = (Xfloattype) 0.0; \
wtsum = (Xfloattype) 0.0; \
if ( Usevar ) { \
sum_var = (Xfloattype) 0.0; \
if ( ( Xsigned ) || ( Usebad ) ) bad_var = 0; \
} \
\
/* For each of the four pixels which may contribute to the result, \
test if the pixel indices lie within the input grid. Where they do, \
accumulate the sums required for forming the interpolated \
result. In each case, we supply the pixel's offset within the input \
array and the weight to be applied to it. */ \
if ( lo_y >= lbnd_in[ 1 ] ) { \
if ( lo_x >= lbnd_in[ 0 ] ) { \
FORM_LINEAR_INTERPOLATION_SUM(off_lo, \
frac_lo_x * frac_lo_y,Xtype, \
Xfloattype, Xsigned, \
Usebad,Usevar) \
} \
if ( hi_x <= ubnd_in[ 0 ] ) { \
FORM_LINEAR_INTERPOLATION_SUM(off_lo + 1, \
frac_hi_x * frac_lo_y,Xtype, \
Xfloattype,Xsigned, \
Usebad,Usevar) \
} \
} \
if ( hi_y <= ubnd_in[ 1 ] ) { \
if ( lo_x >= lbnd_in[ 0 ] ) { \
FORM_LINEAR_INTERPOLATION_SUM(off_lo + ystride, \
frac_lo_x * frac_hi_y,Xtype, \
Xfloattype,Xsigned, \
Usebad,Usevar) \
} \
if ( hi_x <= ubnd_in[ 0 ] ) { \
FORM_LINEAR_INTERPOLATION_SUM(off_lo + ystride + 1, \
frac_hi_x * frac_hi_y,Xtype, \
Xfloattype,Xsigned, \
Usebad,Usevar) \
} \
} \
} \
} \
}
/* This subsidiary macro assembles the input data needed in
preparation for forming the interpolated value in the n-dimensional
case. */
#define ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \
\
/* Initialise offsets into the input array. Then loop to obtain each
coordinate associated with the current output point. */ \
off_in = 0; \
if ( Usebad ) pixel = 0; \
for ( idim = 0; idim < ndim_in; idim++ ) { \
xn = coords[ idim ][ point ]; \
\
/* Test if the coordinate lies outside the input grid. Also test if \
it is bad. If either is true, the corresponding output pixel value \
will be bad, so give up on this point. */ \
bad = ( xn < xn_min[ idim ] ) || ( xn >= xn_max[ idim ] ) || \
( xn == AST__BAD ); \
if ( bad ) break; \
\
/* If input bad pixels must be detected, obtain the index along the \
current input grid dimension of the pixel which contains this \
coordinate and accumulate the pixel's offset from the start of the \
input array. */ \
if ( Usebad ) { \
pixel += stride[ idim ] * \
( (int) floor( xn + 0.5 ) - lbnd_in[ idim ] ); \
} \
\
/* Obtain the indices along the current dimension of the input grid of \
the two (usually adjacent) pixels which will contribute to the \
output value. If necessary, however, restrict each index to ensure \
it does not lie outside the input grid. Also calculate the \
fractional weight to be given to each pixel in order to interpolate \
linearly between them. */ \
ixn = (int) floor( xn ); \
lo[ idim ] = MaxI( ixn, lbnd_in[ idim ], status ); \
hi[ idim ] = MinI( ixn + 1, ubnd_in[ idim ], status ); \
frac_lo[ idim ] = 1.0 - fabs( xn - (double) lo[ idim ] ); \
frac_hi[ idim ] = 1.0 - fabs( xn - (double) hi[ idim ] ); \
\
/* Store the lower index involved in interpolation along each \
dimension and accumulate the offset from the start of the input \
array of the pixel which has these indices. */ \
dim[ idim ] = lo[ idim ]; \
off_in += stride[ idim ] * ( lo[ idim ] - lbnd_in[ idim ] ); \
\
/* Also store the fractional weight associated with the lower pixel \
along each dimension. */ \
wt[ idim ] = frac_lo[ idim ]; \
} \
\
/* If the input pixel which contains the required coordinates has \
been identified, test if it is bad. */ \
if ( Usebad ) bad = bad || ( in[ pixel ] == badval ); \
\
/* If OK, initialise and loop over adjacent input pixels to obtain an \
interpolated value. */ \
if ( !bad ) { \
sum = (Xfloattype) 0.0; \
wtsum = (Xfloattype) 0.0; \
if ( Usevar ) { \
sum_var = (Xfloattype) 0.0; \
if ( ( Xsigned ) || ( Usebad ) ) bad_var = 0; \
} \
idim = ndim_in - 1; \
wtprod[ idim ] = 1.0; \
done = 0; \
do { \
\
/* Each contributing pixel is weighted by the product of the weights \
which account for the displacement of its centre from the required \
position along each dimension. However, since we typically only \
change the index of one dimension at a time, we can avoid forming \
this product repeatedly by retaining an array of accumulated weight \
products for all higher dimensions. We need then only update the \
lower elements in this array, corresponding to those dimensions \
whose index has changed. We do this here, "idim" being the index of \
the most significant dimension to have changed. Note that on the \
first pass, all dimensions are considered changed, causing this \
array to be initialised. */ \
for ( ii = idim; ii >= 1; ii-- ) { \
wtprod[ ii - 1 ] = wtprod[ ii ] * wt[ ii ]; \
} \
\
/* Accumulate the sums required for forming the interpolated \
result. We supply the pixel's offset within the input array and the \
weight to be applied to it. The pixel weight is formed by including \
the weight factor for dimension zero, since this is not included in \
the "wtprod" array. */ \
FORM_LINEAR_INTERPOLATION_SUM(off_in,wtprod[ 0 ] * wt[ 0 ], \
Xtype,Xfloattype,Xsigned, \
Usebad,Usevar) \
\
/* Now update the indices, offset and weight factors to refer to the \
next input pixel to be considered. */ \
idim = 0; \
do { \
\
/* The first input dimension which still refers to the pixel with the \
lower of the two possible indices is switched to refer to the other \
pixel (with the higher index). The offset into the input array and \
the fractional weight factor for this dimension are also updated \
accordingly. */ \
if ( dim[ idim ] != hi[ idim ] ) { \
dim[ idim ] = hi[ idim ]; \
off_in += stride[ idim ]; \
wt[ idim ] = frac_hi[ idim ]; \
break; \
\
/* Any earlier dimensions (referring to the higher index) are switched \
back to the lower index, if not already there, before going on to \
consider the next dimension. (This process is the same as \
incrementing a binary number and propagating overflows up through \
successive digits, except that dimensions where the "lo" and "hi" \
values are the same can only take one value.) The process stops at \
the first attempt to return the final dimension to the lower \
index. */ \
} else { \
if ( dim[ idim ] != lo[ idim ] ) { \
dim[ idim ] = lo[ idim ]; \
off_in -= stride[ idim ]; \
wt[ idim ] = frac_lo[ idim ]; \
} \
done = ( ++idim == ndim_in ); \
} \
} while ( !done ); \
} while ( !done ); \
}
/* This subsidiary macro adds the contribution from a specified input
pixel to the accumulated sums for forming the linearly interpolated
value. */
#define FORM_LINEAR_INTERPOLATION_SUM(off,wt,Xtype,Xfloattype,Xsigned, \
Usebad,Usevar) \
\
/* Obtain the offset of the input pixel to use. */ \
off_in = ( off ); \
\
/* If necessary, test if this pixel is bad. If not, then obtain the \
weight to apply to it. */ \
if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \
pixwt = ( wt ); \
\
/* Increment the weighted sum of pixel values and the sum of weights. */ \
sum += ( (Xfloattype) in[ off_in ] ) * ( (Xfloattype) pixwt ); \
wtsum += (Xfloattype) pixwt; \
\
/* If an output variance estimate is to be generated, and it still \
seems possible to produce one, then obtain the input variance \
value. */ \
if ( Usevar ) { \
if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \
var = in_var[ off_in ]; \
\
/* Test if the variance value is bad (if the data type is signed, also \
check that it is not negative). */ \
if ( Usebad ) bad_var = ( var == badval ); \
CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \
\
/* If OK, increment the weighted sum of variance values. */ \
if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \
sum_var += ( (Xfloattype) ( pixwt * pixwt ) ) * \
( (Xfloattype) var ); \
} \
} \
} \
}
/* This subsidiary macro calculates the interpolated output value (and
variance) from the sums over contributing pixels and assigns them
to the output array(s). */
#define CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \
Usebad,Usevar,Nobad) \
\
/* Obtain the pixel offset into the output array. */ \
off_out = offset[ point ]; \
\
/* Assign a bad output value (and variance) if required and count it. */ \
if ( bad ) { \
if( !Nobad ) { \
out[ off_out ] = badval; \
if ( Usevar ) out_var[ off_out ] = badval; \
} \
result++; \
\
/* Otherwise, calculate the interpolated value. If the output data \
type is floating point, this result can be stored directly, \
otherwise we must round to the nearest integer. */ \
} else { \
val = sum / wtsum; \
if ( Xfloating ) { \
out[ off_out ] = (Xtype) val; \
} else { \
out[ off_out ] = (Xtype) ( val + ( ( val >= (Xfloattype) 0.0 ) ? \
( (Xfloattype) 0.5 ) : \
( (Xfloattype) -0.5 ) ) ); \
} \
\
/* If a variance estimate is required but none can be obtained, then \
store a bad output variance value and count it. */ \
if ( Usevar ) { \
if ( ( ( Xsigned ) || ( Usebad ) ) && bad_var ) { \
if( !Nobad ) out_var[ off_out ] = badval; \
result++; \
\
/* Otherwise, calculate the variance estimate and store it, rounding \
to the nearest integer if necessary. */ \
} else { \
val = sum_var / ( wtsum * wtsum ); \
if ( Xfloating ) { \
out_var[ off_out ] = (Xtype) val; \
} else { \
out_var[ off_out ] = (Xtype) ( val + \
( ( val >= (Xfloattype) 0.0 ) ? \
( (Xfloattype) 0.5 ) : \
( (Xfloattype) -0.5 ) ) ); \
} \
} \
} \
}
/* This subsidiary macro tests for negative variance values in the
macros above. This check is required only for signed data types. */
#define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \
bad_var = bad_var || ( var < ( (Xtype) 0 ) );
/* Expand the main macro above to generate a function for each
required signed data type. */
#if HAVE_LONG_DOUBLE /* Not normally implemented */
MAKE_INTERPOLATE_LINEAR(LD,long double,1,long double,1)
MAKE_INTERPOLATE_LINEAR(L,long int,0,long double,1)
MAKE_INTERPOLATE_LINEAR(K,INT_BIG,0,long double,1)
#else
MAKE_INTERPOLATE_LINEAR(L,long int,0,double,1)
MAKE_INTERPOLATE_LINEAR(K,INT_BIG,0,double,1)
#endif
MAKE_INTERPOLATE_LINEAR(D,double,1,double,1)
MAKE_INTERPOLATE_LINEAR(F,float,1,float,1)
MAKE_INTERPOLATE_LINEAR(I,int,0,double,1)
MAKE_INTERPOLATE_LINEAR(S,short int,0,float,1)
MAKE_INTERPOLATE_LINEAR(B,signed char,0,float,1)
/* Re-define the macro for testing for negative variances to do
nothing. */
#undef CHECK_FOR_NEGATIVE_VARIANCE
#define CHECK_FOR_NEGATIVE_VARIANCE(Xtype)
/* Expand the main macro above to generate a function for each
required unsigned data type. */
#if HAVE_LONG_DOUBLE /* Not normally implemented */
MAKE_INTERPOLATE_LINEAR(UL,unsigned long int,0,long double,0)
MAKE_INTERPOLATE_LINEAR(UK,UINT_BIG,0,long double,0)
#else
MAKE_INTERPOLATE_LINEAR(UL,unsigned long int,0,double,0)
MAKE_INTERPOLATE_LINEAR(UK,UINT_BIG,0,double,0)
#endif
MAKE_INTERPOLATE_LINEAR(UI,unsigned int,0,double,0)
MAKE_INTERPOLATE_LINEAR(US,unsigned short int,0,float,0)
MAKE_INTERPOLATE_LINEAR(UB,unsigned char,0,float,0)
/* Undefine the macros uxsed above. */
#undef CHECK_FOR_NEGATIVE_VARIANCE
#undef CALC_AND_ASSIGN_OUTPUT
#undef FORM_LINEAR_INTERPOLATION_SUM
#undef ASSEMBLE_INPUT_ND
#undef ASSEMBLE_INPUT_2D
#undef ASSEMBLE_INPUT_1D
#undef MAKE_INTERPOLATE_LINEAR
/*
* Name:
* InterpolateNearest
* Purpose:
* Resample a data grid, using the nearest-pixel interpolation scheme.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* int InterpolateNearest( int ndim_in,
* const int *lbnd_in, const int *ubnd_in,
* const *in, const *in_var,
* int npoint, const int *offset,
* const double *const *coords,
* int flags, badval,
* *out, *out_var )
* Class Membership:
* Mapping member function.
* Description:
* This is a set of functions which resample a rectangular input
* grid of data (and, optionally, associated statistical variance
* values) so as to place them into a new output grid. Each output
* grid point may be mapped on to a position in the input grid in
* an arbitrary way. Where the positions given do not correspond
* with a pixel centre in the input grid, the interpolation scheme
* used is simply to select the nearest pixel (i.e. the one whose
* bounds contain the supplied position).
* Parameters:
* ndim_in
* The number of dimensions in the input grid. This should be at
* least one.
* lbnd_in
* Pointer to an array of integers, with "ndim_in" elements.
* This should give the coordinates of the centre of the first
* pixel in the input grid along each dimension.
* ubnd_in
* Pointer to an array of integers, with "ndim_in" elements.
* This should give the coordinates of the centre of the last
* pixel in the input grid along each dimension.
*
* Note that "lbnd_in" and "ubnd_in" together define the shape
* and size of the input grid, its extent along a particular
* (i'th) dimension being ubnd_in[i]-lbnd_in[i]+1 (assuming "i"
* is zero-based). They also define the input grid's coordinate
* system, with each pixel being of unit extent along each
* dimension with integral coordinate values at its centre.
* in
* Pointer to the array of data to be resampled (with an element
* for each pixel in the input grid). The numerical type of
* these data should match the function used, as given by the
* suffix on the function name. The storage order should be such
* that the index of the first grid dimension varies most
* rapidly and that of the final dimension least rapidly
* (i.e. Fortran array storage order).
* in_var
* An optional pointer to a second array of positive numerical
* values (with the same size and type as the "in" array), which
* represent estimates of the statistical variance associated
* with each element of the "in" array. If this second array is
* given (along with the corresponding "out_var" array), then
* estimates of the variance of the resampled data will also be
* returned.
*
* If no variance estimates are required, a NULL pointer should
* be given.
* npoint
* The number of points at which the input grid is to be
* resampled.
* offset
* Pointer to an array of integers with "npoint" elements. For
* each output point, this array should contain the zero-based
* offset in the output array(s) (i.e. the "out" and,
* optionally, the "out_var" arrays) at which the resampled
* output value(s) should be stored.
* coords
* An array of pointers to double, with "ndim_in"
* elements. Element "coords[coord]" should point at the first
* element of an array of double (with "npoint" elements) which
* contains the values of coordinate number "coord" for each
* interpolation point. The value of coordinate number "coord"
* for interpolation point number "point" is therefore given by
* "coords[coord][point]" (assuming both indices to be
* zero-based). If any point has a coordinate value of AST__BAD
* associated with it, then the corresponding output data (and
* variance) will be set to the value given by "badval" (unles the
* AST__NOBAD flag is specified).
* flags
* The bitwise OR of a set of flag values which control the
* operation of the function. Currently, only the flag
* AST__USEBAD is significant and indicates whether there are
* "bad" (i.e. missing) data in the input array(s) which must be
* recognised and propagated to the output array(s). If this
* flag is not set, all input values are treated literally.
* badval
* If the AST__USEBAD flag is set in the "flags" value (above),
* this parameter specifies the value which is used to identify
* bad data and/or variance values in the input array(s). Its
* numerical type must match that of the "in" (and "in_var")
* arrays. Unles the AST__NOBAD flag is specified in "flags", the
* same value will also be used to flag any output array elements
* for which resampled values could not be obtained. The output
* arrays(s) may be flagged with this value whether or not the
* AST__USEBAD flag is set (the function return value indicates
* whether any such values have been produced).
* out
* Pointer to an array with the same data type as the "in"
* array, into which the resampled data will be returned. Note
* that details of how the output grid maps on to this array
* (e.g. the storage order, number of dimensions, etc.) is
* arbitrary and is specified entirely by means of the "offset"
* array. The "out" array should therefore contain sufficient
* elements to accommodate the "offset" values supplied. There
* is no requirement that all elements of the "out" array should
* be assigned values, and any which are not addressed by the
* contents of the "offset" array will be left unchanged.
* out_var
* An optional pointer to an array with the same data type and
* size as the "out" array, into which variance estimates for
* the resampled values may be returned. This array will only be
* used if the "in_var" array has been given. It is addressed in
* exactly the same way (via the "offset" array) as the "out"
* array. The values returned are estimates of the statistical
* variance of the corresponding values in the "out" array, on
* the assumption that all errors in input grid values (in the
* "in" array) are statistically independent and that their
* variance estimates (in the "in_var" array) may simply be
* summed (with appropriate weighting factors).
*
* If no output variance estimates are required, a NULL pointer
* should be given.
* Returned Value:
* The number of output grid points to which a data value (or a
* variance value if relevant) equal to "badval" has been assigned
* because no valid output value could be obtained.
* Notes:
* - There is a separate function for each numerical type of
* gridded data, distinguished by replacing the in the function
* name by the appropriate 1- or 2-character suffix.
* - A value of zero will be returned if any of these functions is
* invoked with the global error status set, or if it should fail
* for any reason.
*/
/* Define a macro to implement the function for a specific data
type. */
#define MAKE_INTERPOLATE_NEAREST(X,Xtype,Xsigned) \
static int InterpolateNearest##X( int ndim_in, \
const int *lbnd_in, const int *ubnd_in, \
const Xtype *in, const Xtype *in_var, \
int npoint, const int *offset, \
const double *const *coords, \
int flags, Xtype badval, \
Xtype *out, Xtype *out_var, int *status ) { \
\
/* Local Variables: */ \
Xtype var; /* Variance value */ \
double *xn_max; /* Pointer to upper limits array (n-d) */ \
double *xn_min; /* Pointer to lower limits array (n-d) */ \
double x; /* x coordinate value */ \
double xmax; /* x upper limit */ \
double xmin; /* x lower limit */ \
double xn; /* Coordinate value (n-d) */ \
double y; /* y coordinate value */ \
double ymax; /* y upper limit */ \
double ymin; /* y lower limit */ \
int *stride; /* Pointer to array of dimension strides */ \
int bad; /* Output pixel bad? */ \
int idim; /* Loop counter for dimensions */ \
int ix; /* Number of pixels offset in x direction */ \
int ixn; /* Number of pixels offset (n-d) */ \
int iy; /* Number of pixels offset in y direction */ \
int nobad; /* Was the AST__NOBAD flag set? */ \
int off_in; /* Pixel offset into input array */ \
int off_out; /* Pixel offset into output array */ \
int point; /* Loop counter for output points */ \
int result; /* Returned result value */ \
int s; /* Temporary variable for strides */ \
int usebad; /* Use "bad" input pixel values? */ \
int usevar; /* Process variance array? */ \
int ystride; /* Stride along input grid y direction */ \
\
/* Initialise. */ \
result = 0; \
\
/* Check the global error status. */ \
if ( !astOK ) return result; \
\
/* Initialise variables to avoid "used of uninitialised variable" \
messages from dumb compilers. */ \
bad = 0; \
off_in = 0; \
\
/* Determine if we are processing bad pixels or variances. */ \
nobad = flags & AST__NOBAD; \
usebad = flags & AST__USEBAD; \
usevar = in_var && out_var; \
\
/* Handle the 1-dimensional case optimally. */ \
/* ---------------------------------------- */ \
if ( ndim_in == 1 ) { \
\
/* Calculate the coordinate limits of the input array. */ \
xmin = (double) lbnd_in[ 0 ] - 0.5; \
xmax = (double) ubnd_in[ 0 ] + 0.5; \
\
/* Identify four cases, according to whether bad pixels and/or \
variances are being processed. In each case, loop through all the \
output points to (a) assemble the input data needed to form the \
interpolated value, and (b) calculate the result and assign it to \
the output arrays(s). In each case we assign constant values (0 or \
1) to the "Usebad" and "Usevar" flags so that code for handling bad \
pixels and variances can be eliminated when not required. */ \
if ( nobad ) { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,1) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,1) \
} \
} \
} \
\
/* Four more cases as above, but without the AST__NOBAD flag. */ \
} else { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,0) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,0) \
} \
} \
} \
} \
\
/* Handle the 2-dimensional case optimally. */ \
/* ---------------------------------------- */ \
} else if ( ndim_in == 2 ) { \
\
/* Calculate the stride along the y dimension of the input grid. */ \
ystride = ubnd_in[ 0 ] - lbnd_in[ 0 ] + 1; \
\
/* Calculate the coordinate limits of the input array in each \
dimension. */ \
xmin = (double) lbnd_in[ 0 ] - 0.5; \
xmax = (double) ubnd_in[ 0 ] + 0.5; \
ymin = (double) lbnd_in[ 1 ] - 0.5; \
ymax = (double) ubnd_in[ 1 ] + 0.5; \
\
/* Identify four cases, according to whether bad pixels and/or \
variances are being processed. In each case, loop through all the \
output points to (a) assemble the input data needed to form the \
interpolated value, and (b) calculate the result and assign it to \
the output arrays(s). In each case we assign constant values (0 or \
1) to the "Usebad" and "Usevar" flags so that code for handling bad \
pixels and variances can be eliminated when not required. */ \
if ( nobad ) { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,1) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,1) \
} \
} \
} \
\
/* Four more cases as above, but without the AST__NOBAD flag. */ \
} else { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,0) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,0) \
} \
} \
} \
} \
\
/* Handle other numbers of dimensions. */ \
/* ----------------------------------- */ \
} else { \
\
/* Allocate workspace. */ \
stride = astMalloc( sizeof( int ) * (size_t) ndim_in ); \
xn_max = astMalloc( sizeof( double ) * (size_t) ndim_in ); \
xn_min = astMalloc( sizeof( double ) * (size_t) ndim_in ); \
if ( astOK ) { \
\
/* Calculate the stride along each dimension of the input grid. */ \
for ( s = 1, idim = 0; idim < ndim_in; idim++ ) { \
stride[ idim ] = s; \
s *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \
\
/* Calculate the coordinate limits of the input grid in each \
dimension. */ \
xn_min[ idim ] = (double) lbnd_in[ idim ] - 0.5; \
xn_max[ idim ] = (double) ubnd_in[ idim ] + 0.5; \
} \
\
/* Identify four cases, according to whether bad pixels and/or \
variances are being processed. In each case, loop through all the \
output points to (a) assemble the input data needed to form the \
interpolated value, and (b) calculate the result and assign it to \
the output arrays(s). In each case we assign constant values (0 or \
1) to the "Usebad" and "Usevar" flags so that code for handling bad \
pixels and variances can be eliminated when not required. */ \
if ( nobad ) { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,1) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,1) \
} \
} \
} \
\
/* Another 4 cases as above, but without the AST__NOBAD flag. */ \
} else { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,0) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,0) \
} \
} \
} \
} \
} \
\
/* Free the workspace. */ \
stride = astFree( stride ); \
xn_max = astFree( xn_max ); \
xn_min = astFree( xn_min ); \
} \
\
/* If an error has occurred, clear the returned result. */ \
if ( !astOK ) result = 0; \
\
/* Return the result. */ \
return result; \
}
/* This subsidiary macro assembles the input data needed in
preparation for forming the interpolated value in the 1-dimensional
case. */
#define ASSEMBLE_INPUT_1D(X,Xtype,Usebad,Usevar) \
\
/* Obtain the x coordinate of the current point and test if it lies \
outside the input grid, or is bad. */ \
x = coords[ 0 ][ point ]; \
bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \
if ( !bad ) { \
\
/* If not, then obtain the offset within the input grid of the pixel \
which contains the current point. */ \
off_in = (int) floor( x + 0.5 ) - lbnd_in[ 0 ]; \
\
/* If necessary, test if the input pixel is bad. */ \
if ( Usebad ) bad = ( in[ off_in ] == badval ); \
}
/* This subsidiary macro assembles the input data needed in
preparation for forming the interpolated value in the 2-dimensional
case. */
#define ASSEMBLE_INPUT_2D(X,Xtype,Usebad,Usevar) \
\
/* Obtain the x coordinate of the current point and test if it lies \
outside the input grid, or is bad. */ \
x = coords[ 0 ][ point ]; \
bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \
if ( !bad ) { \
\
/* If not, then similarly obtain and test the y coordinate. */ \
y = coords[ 1 ][ point ]; \
bad = ( y < ymin ) || ( y >= ymax ) || ( y == AST__BAD ); \
if ( !bad ) { \
\
/* Obtain the offsets along each input grid dimension of the input \
pixel which contains the current point. */ \
ix = (int) floor( x + 0.5 ) - lbnd_in[ 0 ]; \
iy = (int) floor( y + 0.5 ) - lbnd_in[ 1 ]; \
\
/* Calculate this pixel's offset from the start of the input array. */ \
off_in = ix + ystride * iy; \
\
/* If necessary, test if the input pixel is bad. */ \
if ( Usebad ) bad = ( in[ off_in ] == badval ); \
} \
}
/* This subsidiary macro assembles the input data needed in
preparation for forming the interpolated value in the n-dimensional
case. */
#define ASSEMBLE_INPUT_ND(X,Xtype,Usebad,Usevar) \
\
/* Initialise the offset into the input array. Then loop to obtain \
each coordinate associated with the current output point. */ \
off_in = 0; \
for ( idim = 0; idim < ndim_in; idim++ ) { \
xn = coords[ idim ][ point ]; \
\
/* Test if the coordinate lies outside the input grid, or is bad. If \
either is true, the corresponding output pixel value will be bad, \
so give up on this point. */ \
bad = ( xn < xn_min[ idim ] ) || ( xn >= xn_max[ idim ] ) || \
( xn == AST__BAD ); \
if ( bad ) break; \
\
/* Obtain the offset along the current input grid dimension of the \
input pixel which contains the current point. */ \
ixn = (int) floor( xn + 0.5 ) - lbnd_in[ idim ]; \
\
/* Accumulate this pixel's offset from the start of the input \
array. */ \
off_in += ixn * stride[ idim ]; \
} \
\
/* Once the required input pixel has been located, test if it is \
bad, if necessary. */ \
if ( Usebad ) bad = bad || ( in[ off_in ] == badval );
/* This subsidiary macro assigns the output value (and variance) to
the output array(s). */
#define CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,Usebad,Usevar,Nobad) \
\
/* Obtain the pixel offset into the output array. */ \
off_out = offset[ point ]; \
\
/* If the input data value is bad, assign a bad output value (and \
variance, if required) and count it. */ \
if ( bad ) { \
if( !Nobad ) { \
out[ off_out ] = badval; \
if ( Usevar ) out_var[ off_out ] = badval; \
} \
result++; \
\
/* Otherwise, assign the value obtained from the input grid. */ \
} else { \
out[ off_out ] = in[ off_in ]; \
\
/* If required, obtain the associated variance value. If necessary, \
test if it is bad (if the data type is signed, also check that it \
is not negative). */ \
if ( Usevar ) { \
var = in_var[ off_in ]; \
if ( Usebad ) bad = ( var == badval ); \
CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \
\
/* If the variance value can be bad, and is, then store a bad value in \
the output array and count it. Otherwise, store the variance \
value. */ \
if ( ( ( Xsigned ) || ( Usebad ) ) && bad ) { \
if( !Nobad ) out_var[ off_out ] = badval; \
result++; \
} else { \
out_var[ off_out ] = var; \
} \
} \
}
/* This subsidiary macro tests for negative variance values in the
macros above. This check is required only for signed data
types. */
#define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \
bad = bad || ( var < ( (Xtype) 0 ) );
/* Expand the main macro above to generate a function for each
required signed data type. */
#if HAVE_LONG_DOUBLE /* Not normally implemented */
MAKE_INTERPOLATE_NEAREST(LD,long double,1)
#endif
MAKE_INTERPOLATE_NEAREST(D,double,1)
MAKE_INTERPOLATE_NEAREST(F,float,1)
MAKE_INTERPOLATE_NEAREST(L,long int,1)
MAKE_INTERPOLATE_NEAREST(K,INT_BIG,1)
MAKE_INTERPOLATE_NEAREST(I,int,1)
MAKE_INTERPOLATE_NEAREST(S,short int,1)
MAKE_INTERPOLATE_NEAREST(B,signed char,1)
/* Re-define the macro for testing for negative variances to do
nothing. */
#undef CHECK_FOR_NEGATIVE_VARIANCE
#define CHECK_FOR_NEGATIVE_VARIANCE(Xtype)
/* Expand the main macro above to generate a function for each
required unsigned data type. */
MAKE_INTERPOLATE_NEAREST(UK,UINT_BIG,0)
MAKE_INTERPOLATE_NEAREST(UL,unsigned long int,0)
MAKE_INTERPOLATE_NEAREST(UI,unsigned int,0)
MAKE_INTERPOLATE_NEAREST(US,unsigned short int,0)
MAKE_INTERPOLATE_NEAREST(UB,unsigned char,0)
/* Undefine the macros used above. */
#undef CHECK_FOR_NEGATIVE_VARIANCE
#undef CALC_AND_ASSIGN_OUTPUT
#undef ASSEMBLE_INPUT_ND
#undef ASSEMBLE_INPUT_2D
#undef ASSEMBLE_INPUT_1D
#undef MAKE_INTERPOLATE_NEAREST
/*
* Name:
* InterpolateBlockAverage
* Purpose:
* Resample a data grid, using multidimensional block averaging.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* void InterpolateBlockAverage( int ndim_in,
* const int lbnd_in[],
* const int ubnd_in[],
* const in[],
* const in_var[],
* int npoint, const int offset[],
* const double *const coords[],
* const double params[], int flags,
* badval, *out,
* *out_var, int *nbad )
* Class Membership:
* Mapping member function.
* Description:
* This is a set of functions which resample a rectangular input
* grid of data (and, optionally, associated statistical variance
* values) so as to place them into a new output grid. To generate
* an output grid pixel, a block average is taken over an ndim-
* dimensional hypercube of pixels in the input grid. If variances
* are being used then the input pixels will be weighted according
* to the reciprocals of the corresponding variance values, and
* input pixels without a valid variance will be ignored;
* otherwise an unweighted average will be taken over
* all non-bad pixels in the cube. The size of the cube over which
* the average is taken is determined by the first element of the
* params array.
*
* This "interpolation" scheme is appropriate where an input grid
* is to be resampled onto a much coarser output grid.
* Parameters:
* ndim_in
* The number of dimensions in the input grid. This should be at
* least one.
* lbnd_in
* Pointer to an array of integers, with "ndim_in" elements.
* This should give the coordinates of the centre of the first
* pixel in the input grid along each dimension.
* ubnd_in
* Pointer to an array of integers, with "ndim_in" elements.
* This should give the coordinates of the centre of the last
* pixel in the input grid along each dimension.
*
* Note that "lbnd_in" and "ubnd_in" together define the shape
* and size of the input grid, its extent along a particular
* (i'th) dimension being ubnd_in[i]-lbnd_in[i]+1 (assuming "i"
* is zero-based). They also define the input grid's coordinate
* system, with each pixel being of unit extent along each
* dimension with integral coordinate values at its centre.
* in
* Pointer to the array of data to be resampled (with an element
* for each pixel in the input grid). The numerical type of
* these data should match the function used, as given by the
* suffix on the function name. The storage order should be such
* that the index of the first grid dimension varies most
* rapidly and that of the final dimension least rapidly
* (i.e. Fortran array storage order).
* in_var
* An optional pointer to a second array of positive numerical
* values (with the same size and type as the "in" array), which
* represent estimates of the statistical variance associated
* with each element of the "in" array. If this second array is
* given (along with the corresponding "out_var" array), then
* estimates of the variance of the resampled data will also be
* returned.
*
* If no variance estimates are required, a NULL pointer should
* be given.
* npoint
* The number of points at which the input grid is to be
* resampled.
* offset
* Pointer to an array of integers with "npoint" elements. For
* each output point, this array should contain the zero-based
* offset in the output array(s) (i.e. the "out" and,
* optionally, the "out_var" arrays) at which the resampled
* output value(s) should be stored.
* coords
* An array of pointers to double, with "ndim_in"
* elements. Element "coords[coord]" should point at the first
* element of an array of double (with "npoint" elements) which
* contains the values of coordinate number "coord" for each
* interpolation point. The value of coordinate number "coord"
* for interpolation point number "point" is therefore given by
* "coords[coord][point]" (assuming both indices to be
* zero-based). If any point has a coordinate value of AST__BAD
* associated with it, then the corresponding output data (and
* variance) will be set to the value given by "badval" (unles the
* AST__NOBAD flag is specified).
* params
* A pointer to an array of doubles giving further information
* about how the resampling is to proceed. Only the first
* element is significant; the nearest integer to this gives
* the number of pixels on either side of the central input
* grid pixel to use in each dimension. Therefore
* (1 + 2*params[0])**ndim_in pixels will be averaged over to
* generate each output pixel.
* flags
* The bitwise OR of a set of flag values which control the
* operation of the function. Currently, only the flag
* AST__USEBAD is significant and indicates whether there are
* "bad" (i.e. missing) data in the input array(s) which must be
* recognised and propagated to the output array(s). If this
* flag is not set, all input values are treated literally.
* badval
* If the AST__USEBAD flag is set in the "flags" value (above),
* this parameter specifies the value which is used to identify
* bad data and/or variance values in the input array(s). Its
* numerical type must match that of the "in" (and "in_var")
* arrays. Unles the AST__NOBAD flag is specified in "flags", the
* same value will also be used to flag any output array elements
* for which resampled values could not be obtained. The output
* arrays(s) may be flagged with this value whether or not the
* AST__USEBAD flag is set (the function return value indicates
* whether any such values have been produced).
* out
* Pointer to an array with the same data type as the "in"
* array, into which the resampled data will be returned. Note
* that details of how the output grid maps on to this array
* (e.g. the storage order, number of dimensions, etc.) is
* arbitrary and is specified entirely by means of the "offset"
* array. The "out" array should therefore contain sufficient
* elements to accommodate the "offset" values supplied. There
* is no requirement that all elements of the "out" array should
* be assigned values, and any which are not addressed by the
* contents of the "offset" array will be left unchanged.
* out_var
* An optional pointer to an array with the same data type and
* size as the "out" array, into which variance estimates for
* the resampled values may be returned. This array will only be
* used if the "in_var" array has been given. It is addressed in
* exactly the same way (via the "offset" array) as the "out"
* array. The values returned are estimates of the statistical
* variance of the corresponding values in the "out" array, on
* the assumption that all errors in input grid values (in the
* "in" array) are statistically independent and that their
* variance estimates (in the "in_var" array) may simply be
* summed (with appropriate weighting factors).
*
* If no output variance estimates are required, a NULL pointer
* should be given.
* nbad
* Pointer to an int in which to return the number of
* interpolation points at which an output data value (and/or a
* variance value if relevant) equal to "badval" has been
* assigned because no valid interpolated value could be
* obtained. The maximum value that will be returned is
* "npoint" and the minimum is zero (indicating that all output
* values were successfully obtained).
* Notes:
* - There is a separate function for each numerical type of
* gridded data, distinguished by replacing the in the function
* name by the appropriate 1- or 2-character suffix.
*/
/* Define a macro to implement the function for a specific data
type. */
#define MAKE_INTERPOLATE_BLOCKAVE(X,Xtype,Xfloating,Xfloattype,Xsigned) \
static void InterpolateBlockAverage##X( int ndim_in, \
const int lbnd_in[], \
const int ubnd_in[], \
const Xtype in[], \
const Xtype in_var[], \
int npoint, const int offset[], \
const double *const coords[], \
const double params[], int flags, \
Xtype badval, Xtype *out, \
Xtype *out_var, int *nbad ) { \
\
/* Local Variables: */ \
Xfloattype hi_lim; /* Upper limit on output values */ \
Xfloattype lo_lim; /* Lower limit on output values */ \
Xfloattype pixwt; /* Weight to apply to individual pixel */ \
Xfloattype sum; /* Weighted sum of pixel data values */ \
Xfloattype sum_var; /* Weighted sum of pixel variance values */ \
Xfloattype val; /* Data value to be assigned to output */ \
Xfloattype val_var; /* Variance to be assigned to output */ \
Xfloattype wtsum; /* Sum of weight values */ \
Xfloattype wtsum_sq; /* Square of sum of weights */ \
Xtype var; /* Variance value */ \
double *xn_max; /* Pointer to upper limits array (n-d) */ \
double *xn_min; /* Pointer to lower limits array (n-d) */ \
double x; /* x coordinate value */ \
double xn; /* Coordinate value (n-d) */ \
double y; /* y coordinate value */ \
int *hi; /* Pointer to array of upper indices */ \
int *ixm; /* Pointer to array of current indices */ \
int *lo; /* Pointer to array of lower indices */ \
int *status; /* Pointer to inherited status value */ \
int *stride; /* Pointer to array of dimension strides */ \
int bad; /* Output pixel bad? */ \
int bad_var; /* Output variance bad? */ \
int done; /* All pixel indices done? */ \
int hi_x; /* Upper pixel index (x dimension) */ \
int hi_y; /* Upper pixel index (y dimension) */ \
int idim; /* Loop counter for dimensions */ \
int ix; /* Pixel index in input grid x dimension */ \
int ixn; /* Pixel index in input grid (n-d) */ \
int iy; /* Pixel index in input grid y dimension */ \
int lo_x; /* Lower pixel index (x dimension) */ \
int lo_y; /* Lower pixel index (y dimension) */ \
int neighb; /* Number of adjacent pixels on each side */ \
int nobad; /* Was the AST__NOBAD flag set? */ \
int off1; /* Input pixel offset due to y index */ \
int off_in; /* Offset to input pixel */ \
int off_out; /* Offset to output pixel */ \
int point; /* Loop counter for output points */ \
int s; /* Temporary variable for strides */ \
int usebad; /* Use "bad" input pixel values? */ \
int usevar; /* Process variance array? */ \
int ystride; /* Stride along input grid y dimension */ \
\
/* Initialise. */ \
*nbad = 0; \
\
/* Get a pointer to the inherited status argument. */ \
status = astGetStatusPtr; \
\
/* Check the global error status. */ \
if ( !astOK ) return; \
\
/* Initialise variables to avoid "used of uninitialised variable" \
messages from dumb compilers. */ \
val = 0; \
val_var = 0; \
sum_var = 0; \
wtsum = 0; \
bad = 0; \
bad_var = 0; \
\
/* Determine if we are processing bad pixels or variances. */ \
nobad = flags & AST__NOBAD; \
usebad = flags & AST__USEBAD; \
usevar = in_var && out_var; \
\
/* Set the number of pixels each side of central pixel to use. */ \
neighb = (int) floor( params[ 0 ] + 0.5 ); \
\
/* Set up limits for checking output values to ensure that they do not \
overflow the range of the data type being used. */ \
lo_lim = LO_##X; \
hi_lim = HI_##X; \
\
/* Handle the 1-dimensional case optimally. */ \
/* ---------------------------------------- */ \
if ( ndim_in == 1 ) { \
\
/* Identify four cases, according to whether bad pixels and/or \
variances are being processed. In each case, loop through all the \
output points to (a) assemble the input data needed to form the \
interpolated value, and (b) calculate the result and assign it to \
the output arrays(s). In each case we assign constant values (0 or \
1) to the "Usebad" and "Usevar" flags so that code for handling bad \
pixels and variances can be eliminated when not required. */ \
if ( nobad ) { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \
} \
} \
} \
\
/* Another 4 cases as above, but without the AST__NOBAD flag. */ \
} else { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \
} \
} \
} \
} \
\
/* Handle the 2-dimensional case optimally. */ \
/* ---------------------------------------- */ \
} else if ( ndim_in == 2 ) { \
\
/* Calculate the stride along the y dimension of the input grid. */ \
ystride = ubnd_in[ 0 ] - lbnd_in[ 0 ] + 1; \
\
/* Identify four cases, according to whether bad pixels and/or \
variances are being processed. In each case, loop through all the \
output points to (a) assemble the input data needed to form the \
interpolated value, and (b) calculate the result and assign it to \
the output arrays(s). In each case we assign constant values (0 or \
1) to the "Usebad" and "Usevar" flags so that code for handling bad \
pixels and variances can be eliminated when not required. */ \
if ( nobad ) { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \
} \
} \
} \
\
/* Another 4 cases as above, but without the AST__NOBAD flag. */ \
} else { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \
} \
} \
} \
} \
\
/* Handle other numbers of dimensions. */ \
/* ----------------------------------- */ \
} else { \
\
/* Allocate workspace. */ \
hi = astMalloc( sizeof( int ) * (size_t) ndim_in ); \
lo = astMalloc( sizeof( int ) * (size_t) ndim_in ); \
stride = astMalloc( sizeof( int ) * (size_t) ndim_in ); \
ixm = astMalloc( sizeof( int ) * (size_t) ndim_in ); \
xn_max = astMalloc( sizeof( double ) * (size_t) ndim_in ); \
xn_min = astMalloc( sizeof( double ) * (size_t) ndim_in ); \
if ( astOK ) { \
\
/* Calculate the stride along each dimension of the input grid. */ \
for ( s = 1, idim = 0; idim < ndim_in; idim++ ) { \
stride[ idim ] = s; \
s *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \
\
/* Calculate the coordinate limits of the input grid in each \
dimension. */ \
xn_min[ idim ] = (double) lbnd_in[ idim ] - 0.5; \
xn_max[ idim ] = (double) ubnd_in[ idim ] + 0.5; \
} \
\
/* Identify four cases, according to whether bad pixels and/or \
variances are being processed. In each case, loop through all the \
output points to (a) assemble the input data needed to form the \
interpolated value, and (b) calculate the result and assign it to \
the output arrays(s). In each case we assign constant values (0 or \
1) to the "Usebad" and "Usevar" flags so that code for handling bad \
pixels and variances can be eliminated when not required. */ \
if ( nobad ) { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \
} \
} \
} \
\
/* Another 4 cases as above, but this time without the AST__NOBAD flag. */ \
} else { \
if ( usebad ) { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \
} \
} \
} else { \
if ( usevar ) { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \
} \
} else { \
for ( point = 0; point < npoint; point++ ) { \
ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \
CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \
} \
} \
} \
} \
} \
\
/* Free the workspace. */ \
hi = astFree( hi ); \
lo = astFree( lo ); \
stride = astFree( stride ); \
ixm = astFree( ixm ); \
xn_max = astFree( xn_max ); \
xn_min = astFree( xn_min ); \
} \
\
/* If an error has occurred, clear the returned result. */ \
if ( !astOK ) *nbad = 0; \
\
/* Return. */ \
}
/* This subsidiary macro assembles the input data needed in
preparation for forming the interpolated value in the 1-dimensional
case. */
#define ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \
\
/* Obtain the x coordinate of the current point and test if it is bad. */ \
x = coords[ 0 ][ point ]; \
bad = ( x == AST__BAD ); \
\
/* Note we do not need to check here whether the pixel in this position is \
bad; if any pixels in the cube are good we can form an average. */ \
\
/* If OK, calculate the lowest and highest indices (in the x \
dimension) of the region of neighbouring pixels that will \
contribute to the interpolated result. Constrain these values to \
lie within the input grid. */ \
if ( !bad ) { \
ix = (int) floor( x ); \
lo_x = MaxI( ix - neighb + 1, lbnd_in[ 0 ], status ); \
hi_x = MinI( ix + neighb, ubnd_in[ 0 ], status ); \
\
/* Initialise sums for forming the interpolated result. */ \
sum = (Xfloattype) 0.0; \
wtsum = (Xfloattype) 0.0; \
if ( Usevar ) { \
sum_var = (Xfloattype) 0.0; \
bad_var = 0; \
} \
\
/* Loop to inspect all the contributing pixels, calculating the offset \
of each pixel from the start of the input array. */ \
off_in = lo_x - lbnd_in[ 0 ]; \
for ( ix = lo_x; ix <= hi_x; ix++, off_in++ ) { \
\
/* If necessary, test if the input pixel is bad. */ \
if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \
\
/* If we are using variances, then check that the variance is valid; \
if it is invalid then ignore this pixel altogether. */ \
if ( Usevar ) { \
var = in_var[ off_in ]; \
if ( Usebad ) bad_var = ( var == badval ); \
CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \
\
/* If variance is valid then accumulate suitably weighted values into \
the totals. */ \
if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \
pixwt = (Xfloattype) 1.0 / var; \
sum += pixwt * ( (Xfloattype) in[ off_in ] ); \
wtsum += pixwt; \
sum_var += pixwt; \
} \
\
/* If we are not using variances, then accumulate values into the \
totals with a weighting of unity. */ \
} else { \
sum += (Xfloattype) in[ off_in ]; \
wtsum++; \
} \
} \
} \
}
/* This subsidiary macro assembles the input data needed in
preparation for forming the interpolated value in the 2-dimensional
case. */
#define ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \
\
/* Obtain the x coordinate of the current point and test if it is bad. */ \
x = coords[ 0 ][ point ]; \
bad = ( x == AST__BAD ); \
if ( !bad ) { \
\
/* If not, then similarly obtain and test the y coordinate. */ \
y = coords[ 1 ][ point ]; \
bad = ( y == AST__BAD ); \
\
/* Note we do not need to check here whether the pixel in this position is \
bad; if any pixels in the cube are good we can form an average. */ \
\
/* If OK, calculate the lowest and highest indices (in each dimension) \
of the region of neighbouring pixels that will contribute to the \
interpolated result. Constrain these values to lie within the input \
grid. */ \
if ( !bad ) { \
ix = (int) floor( x ); \
lo_x = MaxI( ix - neighb + 1, lbnd_in[ 0 ], status ); \
hi_x = MinI( ix + neighb, ubnd_in[ 0 ], status ); \
iy = (int) floor( y ); \
lo_y = MaxI( iy - neighb + 1, lbnd_in[ 1 ], status ); \
hi_y = MinI( iy + neighb, ubnd_in[ 1 ], status ); \
\
/* Initialise sums for forming the interpolated result. */ \
sum = (Xfloattype) 0.0; \
wtsum = (Xfloattype) 0.0; \
if ( Usevar ) { \
sum_var = (Xfloattype) 0.0; \
bad_var = 0; \
} \
\
/* Loop to inspect all the contributing pixels, calculating the offset \
of each pixel from the start of the input array. */ \
off1 = lo_x - lbnd_in[ 0 ] + ystride * ( lo_y - lbnd_in[ 1 ] ); \
for ( iy = lo_y; iy <= hi_y; iy++, off1 += ystride ) { \
off_in = off1; \
for ( ix = lo_x; ix <= hi_x; ix++, off_in++ ) { \
\
/* If necessary, test if the input pixel is bad. */ \
if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \
\
/* If we are using variances, then check that the variance is valid; \
if it is invalid then ignore this pixel altogether. */ \
if ( Usevar ) { \
var = in_var[ off_in ]; \
if ( Usebad ) bad_var = ( var == badval ); \
CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \
\
/* If variance is valid then accumulate suitably weighted values into \
the totals. */ \
if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \
pixwt = (Xfloattype) 1.0 / var; \
sum += pixwt * ( (Xfloattype) in[ off_in ] ); \
wtsum += pixwt; \
sum_var += pixwt; \
} \
\
/* If we are not using variances, then accumulate values into the \
totals with a weighting of unity. */ \
} else { \
sum += (Xfloattype) in[ off_in ]; \
wtsum++; \
} \
} \
} \
} \
} \
}
/* This subsidiary macro assembles the input data needed in
preparation for forming the interpolated value in the n-dimensional
case. */
#define ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \
\
/* Initialise offsets into the input array. then loop to obtain each \
coordinate associated with the current output poitn. */ \
off_in = 0; \
for ( idim = 0; idim < ndim_in; idim++ ) { \
xn = coords[ idim ][ point ]; \
\
/* Test if the coordinate is bad. If so give up on this point. */ \
bad = ( xn == AST__BAD ); \
if ( bad ) break; \
\
/* Calculate the lowest and highest indices (in the current dimension) \
of the region of neighbouring pixels that will contribute to the \
interpolated result. Constrain these values to lie within the input \
grid. */ \
ixn = (int) floor( xn ); \
lo[ idim ] = MaxI( ixn - neighb + 1, lbnd_in[ idim ], status ); \
hi[ idim ] = MinI( ixn + neighb, ubnd_in[ idim ], status ); \
\
/* If the cube has a zero dimension then no data can come from it. */ \
bad = ( lo[ idim ] > hi[ idim ] ); \
if ( bad ) break; \
\
/* Accumulate the offset (from the start of the input array) of the \
contributing pixel which has the lowest index in each dimension. */ \
off_in += stride[ idim ] * ( lo[ idim ] - lbnd_in[ idim ] ); \
\
/* Initialise an array to keep track of the current position in the \
input cube. */ \
ixm[ idim ] = lo[ idim ]; \
} \
\
/* Note we do not need to check here whether the pixel in this position is \
bad; if any pixels in the cube are good we can form an average. */ \
\
/* If OK, initialise sums for forming the interpolated result. */ \
if ( !bad ) { \
sum = (Xfloattype) 0.0; \
wtsum= (Xfloattype) 0.0; \
if ( Usevar ) { \
sum_var = (Xfloattype) 0.0; \
bad_var = 0; \
} \
\
/* Loop to inspect all the contributing pixels, calculating the offset \
of each pixel from the start of the input array. */ \
do { \
\
/* If necessary, test if the input pixel is bad. */ \
if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \
\
/* If we are using variances, then check that the variance is valid; \
if it is invalid then ignore this pixel altogether. */ \
if ( Usevar ) { \
var = in_var[ off_in ]; \
if ( Usebad ) bad_var = ( var == badval ); \
CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \
\
/* If variance is valid then accumulate suitably weighted values into \
the totals. */ \
if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \
pixwt = (Xfloattype) 1.0 / var; \
sum += pixwt * ( (Xfloattype) in[ off_in ] ); \
wtsum += pixwt; \
sum_var += pixwt; \
} \
\
/* If we are not using variances, then accumulate values into the \
totals with a weighting of unity. */ \
} else { \
sum += (Xfloattype) in[ off_in ]; \
wtsum++; \
} \
} \
\
/* Locate the next pixel in the input cube; try incrementing the lowest \
dimension index first, if that rolls over increment the next \
dimension index, and so on. */ \
for ( idim = 0; idim < ndim_in; idim++ ) { \
if ( ixm[ idim ] < hi[ idim ] ) { \
off_in += stride[ idim ]; \
ixm[ idim ]++; \
break; \
} else { \
off_in -= stride[ idim ] * ( hi[ idim ] - lo[ idim ] ); \
ixm[ idim ] = lo[ idim ]; \
} \
} \
\
/* If the highest dimension index has rolled over, we have done all \
the pixels in the cube. */ \
done = ( idim == ndim_in ); \
} while ( !done ); \
}
/* This subsidiary macro calculates the interpolated output value (and
variance) from the sums over contributing pixels, checks the
results for validity, and assigns them to the output array(s). */
#define CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Usebad,Usevar,Nobad) \
\
/* If the output data value has not yet been flagged as bad, then \
check that an interpolated value can actually be produced. First \
check that the sum of weights is not zero. */ \
if ( !bad ) { \
bad = ( wtsum == (Xfloattype) 0.0 ); \
\
/* If OK, calculate the interpolated value. Then, if the output data \
type is not floating point, check that this value will not overflow \
the available output range. */ \
if ( !bad ) { \
val = sum / wtsum; \
if ( !( Xfloating ) ) { \
bad = ( val <= lo_lim ) || ( val >= hi_lim ); \
} \
} \
\
/* If no interpolated data value can be produced, then no associated \
variance will be required either. */ \
if ( ( Usevar ) && bad ) bad_var = 1; \
} \
\
/* Now perform similar checks on the output variance value (if \
required). This time we check that the square of the sum of \
weights is not zero (since this might underflow before the sum of \
weights). Again we also check to prevent the result overflowing the \
output data type. */ \
if ( ( Usevar ) && !bad_var ) { \
wtsum_sq = wtsum * wtsum; \
bad_var = ( wtsum_sq == (Xfloattype) 0.0 ); \
if ( !bad_var ) { \
val_var = sum_var / wtsum_sq; \
if ( !( Xfloating ) ) { \
bad_var = ( val_var <= lo_lim ) || ( val_var >= hi_lim ); \
} \
} \
} \
\
/* Obtain the pixel offset into the output array. */ \
off_out = offset[ point ]; \
\
/* Assign a bad output value (and variance) if required and count it. */ \
if ( bad ) { \
if( !Nobad ) { \
out[ off_out ] = badval; \
if ( Usevar ) out_var[ off_out ] = badval; \
} \
(*nbad)++; \
\
/* Otherwise, assign the interpolated value. If the output data type \
is floating point, the result can be stored directly, otherwise we \
must round to the nearest integer. */ \
} else { \
if ( Xfloating ) { \
out[ off_out ] = (Xtype) val; \
} else { \
out[ off_out ] = (Xtype) ( val + ( ( val >= (Xfloattype) 0.0 ) ? \
( (Xfloattype) 0.5 ) : \
( (Xfloattype) -0.5 ) ) ); \
} \
\
/* If a variance estimate is required but none can be obtained, then \
store a bad output variance value and count it. */ \
if ( Usevar ) { \
if ( bad_var ) { \
if( !Nobad ) out_var[ off_out ] = badval; \
(*nbad)++; \
\
/* Otherwise, store the variance estimate, rounding to the nearest \
integer if necessary. */ \
} else { \
if ( Xfloating ) { \
out_var[ off_out ] = (Xtype) val_var; \
} else { \
out_var[ off_out ] = (Xtype) ( val_var + \
( ( val_var >= (Xfloattype) 0.0 ) ? \
( (Xfloattype) 0.5 ) : \
( (Xfloattype) -0.5 ) ) ); \
} \
} \
} \
}
/* These subsidiary macros define limits for range checking of results
before conversion to the final data type. For each data type code
, HI_ gives the least positive floating point value which
just overflows that data type towards plus infinity, while LO_
gives the least negative floating point value which just overflows
that data type towards minus infinity. Thus, a floating point value
must satisfy LO is a floating point type, the limits are not actually used,
but must be present to permit error-free compilation. */
#if HAVE_LONG_DOUBLE /* Not normally implemented */
#define HI_LD ( 0.0L )
#define LO_LD ( 0.0L )
#endif
#define HI_D ( 0.0 )
#define LO_D ( 0.0 )
#define HI_F ( 0.0f )
#define LO_F ( 0.0f )
#if HAVE_LONG_DOUBLE /* Not normally implemented */
#define HI_L ( 0.5L + (long double) LONG_MAX )
#define LO_L ( -0.5L + (long double) LONG_MIN )
#define HI_UL ( 0.5L + (long double) ULONG_MAX )
#define LO_UL ( -0.5L )
#define HI_K ( 0.5L + (long double) LONG_MAX )
#define LO_K ( -0.5L + (long double) LONG_MIN )
#define HI_UK ( 0.5L + (long double) ULONG_MAX )
#define LO_UK ( -0.5L )
#else
#define HI_L ( 0.5 + (double) LONG_MAX )
#define LO_L ( -0.5 + (double) LONG_MIN )
#define HI_UL ( 0.5 + (double) ULONG_MAX )
#define LO_UL ( -0.5 )
#define HI_K ( 0.5 + (double) LONG_MAX )
#define LO_K ( -0.5 + (double) LONG_MIN )
#define HI_UK ( 0.5 + (double) ULONG_MAX )
#define LO_UK ( -0.5 )
#endif
#define HI_I ( 0.5 + (double) INT_MAX )
#define LO_I ( -0.5 + (double) INT_MIN )
#define HI_UI ( 0.5 + (double) UINT_MAX )
#define LO_UI ( -0.5 )
#define HI_S ( 0.5f + (float) SHRT_MAX )
#define LO_S ( -0.5f + (float) SHRT_MIN )
#define HI_US ( 0.5f + (float) USHRT_MAX )
#define LO_US ( -0.5f )
#define HI_B ( 0.5f + (float) SCHAR_MAX )
#define LO_B ( -0.5f + (float) SCHAR_MIN )
#define HI_UB ( 0.5f + (float) UCHAR_MAX )
#define LO_UB ( -0.5f )
/* This subsidiary macro tests for negative variance values. This
check is required only for signed data types. */
#define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \
bad_var = bad_var || ( var < ( (Xtype) 0 ) );
/* Expand the main macro above to generate a function for each
required signed data type. */
#if HAVE_LONG_DOUBLE /* Not normally implemented */
MAKE_INTERPOLATE_BLOCKAVE(LD,long double,1,long double,1)
MAKE_INTERPOLATE_BLOCKAVE(L,long int,0,long double,1)
MAKE_INTERPOLATE_BLOCKAVE(K,INT_BIG,0,long double,1)
#else
MAKE_INTERPOLATE_BLOCKAVE(L,long int,0,double,1)
MAKE_INTERPOLATE_BLOCKAVE(K,INT_BIG,0,double,1)
#endif
MAKE_INTERPOLATE_BLOCKAVE(D,double,1,double,1)
MAKE_INTERPOLATE_BLOCKAVE(F,float,1,float,1)
MAKE_INTERPOLATE_BLOCKAVE(I,int,0,double,1)
MAKE_INTERPOLATE_BLOCKAVE(S,short int,0,float,1)
MAKE_INTERPOLATE_BLOCKAVE(B,signed char,0,float,1)
/* Re-define the macro for testing for negative variances to do
nothing. */
#undef CHECK_FOR_NEGATIVE_VARIANCE
#define CHECK_FOR_NEGATIVE_VARIANCE(Xtype)
/* Expand the main macro above to generate a function for each
required unsigned data type. */
#if HAVE_LONG_DOUBLE /* Not normally implemented */
MAKE_INTERPOLATE_BLOCKAVE(UL,unsigned long int,0,long double,0)
MAKE_INTERPOLATE_BLOCKAVE(UK,UINT_BIG,0,long double,0)
#else
MAKE_INTERPOLATE_BLOCKAVE(UL,unsigned long int,0,double,0)
MAKE_INTERPOLATE_BLOCKAVE(UK,UINT_BIG,0,double,0)
#endif
MAKE_INTERPOLATE_BLOCKAVE(UI,unsigned int,0,double,0)
MAKE_INTERPOLATE_BLOCKAVE(US,unsigned short int,0,float,0)
MAKE_INTERPOLATE_BLOCKAVE(UB,unsigned char,0,float,0)
/* Undefine the macros used above. */
#undef CHECK_FOR_NEGATIVE_VARIANCE
#if HAVE_LONG_DOUBLE /* Not normally implemented */
#undef HI_LD
#undef LO_LD
#endif
#undef HI_D
#undef LO_D
#undef HI_F
#undef LO_F
#undef HI_L
#undef LO_L
#undef HI_UL
#undef LO_UL
#undef HI_K
#undef LO_K
#undef HI_UK
#undef LO_UK
#undef HI_I
#undef LO_I
#undef HI_UI
#undef LO_UI
#undef HI_S
#undef LO_S
#undef HI_US
#undef LO_US
#undef HI_B
#undef LO_B
#undef HI_UB
#undef LO_UB
#undef CALC_AND_ASSIGN_OUTPUT
#undef ASSEMBLE_INPUT_ND
#undef ASSEMBLE_INPUT_2D
#undef ASSEMBLE_INPUT_1D
#undef MAKE_INTERPOLATE_BLOCKAVE
static void Invert( AstMapping *this, int *status ) {
/*
*++
* Name:
c astInvert
f AST_INVERT
* Purpose:
* Invert a Mapping.
* Type:
* Public virtual function.
* Synopsis:
c #include "mapping.h"
c void astInvert( AstMapping *this )
f CALL AST_INVERT( THIS, STATUS )
* Class Membership:
* Mapping method.
* Description:
c This function inverts a Mapping by reversing the boolean sense
f This routine inverts a Mapping by reversing the boolean sense
* of its Invert attribute. If this attribute is zero (the
* default), the Mapping will transform coordinates in the way
* specified when it was created. If it is non-zero, the input and
* output coordinates will be inter-changed so that the direction
* of the Mapping is reversed. This will cause it to display the
* inverse of its original behaviour.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the Mapping.
f STATUS = INTEGER (Given and Returned)
f The global status.
*--
*/
/* Local Variables: */
int invert; /* New Invert attribute value */
/* Check the global error status. */
if ( !astOK ) return;
/* Determine the new Invert attribute value. */
invert = !astGetInvert( this );
/* Clear the old value. */
astClearInvert( this );
/* If the resulting default value is not the one required, then set a
new value explicitly. */
if ( astGetInvert( this ) != invert ) astSetInvert( this, invert );
}
static double J1Bessel( double x, int *status ) {
/*
* Name:
* J1Bessel
* Purpose:
* Calculates the first-order Bessel function of the first kind.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* double J1Bessel( double x, int *status )
* Class Membership:
* Mapping member function.
* Description:
* This function calculates the value of the first-order Bessel function
* of the first kind.
* Parameters:
* x
* The argument for J1.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The calculated J1(x) value.
* Notes:
* - The algorithm is taken from the SCUBA routine SCULIB_BESSJ1, by
* J.Lightfoot.
* - This function does not perform error checking and does not
* generate errors.
*/
/* Local Variables: */
static double p1 = 1.0;
static double p2 = 0.183105E-2;
static double p3 = -0.3516396496E-4;
static double p4 = 0.2457520174E-5;
static double p5 = -0.240337019E-6;
static double q1 = 0.04687499995;
static double q2 = -0.2002690873E-3;
static double q3 = 0.8449199096E-5;
static double q4 = -0.88228987E-6;
static double q5 = 0.105787412E-6;
static double r1 = 72362614232.0;
static double r2 = -7895059235.0;
static double r3 = 242396853.1;
static double r4 = -2972611.439;
static double r5 = 15704.48260;
static double r6 = -30.16036606;
static double s1 = 144725228442.0;
static double s2 = 2300535178.0;
static double s3 = 18583304.74;
static double s4 = 99447.43394;
static double s5 = 376.9991397;
static double s6 = 1.0;
double ax;
double xx;
double z;
double y;
double value;
int s;
/* Calculate the value */
ax = fabs( x );
if( ax < 8.0 ) {
y = x*x;
value = x*( r1 + y*( r2 + y*( r3 + y*( r4 + y*( r5 + y*r6 ) ) ) ) ) /
( s1 + y*( s2 + y*( s3 + y*( s4 + y*( s5 + y*s6 ) ) ) ) );
} else {
s = ( x >= 0.0 ) ? 1 : -1;
z = 8.0 / ax;
y = z*z;
xx = ax - 2.356194491;
value = sqrt ( 0.636619772/ax )*( cos( xx )*( p1 + y*( p2 + y*
( p3 + y*( p4 + y*p5 ) ) ) )-z*sin( xx )*( q1 + y*( q2 + y*( q3 + y*
( q4 + y*q5 ) ) ) ) )*s;
}
return value;
}
static int LinearApprox( AstMapping *this, const double *lbnd,
const double *ubnd, double tol, double *fit, int *status ) {
/*
*++
* Name:
c astLinearApprox
f AST_LINEARAPPROX
* Purpose:
* Obtain a linear approximation to a Mapping, if appropriate.
* Type:
* Public virtual function.
* Synopsis:
c #include "mapping.h"
c int astLinearApprox( AstMapping *this, const double *lbnd,
c const double *ubnd, double tol, double *fit )
f RESULT = AST_LINEARAPPROX( THIS, LBND, UBND, TOL, FIT, STATUS )
* Class Membership:
* Mapping function.
* Description:
* This function tests the forward coordinate transformation
* implemented by a Mapping over a given range of input coordinates. If
* the transformation is found to be linear to a specified level of
* accuracy, then an array of fit coefficients is returned. These
* may be used to implement a linear approximation to the Mapping's
* forward transformation within the specified range of output coordinates.
* If the transformation is not sufficiently linear, no coefficients
* are returned.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the Mapping.
c lbnd
f LBND( * ) = DOUBLE PRECISION (Given)
c Pointer to an array of doubles
f An array
* containing the lower bounds of a box defined within the input
* coordinate system of the Mapping. The number of elements in this
* array should equal the value of the Mapping's Nin attribute. This
* box should specify the region over which linearity is required.
c ubnd
f UBND( * ) = DOUBLE PRECISION (Given)
c Pointer to an array of doubles
f An array
* containing the upper bounds of the box specifying the region over
* which linearity is required.
c tol
f TOL = DOUBLE PRECISION (Given)
* The maximum permitted deviation from linearity, expressed as
* a positive Cartesian displacement in the output coordinate
* space of the Mapping. If a linear fit to the forward
* transformation of the Mapping deviates from the true transformation
* by more than this amount at any point which is tested, then no fit
* coefficients will be returned.
c fit
f FIT( * ) = DOUBLE PRECISION (Returned)
c Pointer to an array of doubles
f An array
* in which to return the co-efficients of the linear
* approximation to the specified transformation. This array should
* have at least "( Nin + 1 ) * Nout", elements. The first Nout elements
* hold the constant offsets for the transformation outputs. The
* remaining elements hold the gradients. So if the Mapping has 2 inputs
* and 3 outputs the linear approximation to the forward transformation
* is:
*
c X_out = fit[0] + fit[3]*X_in + fit[4]*Y_in
f X_out = fit(1) + fit(4)*X_in + fit(5)*Y_in
*
c Y_out = fit[1] + fit[5]*X_in + fit[6]*Y_in
f Y_out = fit(2) + fit(6)*X_in + fit(7)*Y_in
*
c Z_out = fit[2] + fit[7]*X_in + fit[8]*Y_in
f Z_out = fit(3) + fit(8)*X_in + fit(9)*Y_in
*
f STATUS = INTEGER (Given and Returned)
f The global status.
* Returned Value:
c astLinearApprox()
f AST_LINEARAPPROX = LOGICAL
* If the forward transformation is sufficiently linear,
c a non-zero value is returned. Otherwise zero is returned
f .TRUE is returned. Otherwise .FALSE. is returned
* and the fit co-efficients are set to AST__BAD.
* Notes:
* - This function fits the Mapping's forward transformation. To fit
* the inverse transformation, the Mapping should be inverted using
c astInvert
f AST_INVERT
* before invoking this function.
c - A value of zero
f - A value of .FALSE.
* will be returned if this function is invoked
* with the global error status set, or if it should fail for any
* reason.
*--
* Implementation Deficiencies:
* Sub-classes which implement linear mappings should probably
* over-ride this function to get better accuracy and faster execution,
* but currently they do not.
*/
/* Local Variables: */
AstPointSet *pset_in_f; /* PointSet for input fitting points */
AstPointSet *pset_in_t; /* PointSet for input test points */
AstPointSet *pset_out_f; /* PointSet for output fitting points */
AstPointSet *pset_out_t; /* PointSet for output test points */
double **ptr_in_f; /* Input coordinate array pointers */
double **ptr_in_t; /* Input coordinate array pointers */
double **ptr_out_f; /* Output coordinate array pointers */
double **ptr_out_t; /* Output coordinate array pointers */
double *grad; /* Pointer to matrix of gradients */
double *zero; /* Pointer to array of zero point values */
double diff; /* Difference in coordinate values */
double err; /* Sum of squared error */
double frac; /* Fraction of input coordinate range */
double in1; /* Input coordinate value */
double in2; /* Input coordinate value */
double indiff; /* Difference in input coordinate values */
double out1; /* Output coordinate value */
double out2; /* Output coordinate value */
double x0; /* Coordinate of grid centre */
double y; /* Output coordinate (transformed) */
double yfit; /* Coordinate resulting from fit */
double z; /* Sum for calculating zero points */
int *vertex; /* Pointer to flag array for vertices */
int coord_in; /* Loop counter for input coordinates */
int coord_out; /* Loop counter for output coordinates. */
int done; /* All vertices visited? */
int face1; /* Index of first face coordinates */
int face2; /* Index of second face coordinates */
int face; /* Loop counter for faces */
int ii; /* Index into gradient matrix */
int linear; /* Mapping is linear? */
int nc; /* Number of coeffs in fit */
int ndim_in; /* Number of Mapping inputs */
int ndim_out; /* Number of Mapping outputs */
int npoint; /* Number of test points required */
int point; /* Counter for points */
int result; /* Returned flag */
/* Initialise. */
result = 0;
/* Check the global error status. */
if ( !astOK ) return result;
/* Further initialisation. */
linear = 1;
grad = NULL;
zero = NULL;
/* Get the number of Mapping output and inputs. */
ndim_in = astGetNin( this );
ndim_out = astGetNout( this );
/* Store the number of coefficients in the fit.*/
nc = ( ndim_in + 1 ) * ndim_out;
/* Create a PointSet to hold input coordinates and obtain a pointer
to its coordinate arrays. */
pset_in_f = astPointSet( 2 * ndim_in, ndim_in, "", status );
ptr_in_f = astGetPoints( pset_in_f );
if ( astOK ) {
/* Set up and transform an initial set of points. */
/* ---------------------------------------------- */
/* Loop to set up input coordinates at the centre of each face of the
input grid, storing them in the PointSet created above. */
point = 0;
for ( face = 0; face < ( 2 * ndim_in ); face++ ) {
for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) {
ptr_in_f[ coord_in ][ point ] =
0.5 * ( lbnd[ coord_in ] + ubnd[ coord_in ] );
}
ptr_in_f[ face / 2 ][ point ] = ( face % 2 ) ?
ubnd[ face / 2 ] : lbnd[ face / 2 ];
point++;
}
}
/* Transform these coordinates into the output grid's coordinate system
and obtain an array of pointers to the resulting coordinate
data. */
pset_out_f = astTransform( this, pset_in_f, 1, NULL );
ptr_out_f = astGetPoints( pset_out_f );
if ( astOK ) {
/* Fit a linear approximation to the points. */
/* ----------------------------------------- */
/* Obtain pointers to the locations in the fit coefficients array
where the gradients and zero points should be stored. */
grad = fit + ndim_out;
zero = fit;
/* On the assumption that the transformation applied above is
approximately linear, loop to determine the matrix of gradients and
the zero points which describe it. */
ii = 0;
for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) {
z = 0.0;
for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) {
/* Find the indices of opposite faces in each input dimension. */
face1 = 2 * coord_in;
face2 = face1 + 1;
/* Obtain the input and output coordinates at these face centres. */
in1 = ptr_in_f[ coord_in ][ face1 ];
in2 = ptr_in_f[ coord_in ][ face2 ];
out1 = ptr_out_f[ coord_out ][ face1 ];
out2 = ptr_out_f[ coord_out ][ face2 ];
/* Check whether any transformed coordinates are bad. If so, the
transformation cannot be linear, so give up trying to fit it. */
if ( ( out1 == AST__BAD ) || ( out2 == AST__BAD ) ) {
linear = 0;
break;
}
/* If possible, determine the gradient along this dimension, storing
it in the appropriate element of the gradient matrix. */
indiff = in2 - in1;
if ( indiff != 0.0 ) {
grad[ ii++ ] = ( out2 - out1 ) / indiff;
} else {
grad[ ii++ ] = 0.0;
}
/* Accumulate the sum used to determine the zero point. */
z += ( out1 + out2 );
}
/* Also quit the outer loop if a linear fit cannot be obtained. */
if ( !linear ) break;
/* Determine the average zero point from all dimensions. */
zero[ coord_out ] = z / (double) ( 2 * ndim_in );
}
/* If a linear fit was obtained, its zero points will be appropriate
to an input coordinate system with an origin at the centre of the
input grid (we assume this to simplify the calculations above). To
correct for this, we transform the actual input coordinates of the
grid's centre through the matrix of gradients and subtract the
resulting coordinates from the zero point values. The zero points
are then correct for the actual output and input coordinate systems
we are using. */
if ( linear ) {
ii = 0;
for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) {
for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) {
x0 = 0.5 * ( lbnd[ coord_in ] + ubnd[ coord_in ] );
zero[ coord_out ] -= grad[ ii++ ] * x0;
}
}
}
}
/* Annul the pointers to the PointSets used above. */
pset_out_f = astAnnul( pset_out_f );
pset_in_f = astAnnul( pset_in_f );
/* Calculate the number of test points required. */
/* --------------------------------------------- */
/* If we have obtained a linear fit above, it will (by construction)
be exact at the centre of each face of the input grid. However, it
may not fit anywhere else. We therefore set up some test points to
determine if it is an adequate approximation elsewhere. */
if ( astOK && linear ) {
/* Calculate the number of test points required to place one at each
vertex of the grid. */
npoint = 1;
for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) {
npoint *= 2;
}
/* Now calculate the total number of test points required, also
allowing one at the centre, one at half the distance to each face,
and one at half the distance to each vertex. */
npoint = 1 + 2 * ( ndim_in + npoint );
/* Set up test points in the input coordinate system. */
/* --------------------------------------------------- */
/* Create a PointSet to hold the test coordinates and obtain an array
of pointers to its coordinate data. */
pset_in_t = astPointSet( npoint, ndim_in, "", status );
ptr_in_t = astGetPoints( pset_in_t );
if ( astOK ) {
/* If the input array is 1-dimensional, the face and vertex positions
calculated below will co-incide. Therefore, we simply distribute
the required number of test points uniformly throughout the input
coordinate range (avoiding the end-points, where the fit has been
obtained). The coordinates are stored in the PointSet created
above. */
if ( ndim_in == 1 ) {
for ( point = 0; point < npoint; point++ ) {
frac = ( (double) ( point + 1 ) ) / (double) ( npoint + 1 );
ptr_in_t[ 0 ][ point ] = ( 1.0 - frac ) * lbnd[ 0 ] +
frac * ubnd[ 0 ];
}
/* Otherwise, generate one point at the grid centre (offset slightly
since the exact centre may not be very representative). */
} else {
point = 0;
for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) {
ptr_in_t[ coord_in ][ point ] =
0.49 * lbnd[ coord_in ] + 0.51 * ubnd[ coord_in ];
}
point++;
/* Similarly generate a point half way between the grid centre and the
centre of each face. Again introduce some small random offsets to break
any regularity in the grid. */
for ( face = 0; face < ( 2 * ndim_in ); face++ ) {
for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) {
ptr_in_t[ coord_in ][ point ] =
0.48 * lbnd[ coord_in ] + 0.52 * ubnd[ coord_in ];
}
ptr_in_t[ face / 2 ][ point ] =
( 0.51 * ( ( ( face % 2 ) ? ubnd[ face / 2 ] :
lbnd[ face / 2 ] ) ) +
0.49 * ptr_in_t[ face / 2 ][ 0 ] );
point++;
}
/* Allocate workspace and initialise flags for identifying the
vertices. */
vertex = astMalloc( sizeof( int ) * (size_t) ndim_in );
if ( astOK ) {
for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) {
vertex[ coord_in ] = 0;
}
/* Now loop to visit each input grid vertex. */
done = 0;
do {
/* Generate a test point at each vertex. */
for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) {
ptr_in_t[ coord_in ][ point ] = vertex[ coord_in ] ?
ubnd[ coord_in ] :
lbnd[ coord_in ];
/* Also place one half way between the grid centre and each vertex. */
ptr_in_t[ coord_in ][ point + 1 ] =
( 0.52 * ptr_in_t[ coord_in ][ point ] +
0.48 * ptr_in_t[ coord_in ][ 0 ] );
}
point += 2;
/* Now update the array of vertex flags to identify the next vertex. */
coord_in = 0;
do {
/* The least significant dimension which does not have its upper bound
as one of the vertex coordinates is changed to use its upper bound
in the next vertex. */
if ( !vertex[ coord_in ] ) {
vertex[ coord_in ] = 1;
break;
/* Any less significant dimensions whose upper bounds are already
being used are changed to use their lower bounds in the next
vertex. */
} else {
vertex[ coord_in ] = 0;
/* All vertices have been visited when the most significant dimension
is changed back to using its lower bound. */
done = ( ++coord_in == ndim_in );
}
} while ( !done );
} while ( !done );
}
/* Free the workspace used for vertex flags. */
vertex = astFree( vertex );
}
/* Transform the test points. */
/* -------------------------- */
/* Use the Mapping to transform the test points into the output grid's
coordinate system, obtaining a pointer to the resulting arrays of
output coordinates. */
pset_out_t = astTransform( this, pset_in_t, 1, NULL );
ptr_out_t = astGetPoints( pset_out_t );
/* Test the linear fit for accuracy. */
/* --------------------------------- */
/* If OK so far, then loop to use this fit to transform each test
point and compare the result with the result of applying the
Mapping. */
if ( astOK ) {
for ( point = 0; point < npoint; point++ ) {
/* Initialise the fitting error for the current point. */
err = 0.0;
/* Obtain each output coordinate (produced by using the Mapping) in
turn and check that it is not bad. If it is, then the
transformation is not linear, so give up testing the fit. */
ii = 0;
for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) {
y = ptr_out_t[ coord_out ][ point ];
if ( y == AST__BAD ) {
linear = 0;
break;
}
/* Apply the fitted transformation to the input coordinates to obtain
the approximate output coordinate value. */
yfit = zero[ coord_out ];
for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) {
yfit += grad[ ii++ ] * ptr_in_t[ coord_in ][ point ];
}
/* Form the sum of squared differences between the Mapping's
transformation and the fit. */
diff = ( y - yfit );
err += diff * diff;
}
/* Quit the outer loop if the Mapping is found to be non-linear. */
if ( !linear ) break;
/* Test if the Cartesian distance between the true output coordinate
and the approximate one exceeds the accuracy tolerance. If this
happens for any test point, we declare the Mapping non-linear and
give up. */
if ( sqrt( err ) > tol ) {
linear = 0;
break;
}
}
}
/* Annul the pointers to the PointSets used above. */
pset_out_t = astAnnul( pset_out_t );
}
pset_in_t = astAnnul( pset_in_t );
}
/* If an error occurred, or the Mapping was found to be non-linear,
then set the coefficients to AST_BAD. Otherwise, set the returned flag
to indicate that the fit was succesful. */
if ( !astOK || !linear ) {
for( ii = 0; ii < nc; ii++ ) fit[ ii ] = AST__BAD;
} else {
result = 1;
}
/* Return the result. */
return result;
}
static double LocalMaximum( const MapData *mapdata, double acc, double fract,
double x[], int *status ) {
/*
* Name:
* LocalMaximum
* Purpose:
* Find a local maximum in a Mapping function.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* double LocalMaximum( const MapData *mapdata, double acc, double fract,
* double x[], int *status );
* Class Membership:
* Mapping member function.
* Description:
* This function finds a local maximum in the Mapping function
* supplied. It employs the modified simplex method (as
* implemented by UphillSimplex), but repeatedly re-starts the
* simplex algorithm and tests for convergence of successive
* maxima, so as to further improve robustness on difficult
* problems.
* Parameters:
* mapdata
* Pointer to a MapData structure describing the Mapping
* function, its coordinate constraints, etc.
* acc
* The required accuracy with which the maximum is to be found.
* fract
* A value between 0.0 and 1.0 which determines the initial step
* length along each coordinate axis. It should be given as a
* fraction of the difference between the upper and lower
* constraint values for each axis (as specified in the
* "mapdata" structure).
* x
* Pointer to an array of double containing the coordinates of
* an initial estimate of the position of the maximum. On exit,
* this will be updated to contain the best estimate of the
* maximum's position, as found by this function.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The best estimate of the Mapping function's maximum value.
* Notes:
* - A value of AST__BAD will be returned, and no useful
* information about a solution will be produced, if this function
* is invoked with the global error status set or if it should fail
* for any reason.
*/
/* Local Constants: */
const int maxcall = 1500; /* Maximum number of function evaluations */
const int maxiter = 5; /* Maximum number of iterations */
/* Local Variables: */
double *dx; /* Pointer to array of step lengths */
double err; /* Simplex error estimate */
double maximum; /* Simplex maximum value */
double middle; /* Middle coordinate between bounds */
double result; /* Result value to return */
int coord; /* Loop counter for coordinates */
int done; /* Iterations complete? */
int iter; /* Loop counter for iterations */
int ncall; /* Number of function calls (junk) */
/* Initialise. */
result = AST__BAD;
/* Check the global error status. */
if ( !astOK ) return result;
/* Further initialise to avoid compiler warnings. */
err = 0.0;
/* Allocate workspace. */
dx = astMalloc( sizeof( double ) * (size_t) mapdata->nin );
/* Perform iterations to repeatedly identify a local maximum. */
for ( iter = 0; astOK && ( iter < maxiter ); iter++ ) {
/* Set up initial step lengths along each coordinate axis, adjusting
their signs to avoid placing points outside the coordinate
constraints (i.e. step away from the closer boundary on each
axis). */
for ( coord = 0; coord < mapdata->nin; coord++ ) {
middle = 0.5 * ( mapdata->lbnd[ coord ] + mapdata->ubnd[ coord ] );
dx[ coord ] = fract * ( mapdata->ubnd[ coord ] -
mapdata->lbnd[ coord ] );
if ( x[ coord ] > middle ) dx[ coord ] = -dx[ coord ];
}
/* Find an approximation to a local maximum using the simplex method
and check for errors. */
maximum = UphillSimplex( mapdata, acc, maxcall, dx, x, &err, &ncall, status );
if ( astOK ) {
/* Use this maximum value if no previous maximum has been found. */
if ( result == AST__BAD ) {
result = maximum;
/* Otherwise use it only if it improves on the previous maximum. */
} else if ( maximum >= result ) {
/* We iterate, re-starting the simplex algorithm from its previous
best position so as to guard against premature false
convergence. Iterations continue until the improvement in the
maximum is no greater than the required accuracy (and the simplex
algorithm itself has converged to the required accuracy). Note when
iterations should cease. */
done = ( ( ( maximum - result ) <= acc ) && ( err <= acc ) );
/* Store the best maximum and quit iterating if appropriate. */
result = maximum;
if ( done ) break;
}
/* Otherwise, decrement the initial step size for the next iteration. */
fract /= 1000.0;
}
}
/* Free the workspace. */
dx = astFree( dx );
/* If an error occurred, clear the result value. */
if ( !astOK ) result = AST__BAD;
/* return the result. */
return result;
}
static void MapBox( AstMapping *this,
const double lbnd_in[], const double ubnd_in[],
int forward, int coord_out,
double *lbnd_out, double *ubnd_out,
double xl[], double xu[], int *status ) {
/*
*+
* Name:
* astMapBox
* Purpose:
* Find a bounding box for a Mapping.
* Type:
* Protected virtual function.
* Synopsis:
* #include "mapping.h"
* void astMapBox( AstMapping *this,
* const double lbnd_in[], const double ubnd_in[],
* int forward, int coord_out,
* double *lbnd_out, double *ubnd_out,
* double xl[], double xu[] );
* Class Membership:
* Mapping method.
* Description:
* This function allows you to find the "bounding box" which just
* encloses another box after it has been transformed by a Mapping
* (using either its forward or inverse transformation). A typical
* use might be to calculate the size which an image would have
* after being transformed by the Mapping.
*
* The function works on one dimension at a time. When supplied
* with the lower and upper bounds of a rectangular region (box) of
* input coordinate space, it finds the lowest and highest values
* taken by a nominated output coordinate within that
* region. Optionally, it also returns the input coordinates where
* these bounding values are attained. It should be used repeatedly
* if the extent of the bounding box is required in more than one
* dimension.
* Parameters:
* this
* Pointer to the Mapping.
* lbnd_in
* Pointer to an array of double, with one element for each
* Mapping input coordinate. This should contain the lower bound
* of the input box in each dimension.
* ubnd_in
* Pointer to an array of double, with one element for each
* Mapping input coordinate. This should contain the upper bound
* of the input box in each dimension.
*
* Note that it is permissible for the lower bound to exceed the
* corresponding upper bound, as the values will simply be
* swapped before use.
* forward
* If this value is non-zero, then the Mapping's forward
* transformation will be used to transform the input
* box. Otherwise, its inverse transformation will be used.
*
* (If the inverse transformation is selected, then references
* to "input" and "output" coordinates in this description
* should be transposed. For example, the size of the "lbnd_in"
* and "ubnd_in" arrays should match the number of output
* coordinates, as given by the Mapping's Nout attribute.)
* coord_out
* The (zero-based) index of the output coordinate for which the
* lower and upper bounds are required.
* lbnd_out
* Pointer to a double in which to return the lowest value taken
* by the nominated output coordinate within the specified
* region of input coordinate space.
* ubnd_out
* Pointer to a double in which to return the highest value
* taken by the nominated output coordinate within the specified
* region of input coordinate space.
* xl
* An optional pointer to an array of double, with one element
* for each Mapping input coordinate. If given, this array will
* be filled with the coordinates of an input point (although
* not necessarily a unique one) for which the nominated output
* coordinate takes the lower bound value returned in
* "*lbnd_out".
*
* If these coordinates are not required, a NULL pointer may be
* supplied.
* xu
* An optional pointer to an array of double, with one element
* for each Mapping input coordinate. If given, this array will
* be filled with the coordinates of an input point (although
* not necessarily a unique one) for which the nominated output
* coordinate takes the upper bound value returned in
* "*ubnd_out".
*
* If these coordinates are not required, a NULL pointer may be
* supplied.
* Notes:
* - Any input points which are transformed by the Mapping to give
* output coordinates containing the value AST__BAD are regarded as
* invalid and are ignored, They will make no contribution to
* determining the output bounds, even although the nominated
* output coordinate might still have a valid value at such points.
* - An error will occur if the required output bounds cannot be
* found. Typically, this might occur if all the input points which
* the function considers turn out to be invalid (see above). The
* number of points considered before generating such an error is
* quite large, however, so this is unlikely to occur by accident
* unless valid points are restricted to a very small subset of the
* input coordinate space.
* - The values returned via "lbnd_out", "ubnd_out", "xl" and "xu"
* will be set to the value AST__BAD if this function should fail
* for any reason. Their initial values on entry will not be
* altered if the function is invoked with the global error status
* set.
*-
* Implementation Notes:
* - This function implements the basic astMapBox method available
* via the protected interface to the Mapping class. The public
* interface to this method is provided by the astMapBoxId_
* function.
*/
/* Local Variables: */
MapData mapdata; /* Structure to describe Mapping function */
double *x_l; /* Pointer to coordinate workspace */
double *x_u; /* Pointer to coordinate workspace */
double lbnd; /* Required lower bound */
double ubnd; /* Required upper bound */
int coord; /* Loop counter for coordinates. */
int nin; /* Effective number of input coordinates */
int nout; /* Effective number of output coordinates */
int refine; /* Can bounds be refined? */
/* Check the global error status. */
if ( !astOK ) return;
/* Initialisation to avoid compiler warnings. */
lbnd = AST__BAD;
ubnd = AST__BAD;
/* Obtain the effective numbers of input and output coordinates for
the Mapping, taking account of which transformation is to be
used. */
nin = forward ? astGetNin( this ) : astGetNout( this );
nout = forward ? astGetNout( this ) : astGetNin( this );
/* Check that the output coordinate index supplied is valid and report
an error if it is not. Use public (one-based) coordinate numbering
in the error message. */
if ( astOK ) {
if ( ( coord_out < 0 ) || ( coord_out >= nout ) ) {
astError( AST__BADCI, "astMapBox(%s): Output coordinate index (%d) "
"invalid - it should be in the range 1 to %d.", status,
astGetClass( this ), coord_out + 1, nout );
}
}
/* Initialise a MapData structure to describe the Mapping function
whose limits are to be found. Since it may be evaluated many
times, we attempt to simplify the Mapping supplied. */
if ( astOK ) {
mapdata.mapping = astSimplify( this );
/* Store the number of input/output coordinates and the index of the
output coordinate in which we are interested. */
mapdata.nin = nin;
mapdata.nout = nout;
mapdata.coord = coord_out;
/* Note which Mapping transformation is being used. */
mapdata.forward = forward;
/* Store pointers to arrays which will contain the input coordinate
bounds. */
mapdata.lbnd = astMalloc( sizeof( double ) * (size_t) nin );
mapdata.ubnd = astMalloc( sizeof( double ) * (size_t) nin );
/* Create PointSets for passing coordinate data to and from the
Mapping. */
mapdata.pset_in = astPointSet( 1, nin, "", status );
mapdata.pset_out = astPointSet( 1, nout, "", status );
/* Obtain pointers to these PointSets' coordinate arrays. */
mapdata.ptr_in = astGetPoints( mapdata.pset_in );
mapdata.ptr_out = astGetPoints( mapdata.pset_out );
/* Allocate workspace for the returned input coordinates. */
x_l = astMalloc( sizeof( double ) * (size_t) nin );
x_u = astMalloc( sizeof( double ) * (size_t) nin );
if ( astOK ) {
/* Initialise the output bounds and corresponding input coordinates to
"unknown". */
for ( coord = 0; coord < nin; coord++ ) {
x_l[ coord ] = AST__BAD;
x_u[ coord ] = AST__BAD;
/* Initialise the input bounds, ensuring they are the correct way
around (if not already supplied this way). */
mapdata.lbnd[ coord ] = ( lbnd_in[ coord ] < ubnd_in[ coord ] ) ?
lbnd_in[ coord ] : ubnd_in[ coord ];
mapdata.ubnd[ coord ] = ( ubnd_in[ coord ] > lbnd_in[ coord ] ) ?
ubnd_in[ coord ] : lbnd_in[ coord ];
}
/* First examine a set of special input points to obtain an initial
estimate of the required output bounds. Do this only so long as the
number of points involved is not excessive. */
if ( nin <= 12 ) {
refine = SpecialBounds( &mapdata, &lbnd, &ubnd, x_l, x_u, status );
} else {
refine = 1;
}
/* Then attempt to refine this estimate using a global search
algorithm. */
if( refine ) GlobalBounds( &mapdata, &lbnd, &ubnd, x_l, x_u, status );
/* If an error occurred, generate a contextual error message. */
if ( !astOK ) {
astError( astStatus, "Unable to find a bounding box for a %s.", status,
astGetClass( this ) );
}
}
/* Return the output bounds and, if required, the input coordinate
values which correspond with them. */
if ( astOK ) {
*lbnd_out = lbnd;
*ubnd_out = ubnd;
for ( coord = 0; coord < nin; coord++ ) {
if ( xl ) xl[ coord ] = x_l[ coord ];
if ( xu ) xu[ coord ] = x_u[ coord ];
}
}
/* Annul the simplified Mapping pointer and the temporary
PointSets. Also free the workspace. */
mapdata.mapping = astAnnul( mapdata.mapping );
mapdata.lbnd = astFree( mapdata.lbnd );
mapdata.ubnd = astFree( mapdata.ubnd );
mapdata.pset_in = astAnnul( mapdata.pset_in );
mapdata.pset_out = astAnnul( mapdata.pset_out );
x_l = astFree( x_l );
x_u = astFree( x_u );
}
/* If an error occurred, then return bad bounds values and
coordinates. */
if ( !astOK ) {
*lbnd_out = AST__BAD;
*ubnd_out = AST__BAD;
for ( coord = 0; coord < nin; coord++ ) {
if ( xl ) xl[ coord ] = AST__BAD;
if ( xu ) xu[ coord ] = AST__BAD;
}
}
}
static double MapFunction( const MapData *mapdata, const double in[],
int *ncall, int *status ) {
/*
* Name:
* MapFunction
* Purpose:
* Return the value of a selected transformed coordinate.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* double MapFunction( const MapData *mapdata, const double in[],
* int *ncall, int *status );
* Class Membership:
* Mapping member function.
* Description:
* This function takes a set of input coordinates and applies a
* Mapping's coordinate transformation to them. It then returns the
* value of one of the transformed coordinates.
*
* It is provided for use by optimisation functions (e.g. those
* used for finding bounding boxes). The Mapping to be used and
* associated parameters (such as constraints on the range of input
* coordinates and the index of the output coordinate to be
* returned) are supplied in a MapData structure. The value
* returned will be negated if the "negate" component of this
* structure is non-zero.
*
* The value AST__BAD will be returned by this function if the
* input coordinates lie outside the constrained range given in
* the MapData structure, or if any of the transformed output
* coordinates is bad.
* Parameters:
* mapdata
* Pointer to a MapData structure which describes the Mapping to
* be used.
* in
* A double array containing the input coordinates of a single point.
* ncall
* Pointer to an int containing a count of the number of times
* the Mapping's coordinate transformation has been used. This
* value will be updated to reflect any use made by this
* function. Normally, this means incrementing the value by 1,
* but this will be omitted if the input coordinates supplied
* are outside the constrained range so that no transformation
* is performed.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The selected output coordinate value, or AST__BAD, as appropriate.
* Notes:
* - A value of AST__BAD will be returned if this function is
* invoked with the global error status set, or if it should fail
* for any reason.
*/
/* Local Variables: */
double result; /* Result to be returned */
int bad; /* Output coordinates invalid? */
int coord_in; /* Loop counter for input coordinates */
int coord_out; /* Loop counter for output coordinates */
int outside; /* Input point outside bounds? */
/* Initialise. */
result = AST__BAD;
/* Check the global error status. */
if ( !astOK ) return result;
/* See if the input point lies outside the required bounds. */
outside = 0;
for ( coord_in = 0; coord_in < mapdata->nin; coord_in++ ) {
if ( ( in[ coord_in ] < mapdata->lbnd[ coord_in ] ) ||
( in[ coord_in ] > mapdata->ubnd[ coord_in ] ) ) {
outside = 1;
break;
}
/* Also store the input coordinates in the memory associated with the
Mapping's input PointSet. */
mapdata->ptr_in[ coord_in ][ 0 ] = in[ coord_in ];
}
/* If the input coordinates are within bounds, transform them, using the
PointSets identified in the "mapdata" structure. */
if ( !outside ) {
(void) astTransform( mapdata->mapping, mapdata->pset_in,
mapdata->forward, mapdata->pset_out );
/* Increment the number of calls to astTransform and check the error
status. */
( *ncall )++;
if ( astOK ) {
/* If OK, test if any of the output coordinates is bad. */
bad = 0;
for ( coord_out = 0; coord_out < mapdata->nout; coord_out++ ) {
if ( mapdata->ptr_out[ coord_out ][ 0 ] == AST__BAD ) {
bad = 1;
break;
}
}
/* If not, then extract the required output coordinate, negating it if
necessary. */
if ( !bad ) {
result = mapdata->ptr_out[ mapdata->coord ][ 0 ];
if ( mapdata->negate ) result = -result;
}
}
}
/* Return the result. */
return result;
}
static int MapList( AstMapping *this, int series, int invert, int *nmap,
AstMapping ***map_list, int **invert_list, int *status ) {
/*
*+
* Name:
* astMapList
* Purpose:
* Decompose a Mapping into a sequence of simpler Mappings.
* Type:
* Protected virtual function.
* Synopsis:
* #include "mapping.h"
* int astMapList( AstMapping *this, int series, int invert, int *nmap,
* AstMapping ***map_list, int **invert_list )
* Class Membership:
* Mapping method.
* Description:
* This function decomposes a Mapping (which, in derived classes,
* may be a compound Mapping) into a sequence of simpler Mappings
* which may be applied in sequence to achieve the same effect. The
* Mapping is decomposed as far as possible, but it is not
* guaranteed that this will necessarily yield any more than one
* Mapping, which may actually be the original one supplied.
*
* This function is provided to support both the simplification of
* compound Mappings, and the analysis of Mapping structure so that
* particular forms can be recognised.
* Parameters:
* this
* Pointer to the Mapping to be decomposed (the Mapping is not
* actually modified by this function).
* series
* If this value is non-zero, an attempt will be made to
* decompose the Mapping into a sequence of equivalent Mappings
* which can be applied in series (i.e. one after the other). If
* it is zero, the decomposition will instead yield Mappings
* which can be applied in parallel (i.e. on successive sub-sets
* of the input/output coordinates).
* invert
* The value to which the Mapping's Invert attribute is to be
* (notionally) set before performing the
* decomposition. Normally, the value supplied here will be the
* actual Invert value obtained from the Mapping (e.g. using
* astGetInvert). Sometimes, however, when a Mapping is
* encapsulated within another structure, that structure may
* retain an Invert value (in order to prevent external
* interference) which should be used instead.
*
* Note that the actual Invert value of the Mapping supplied is
* not used (or modified) by this function.
* nmap
* The address of an int which holds a count of the number of
* individual Mappings in the decomposition. On entry, this
* should count the number of Mappings already in the
* "*map_list" array (below). On exit, it is updated to include
* any new Mappings appended by this function.
* map_list
* Address of a pointer to an array of Mapping pointers. On
* entry, this array pointer should either be NULL (if no
* Mappings have yet been obtained) or should point at a
* dynamically allocated array containing Mapping pointers
* ("*nmap" in number) which have been obtained from a previous
* invocation of this function.
*
* On exit, the dynamic array will be enlarged to contain any
* new Mapping pointers that result from the decomposition
* requested. These pointers will be appended to any previously
* present, and the array pointer will be updated as necessary
* to refer to the enlarged array (any space released by the
* original array will be freed automatically).
*
* The new Mapping pointers returned will identify a sequence of
* Mappings which, when applied in order, will perform a forward
* transformation equivalent to that of the original Mapping
* (after its Invert flag has first been set to the value
* requested above). The Mappings should be applied in series or
* in parallel according to the type of decomposition requested.
*
* All the Mapping pointers returned by this function should be
* annulled by the caller, using astAnnul, when no longer
* required. The dynamic array holding these pointers should
* also be freed, using astFree.
* invert_list
* Address of a pointer to an array of int. On entry, this array
* pointer should either be NULL (if no Mappings have yet been
* obtained) or should point at a dynamically allocated array
* containing Invert attribute values ("*nmap" in number) which
* have been obtained from a previous invocation of this
* function.
*
* On exit, the dynamic array will be enlarged to contain any
* new Invert attribute values that result from the
* decomposition requested. These values will be appended to any
* previously present, and the array pointer will be updated as
* necessary to refer to the enlarged array (any space released
* by the original array will be freed automatically).
*
* The new Invert values returned identify the values which must
* be assigned to the Invert attributes of the corresponding
* Mappings (whose pointers are in the "*map_list" array) before
* they are applied. Note that these values may differ from the
* actual Invert attribute values of these Mappings, which are
* not relevant.
*
* The dynamic array holding these values should be freed by the
* caller, using astFree, when no longer required.
* Returned Value:
* A non-zero value is returned if the supplied Mapping contained any
* inverted CmpMaps.
* Notes:
* - It is unspecified to what extent the original Mapping and the
* individual (decomposed) Mappings are
* inter-dependent. Consequently, the individual Mappings cannot be
* modified without risking modification of the original.
* - If this function is invoked with the global error status set,
* or if it should fail for any reason, then the *nmap value, the
* list of Mapping pointers and the list of Invert values will all
* be returned unchanged.
*-
*/
/* Check the global error status. */
if ( !astOK ) return 0;
/* Since we are dealing with a basic Mapping, only one new Mapping
pointer will be returned. Extend the dynamic arrays to accommodate
this Mapping. */
*map_list = astGrow( *map_list, *nmap + 1, sizeof( AstMapping * ) );
*invert_list = astGrow( *invert_list, *nmap + 1, sizeof( int ) );
if ( astOK ) {
/* Return the invert flag value for the Mapping and a clone of the
Mapping pointer. */
( *invert_list )[ *nmap ] = ( invert != 0 );
( *map_list )[ *nmap ] = astClone( this );
/* If OK, return the new Mapping count. */
if ( astOK ) ( *nmap )++;
}
return 0;
}
static int MapMerge( AstMapping *this, int where, int series, int *nmap,
AstMapping ***map_list, int **invert_list, int *status ) {
/*
*+
* Name:
* astMapMerge
* Purpose:
* Simplify a sequence of Mappings.
* Type:
* Protected virtual function.
* Synopsis:
* #include "mapping.h"
* int astMapMerge( AstMapping *this, int where, int series, int *nmap,
* AstMapping ***map_list, int **invert_list )
* Class Membership:
* Mapping method.
* Description:
* This function attempts to simplify a sequence of Mappings by
* merging a nominated Mapping in the sequence with its neighbours,
* so as to shorten the sequence if possible.
*
* In many cases, simplification will not be possible and the
* function will return -1 to indicate this, without further
* action.
*
* In most cases of interest, however, this function will either
* attempt to replace the nominated Mapping with one which it
* considers simpler, or to merge it with the Mappings which
* immediately precede it or follow it in the sequence (both will
* normally be considered). This is sufficient to ensure the
* eventual simplification of most Mapping sequences by repeated
* application of this function.
*
* In some cases, the function may attempt more elaborate
* simplification, involving any number of other Mappings in the
* sequence. It is not restricted in the type or scope of
* simplification it may perform, but will normally only attempt
* elaborate simplification in cases where a more straightforward
* approach is not adequate.
* Parameters:
* this
* Pointer to the nominated Mapping which is to be merged with
* its neighbours. This should be a cloned copy of the Mapping
* pointer contained in the array element "(*map_list)[where]"
* (see below). This pointer will not be annulled, and the
* Mapping it identifies will not be modified by this function.
* where
* Index in the "*map_list" array (below) at which the pointer
* to the nominated Mapping resides.
* series
* A non-zero value indicates that the sequence of Mappings to
* be simplified will be applied in series (i.e. one after the
* other), whereas a zero value indicates that they will be
* applied in parallel (i.e. on successive sub-sets of the
* input/output coordinates).
* nmap
* Address of an int which counts the number of Mappings in the
* sequence. On entry this should be set to the initial number
* of Mappings. On exit it will be updated to record the number
* of Mappings remaining after simplification.
* map_list
* Address of a pointer to a dynamically allocated array of
* Mapping pointers (produced, for example, by the astMapList
* method) which identifies the sequence of Mappings. On entry,
* the initial sequence of Mappings to be simplified should be
* supplied.
*
* On exit, the contents of this array will be modified to
* reflect any simplification carried out. Any form of
* simplification may be performed. This may involve any of: (a)
* removing Mappings by annulling any of the pointers supplied,
* (b) replacing them with pointers to new Mappings, (c)
* inserting additional Mappings and (d) changing their order.
*
* The intention is to reduce the number of Mappings in the
* sequence, if possible, and any reduction will be reflected in
* the value of "*nmap" returned. However, simplifications which
* do not reduce the length of the sequence (but improve its
* execution time, for example) may also be performed, and the
* sequence might conceivably increase in length (but normally
* only in order to split up a Mapping into pieces that can be
* more easily merged with their neighbours on subsequent
* invocations of this function).
*
* If Mappings are removed from the sequence, any gaps that
* remain will be closed up, by moving subsequent Mapping
* pointers along in the array, so that vacated elements occur
* at the end. If the sequence increases in length, the array
* will be extended (and its pointer updated) if necessary to
* accommodate any new elements.
*
* Note that any (or all) of the Mapping pointers supplied in
* this array may be annulled by this function, but the Mappings
* to which they refer are not modified in any way (although
* they may, of course, be deleted if the annulled pointer is
* the final one).
* invert_list
* Address of a pointer to a dynamically allocated array which,
* on entry, should contain values to be assigned to the Invert
* attributes of the Mappings identified in the "*map_list"
* array before they are applied (this array might have been
* produced, for example, by the astMapList method). These
* values will be used by this function instead of the actual
* Invert attributes of the Mappings supplied, which are
* ignored.
*
* On exit, the contents of this array will be updated to
* correspond with the possibly modified contents of the
* "*map_list" array. If the Mapping sequence increases in
* length, the "*invert_list" array will be extended (and its
* pointer updated) if necessary to accommodate any new
* elements.
* Returned Value:
* If simplification was possible, the function returns the index
* in the "map_list" array of the first element which was
* modified. Otherwise, it returns -1 (and makes no changes to the
* arrays supplied).
* Notes:
* - A value of -1 will be returned if this function is invoked
* with the global error status set, or if it should fail for any
* reason.
*-
*/
/* This is the default method which is inherited by all Mappings which
do not explicitly provide their own simplification method. Return
-1 to indicate that no simplification is provided. */
return -1;
}
static int *MapSplit( AstMapping *this, int nin, const int *in,
AstMapping **map, int *status ){
/*
*+
* Name:
* astMapSplit
* Purpose:
* Create a Mapping representing a subset of the inputs of an existing
* Mapping.
* Type:
* Protected virtual function.
* Synopsis:
* #include "mapping.h"
* int *astMapSplit( AstMapping *this, int nin, const int *in,
* AstMapping **map )
* Class Membership:
* Mapping method.
* Description:
* This function creates a new Mapping by picking specified inputs from
* an existing Mapping. This is only possible if the specified inputs
* correspond to some subset of the Mapping outputs. That is, there
* must exist a subset of the Mapping outputs for which each output
* depends only on the selected Mapping inputs, and not on any of the
* inputs which have not been selected. Also, any output which is not in
* this subset must not depend on any of the selected inputs. If these
* conditions are not met by the supplied Mapping, then a NULL Mapping
* is returned.
* Parameters:
* this
* Pointer to the Mapping to be split (the Mapping is not
* actually modified by this function).
* nin
* The number of inputs to pick from "this".
* in
* Pointer to an array of indices (zero based) for the inputs which
* are to be picked. This array should have "nin" elements. If "Nin"
* is the number of inputs of the supplied Mapping, then each element
* should have a value in the range zero to Nin-1.
* map
* Address of a location at which to return a pointer to the new
* Mapping. This Mapping will have "nin" inputs (the number of
* outputs may be differetn to "nin"). A NULL pointer will be
* returned if the supplied Mapping has no subset of outputs which
* depend only on the selected inputs. The returned Mapping is a
* deep copy of the required parts of the supplied Mapping.
* Returned Value:
* A pointer to a dynamically allocated array of ints. The number of
* elements in this array will equal the number of outputs for the
* returned Mapping. Each element will hold the index of the
* corresponding output in the supplied Mapping. The array should be
* freed using astFree when no longer needed. A NULL pointer will
* be returned if no output Mapping can be created.
* Notes:
* - If this function is invoked with the global error status set,
* or if it should fail for any reason, then NULL values will be
* returned as the function value and for the "map" pointer.
*-
* Implementation Notes:
* - This function implements the basic astMapSplit method available
* via the protected interface to the Mapping class. The public
* interface to this method is provided by the astMapSplitId_
* function.
*/
/* Local Variables: */
AstCmpMap *rmap; /* Unsimplified result mapping */
AstPermMap *pm; /* PermMap which rearranges the inputs */
int *outperm; /* PermMap output axis permutation array */
int *result; /* Pointer to returned array */
int iin; /* Input index */
int iout; /* Output index */
int mapnin; /* Number of Mapping inputs */
int nout; /* No of outputs */
int ok; /* Can the supplied "in" array be used? */
int perm; /* Are the inputs permuted? */
/* Initialise */
result = NULL;
*map = NULL;
/* Check the global error status. */
if ( !astOK ) return result;
/* Verify the input axis indices.*/
mapnin = astGetNin( this );
for( iin = 0; iin < nin; iin++ ){
if( in[ iin ] < 0 || in[ iin ] >= mapnin ) {
astError( AST__AXIIN, "astMapSplit(%s): One of the supplied Mapping "
"input indices has value %d which is invalid; it should "
"be in the range 1 to %d.", status, astGetClass( this ),
in[ iin ] + 1, mapnin );
break;
}
}
/* Since we are dealing with a basic Mapping, we can only create the
required output Mapping if all inputs are being selected. */
if( nin == mapnin ) {
/* The inputs may have been selected in a different order to that in
which they occur in the supplied Mapping. We therefore create a
PermMap which rearranges the inputs into the order they have in the
supplied Mapping. The supplied "in" array can act as the PermMap's
"inperm" array. Allocate memory for the "outperm" array. */
outperm = astMalloc( sizeof(int)*(size_t) nin );
if( astOK ) {
/* Store the input index for each output in the outperm array and check that
each input has been selected once and only once. Also set a flag
indicating if a PermMap is needed. */
perm = 0;
ok = 1;
for( iout = 0; iout < nin; iout++ ) outperm[ iout ] = -1;
for( iin = 0; iin < nin; iin++ ) {
iout = in[ iin ];
if( outperm[ iout ] != -1 ) {
ok = 0;
break;
} else {
outperm[ iout ] = iin;
}
}
for( iout = 0; iout < nin; iout++ ) {
if( outperm[ iout ] == -1 ) {
ok = 0;
break;
} else if( outperm[ iout ] != iout ) {
perm = 1;
}
}
if( ok ) {
/* Allocate the array to hold the returned output indices. */
nout = astGetNout( this );
result = astMalloc( sizeof(int)*(size_t) nout );
if( astOK ) {
/* The outputs are copied from the supplied Mapping. */
for( iout = 0; iout < nout; iout++ ) result[ iout ] = iout;
/* If the inputs are to be permuted, create the PermMap. */
if( perm ) {
pm = astPermMap( nin, in, nin, outperm, NULL, "", status );
/* The returned Mapping is a series CmpMap containing this PermMap
followed by the supplied Mapping. */
rmap = astCmpMap( pm, this, 1, "", status );
*map = astSimplify( rmap );
rmap = astAnnul( rmap );
/* Annul the PermMap pointer. */
pm = astAnnul( pm );
/* If no input permutation is needed, the resturned Mapping is just the
supplied Mapping. */
} else {
*map = astClone( this );
}
}
}
/* Free resources. */
outperm = astFree( outperm );
}
}
/* Free resources if an error has occurred. */
if( !astOK ) {
result = astFree( result );
*map = astAnnul( *map );
}
/* Return the list of output indices. */
return result;
}
static double MatrixDet( int nrow, int ncol, const double *matrix, int *status ){
/*
* Name:
* MatrixDet
* Purpose:
* Return the determinant of a matrix.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* double MatrixDet( int nrow, int ncol, const double *matrix, int *status )
* Class Membership:
* Mapping member function.
* Description:
* This function returns the determinant of the supplied matrix. Any
* rows or columns that hold only zeros or AST_BAD values are first
* removed from the matrix. If the resulting matrix is not square, a
* value of AST__BAD is returned for the determinant.
* Parameters:
* nrow
* The number of rows in the matrix.
* ncol
* The number of columns in the matrix.
* matrix
* The matrix element values. The first row of "ncol" elements
* should be supplied first, followed by the second row, etc.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The determinant, or AST__BAD if the determinant could not be
* caclculated.
*/
/* Local Variables: */
const double *sqmat;
const double *m;
double *a;
double *y;
double result;
int *iw;
int *usecol;
int *userow;
int i;
int icol;
int irow;
int jf;
int ncoluse;
int ndim;
int nrowuse;
/* Initialise */
result = AST__BAD;
/* Check the global error status. */
if ( !astOK ) return result;
/* Initialise... */
sqmat = NULL;
nrowuse = 0;
ncoluse = 0;
/* Flag any rows and columns that should be ignored because they contain
only bad values or zeros. */
userow = astCalloc( nrow, sizeof( *userow ) );
usecol = astCalloc( ncol, sizeof( *userow ) );
if( astOK ) {
m = matrix;
for( irow = 0; irow < nrow; irow++ ) {
for( icol = 0; icol < ncol; icol++,m++ ) {
if( *m != AST__BAD && *m != 0.0 ) {
usecol[ icol ] = 1;
userow[ irow ] = 1;
}
}
}
/* Find the number of usable rows and columns. */
for( irow = 0; irow < nrow; irow++ ) {
if( userow[ irow ] ) nrowuse++;
}
for( icol = 0; icol < ncol; icol++ ) {
if( usecol[ icol ] ) ncoluse++;
}
}
/* Return AST__BAD if the resulting matrix is not square. */
if( ncoluse == nrowuse ) {
ndim = ncoluse;
/* If any rows or columns contained just bad or zero values, create a new
matrix that excludes them. */
if( ncol > ndim || nrow > ndim ) {
sqmat = astMalloc( ndim*ndim*sizeof(*sqmat) );
if( astOK ) {
m = matrix;
a = (double *) sqmat;
for( irow = 0; irow < nrow; irow++ ) {
if( userow[ irow ] ) {
for( icol = 0; icol < ncol; icol++,m++ ) {
if( usecol[ icol ] ) *(a++) = *m;
}
} else {
m += ncol;
}
}
}
/* If no rows or columns contained just bad values, use the supplied
matrix. */
} else {
sqmat = matrix;
}
/* Calculate the determinant of the modified matrix */
if( ndim == 1 ) {
result = sqmat[ 0 ];
} else if( ndim == 2 ) {
result = sqmat[ 0 ]*sqmat[ 3 ] - sqmat[ 1 ]*sqmat[ 2 ];
} else {
a = astStore( NULL, sqmat, sizeof( double )*(size_t) (ndim*ndim) );
iw = astMalloc( sizeof( int )*(size_t) ndim );
y = astMalloc( sizeof( double )*(size_t) ndim );
if( y ) {
for( i = 0; i < ndim; i++ ) y[ i ] = 1.0;
palDmat( ndim, a, y, &result, &jf, iw );
}
y = astFree( y );
iw = astFree( iw );
a = astFree( a );
}
}
/* Free the square matrix if it was allocated here. */
if( sqmat != matrix ) sqmat = astFree( (void *) sqmat );
/* Free the usable row/column flags. */
userow = astFree( userow );
usecol = astFree( usecol );
return result;
}
static double MaxD( double a, double b, int *status ) {
/*
* Name:
* MaxD
* Purpose:
* Return the maximum of two double values.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* double MaxD( double a, double b, int *status )
* Class Membership:
* Mapping member function.
* Description:
* This function returns the maximum of two double values.
* Parameters:
* a
* The first value.
* b
* The second value.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The maximum.
*/
/* Return the larger value. */
return ( a > b ) ? a : b;
}
static int MaxI( int a, int b, int *status ) {
/*
* Name:
* MaxI
* Purpose:
* Return the maximum of two integer values.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* int MaxI( int a, int b, int *status )
* Class Membership:
* Mapping member function.
* Description:
* This function returns the maximum of two integer values.
* Parameters:
* a
* The first value.
* b
* The second value.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The maximum.
*/
/* Return the larger value. */
return ( a > b ) ? a : b;
}
static int MinI( int a, int b, int *status ) {
/*
* Name:
* MinI
* Purpose:
* Return the minimum of two integer values.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* int MinI( int a, int b, int *status )
* Class Membership:
* Mapping member function.
* Description:
* This function returns the minimum of two integer values.
* Parameters:
* a
* The first value.
* b
* The second value.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The minimum.
*/
/* Return the smaller value. */
return ( a < b ) ? a : b;
}
static double NewVertex( const MapData *mapdata, int lo, double scale,
double x[], double f[], int *ncall, double xnew[], int *status ) {
/*
* Name:
* NewVertex
* Purpose:
* Locate a new vertex for a simplex.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* double NewVertex( const MapData *mapdata, int lo, double scale,
* double x[], double f[], int *ncall, double xnew[], int *status );
* Class Membership:
* Mapping member function.
* Description:
* This function is provided for use during optimisation of a
* Mapping function using the simplex method. It generates the
* coordinates of a new simplex vertex and evaluates the Mapping
* function at that point. If the function's value is better then
* (i.e. larger than) the value at the previously worst vertex,
* then it is used to replace that vertex.
* Parameters:
* mapdata
* Pointer to a MapData structure which describes the Mapping
* function to be used.
* lo
* The (zero-based) index of the simplex vertex which initially
* has the worst (lowest) value.
* scale
* The scale factor to be used to generate the new vertex. The
* distance of the worst vertex from the centre of the face
* opposite it is scaled by this factor to give the new vertex
* position. Negative factors result in reflection through this
* opposite face.
* x
* An array of double containing the coordinates of the vertices
* of the simplex. The coordinates of the first vertex are
* stored first, then those of the second vertex, etc. This
* array will be updated by this function if the new vertex is
* used to replace an existing one.
* f
* An array of double containing the Mapping function values at
* each vertex of the simplex. This array will be updated by
* this function if the new vertex is used to replace an
* existing one.
* ncall
* Pointer to an int containing a count of the number of times
* the Mapping function has been invoked. This value will be
* updated to reflect the actions of this function.
* xnew
* An array of double with one element for each input coordinate
* of the Mapping function. This is used as workspace.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The Mapping function value at the new vertex. This value is
* returned whether or not the new vertex replaces an existing one.
* Notes:
* - A value of AST__BAD will be returned by this function if it is
* invoked with the global error status set, or if it should fail
* for any reason.
* - A value of AST__BAD will also be returned if the new vertex
* lies outside the constrained range of input coordinates
* associated with the Mapping function (as specified in the
* MapData structure supplied) or if any of the transformed output
* coordinates produced by the underlying Mapping is bad. In either
* case the new vertex will not be used to replace an existing one.
*/
/* Local Variables: */
double fnew; /* Function value at new vertex */
double xface; /* Coordinate of centre of magnification */
int coord; /* Loop counter for coordinates */
int ncoord; /* Number of coordinates */
int nvertex; /* Number of simplex vertices */
int vertex; /* Loop counter for vertices */
/* Initialise. */
fnew = AST__BAD;
/* Check the global error status. */
if ( !astOK ) return fnew;
/* Obtain the number of Mapping input coordinates from the MapData
structure and calculate the number of simplex vertices. */
ncoord = mapdata->nin;
nvertex = ncoord + 1;
/* Loop to obtain each coordinate of the new vertex. */
for ( coord = 0; coord < ncoord; coord++ ) {
/* Loop over all vertices except the lowest one and average their
coordinates. This gives the coordinate of the centre of the face
opposite the lowest vertex, which will act as the centre of
magnification. */
xface = 0.0;
for ( vertex = 0; vertex < nvertex; vertex++ ) {
if ( vertex != lo ) {
/* Divide each coordinate by the number of vertices as the sum is
accumulated in order to minimise the risk of overflow. */
xface += x[ vertex * ncoord + coord ] /
( (double ) ( nvertex - 1 ) );
}
}
/* Magnify the lowest vertex's distance from this point by the
required factor to give the coordinates of the new vertex. */
xnew[ coord ] = xface + ( x[ lo * ncoord + coord ] - xface ) * scale;
}
/* Evaluate the Mapping function at the new vertex. */
fnew = MapFunction( mapdata, xnew, ncall, status );
/* If the result is not bad and exceeds the previous value at the
lowest vertex, then replace the lowest vertex with this new one. */
if ( astOK && ( fnew != AST__BAD ) && ( fnew > f[ lo ] ) ) {
for ( coord = 0; coord < ncoord; coord++ ) {
x[ lo * ncoord + coord ] = xnew[ coord ];
}
f[ lo ] = fnew;
}
/* Return the value at the new vertex. */
return fnew;
}
static int QuadApprox( AstMapping *this, const double lbnd[2],
const double ubnd[2], int nx, int ny, double *fit,
double *rms, int *status ){
/*
*++
* Name:
c astQuadApprox
f AST_QUADAPPROX
* Purpose:
* Obtain a quadratic approximation to a 2D Mapping.
* Type:
* Public virtual function.
* Synopsis:
c #include "mapping.h"
c int QuadApprox( AstMapping *this, const double lbnd[2],
c const double ubnd[2], int nx, int ny, double *fit,
c double *rms )
f RESULT = AST_QUADAPPROX( THIS, LBND, UBND, NX, NY, FIT, RMS, STATUS )
* Class Membership:
* Mapping function.
* Description:
* This function returns the co-efficients of a quadratic fit to the
* supplied Mapping over the input area specified by
c "lbnd" and "ubnd".
f LBND and UBND.
* The Mapping must have 2 inputs, but may have any number of outputs.
* The i'th Mapping output is modelled as a quadratic function of the
* 2 inputs (x,y):
*
* output_i = a_i_0 + a_i_1*x + a_i_2*y + a_i_3*x*y + a_i_4*x*x +
* a_i_5*y*y
*
c The "fit"
f The FIT
* array is returned holding the values of the co-efficients a_0_0,
* a_0_1, etc.
* Parameters:
c this
f THIS = INTEGER (Given)
* Pointer to the Mapping.
c lbnd
f LBND( * ) = DOUBLE PRECISION (Given)
c Pointer to an array of doubles
f An array
* containing the lower bounds of a box defined within the input
* coordinate system of the Mapping. The number of elements in this
* array should equal the value of the Mapping's Nin attribute. This
* box should specify the region over which the fit is to be
* performed.
c ubnd
f UBND( * ) = DOUBLE PRECISION (Given)
c Pointer to an array of doubles
f An array
* containing the upper bounds of the box specifying the region over
* which the fit is to be performed.
c nx
f NX = INTEGER (Given)
* The number of points to place along the first Mapping input. The
* first point is at
c "lbnd[0]" and the last is at "ubnd[0]".
f LBND( 1 ) and the last is at UBND( 1 ).
* If a value less than three is supplied a value of three will be used.
c ny
f NY = INTEGER (Given)
* The number of points to place along the second Mapping input. The
* first point is at
c "lbnd[1]" and the last is at "ubnd[1]".
f LBND( 2 ) and the last is at UBND( 2 ).
* If a value less than three is supplied a value of three will be used.
c fit
f FIT( * ) = DOUBLE PRECISION (Returned)
c Pointer to an array of doubles
f An array
* in which to return the co-efficients of the quadratic
* approximation to the specified transformation. This array should
* have at least "6*Nout", elements. The first 6 elements hold the
* fit to the first Mapping output. The next 6 elements hold the
* fit to the second Mapping output, etc. So if the Mapping has 2
* inputs and 2 outputs the quadratic approximation to the forward
* transformation is:
*
c X_out = fit[0] + fit[1]*X_in + fit[2]*Y_in + fit[3]*X_in*Y_in +
c fit[4]*X_in*X_in + fit[5]*Y_in*Y_in
c Y_out = fit[6] + fit[7]*X_in + fit[8]*Y_in + fit[9]*X_in*Y_in +
c fit[10]*X_in*X_in + fit[11]*Y_in*Y_in
f X_out = fit(1) + fit(2)*X_in + fit(3)*Y_in + fit(4)*X_in*Y_in +
f fit(5)*X_in*X_in + fit(6)*Y_in*Y_in
f Y_out = fit(7) + fit(8)*X_in + fit(9)*Y_in + fit(10)*X_in*Y_in +
f fit(11)*X_in*X_in + fit(12)*Y_in*Y_in
*
c rms
f RMS = DOUBLE PRECISION (Returned)
c Pointer to a double in which to return the
f The
* RMS residual between the fit and the Mapping, summed over all
* Mapping outputs.
f STATUS = INTEGER (Given and Returned)
f The global status.
* Returned Value:
c astQuadApprox()
f AST_QUADAPPROX = LOGICAL
* If a quadratic approximation was created,
c a non-zero value is returned. Otherwise zero is returned
f .TRUE is returned. Otherwise .FALSE. is returned
* and the fit co-efficients are set to AST__BAD.
* Notes:
* - This function fits the Mapping's forward transformation. To fit
* the inverse transformation, the Mapping should be inverted using
c astInvert
f AST_INVERT
* before invoking this function.
c - A value of zero
f - A value of .FALSE.
* will be returned if this function is invoked
* with the global error status set, or if it should fail for any
* reason.
*--
*/
/* Local Variables: */
AstPointSet *pset1;
AstPointSet *pset2;
double **pdat1;
double **pdat2;
double *ofit;
double *px;
double *py;
double *pz;
double det;
double dx;
double dy;
double mat[ 6*6 ];
double sx2;
double sx2y2;
double sx2y;
double sx3;
double sx3y;
double sx4;
double sx;
double sxy2;
double sxy3;
double sxy;
double sy2;
double sy3;
double sy4;
double sy;
double sz;
double sz2;
double szx2;
double szx;
double szxy;
double szy2;
double szy;
double x;
double xx;
double xy;
double y;
double yy;
double z;
int i;
int iout;
int iw[ 6 ];
int ix;
int iy;
int n;
int nin;
int nout;
int np;
int ntot;
int result;
int sing;
/* Initialise the returned values. */
result = 0;
fit[ 0 ] = AST__BAD;
*rms = AST__BAD;
ntot = 0;
/* Check the global error status. */
if( !astOK ) return result;
/* Get the number of Mapping inputs and outputs. Report an error if not
correct. */
nin = astGetI( this, "Nin" );
nout = astGetI( this, "Nout" );
if( nin != 2 && astOK ) {
astError( AST__BADNI, "astQuadApprox(%s): Input Mapping has %d %s - "
"it must have 2 inputs.", status, astGetClass( this ), nin,
(nin==1)?"input":"inputs" );
}
/* Ensure we are using at least 3 points on each of the two input axes. */
if( nx < 3 ) nx = 3;
if( ny < 3 ) ny = 3;
/* Get the total number of grid points. */
np = nx*ny;
/* Create a PointSet to hold the 2D grid of input positions. */
pset1 = astPointSet( np, 2, " ", status );
pdat1 = astGetPoints( pset1 );
/* Create a PointSet to hold the N-D grid of output positions. */
pset2 = astPointSet( np, nout, " ", status );
pdat2 = astGetPoints( pset2 );
/* Check the memory allocation (and everything else) was succesful. */
if( astOK ) {
/* Find the cell dimensions on X and Y input axes. */
dx = ( ubnd[ 0 ] - lbnd[ 0 ] )/( nx - 1 );
dy = ( ubnd[ 1 ] - lbnd[ 1 ] )/( ny - 1 );
/* Create a regular grid of input positions. */
px = pdat1[ 0 ];
py = pdat1[ 1 ];
for( iy = 0; iy < ny; iy++ ) {
x = lbnd[ 0 ];
y = lbnd[ 1 ] + iy*dy;
for( ix = 0; ix < nx; ix++ ) {
*(px++) = x;
*(py++) = y;
x += dx;
}
}
/* Use the supplied Mapping to transform this grid into the output space. */
(void) astTransform( this, pset1, 1, pset2 );
/* Assume the approximation can be created. */
result = 1;
*rms = 0.0;
/* Loop round each Mapping output. */
for( iout = 0; iout < nout && astOK; iout++ ) {
/* Get a pointer to the first element of the fit array for this output. */
ofit = fit + 6*iout;
/* Form the required sums. */
n = 0;
sx = 0.0;
sy = 0.0;
sxy = 0.0;
sx2 = 0.0;
sy2 = 0.0;
sx2y = 0.0;
sx3 = 0.0;
sxy2 = 0.0;
sy3 = 0.0;
sx2y2 = 0.0;
sx3y = 0.0;
sxy3 = 0.0;
sx4 = 0.0;
sy4 = 0.0;
sz = 0.0;
sz2 = 0.0;
szx = 0.0;
szy = 0.0;
szxy = 0.0;
szx2 = 0.0;
szy2 = 0.0;
px = pdat1[ 0 ];
py = pdat1[ 1 ];
pz = pdat2[ iout ];
for( i = 0; i < np; i++ ) {
x = *(px++);
y = *(py++);
z = *(pz++);
if( z != AST__BAD ) {
xx = x*x;
yy = y*y;
xy = x*y;
n++;
sx += x;
sy += y;
sxy += xy;
sx2 += xx;
sy2 += yy;
sx2y += xx*y;
sx3 += xx*x;
sxy2 += x*yy;
sy3 += yy*y;
sx2y2 += xx*yy;
sx3y += xx*xy;
sxy3 += xy*yy;
sx4 += xx*xx;
sy4 += yy*yy;
sz += z;
sz2 += z*z;
szx += z*x;
szy += z*y;
szxy += z*xy;
szx2 += z*xx;
szy2 += z*yy;
}
}
/* Form a matrix (M) and vector (V) such that M.X = V, where X is the
solution vector holding the required best fit parameter values (V is
stored in ofit). */
mat[ 0 ] = n;
mat[ 1 ] = sx;
mat[ 2 ] = sy;
mat[ 3 ] = sxy;
mat[ 4 ] = sx2;
mat[ 5 ] = sy2;
mat[ 6 ] = sx;
mat[ 7 ] = sx2;
mat[ 8 ] = sxy;
mat[ 9 ] = sx2y;
mat[ 10 ] = sx3;
mat[ 11 ] = sxy2;
mat[ 12 ] = sy;
mat[ 13 ] = sxy;
mat[ 14 ] = sy2;
mat[ 15 ] = sxy2;
mat[ 16 ] = sx2y;
mat[ 17 ] = sy3;
mat[ 18 ] = sxy;
mat[ 19 ] = sx2y;
mat[ 20 ] = sxy2;
mat[ 21 ] = sx2y2;
mat[ 22 ] = sx3y;
mat[ 23 ] = sxy3;
mat[ 24 ] = sx2;
mat[ 25 ] = sx3;
mat[ 26 ] = sx2y;
mat[ 27 ] = sx3y;
mat[ 28 ] = sx4;
mat[ 29 ] = sx2y2;
mat[ 30 ] = sy2;
mat[ 31 ] = sxy2;
mat[ 32 ] = sy3;
mat[ 33 ] = sxy3;
mat[ 34 ] = sx2y2;
mat[ 35 ] = sy4;
ofit[ 0 ] = sz;
ofit[ 1 ] = szx;
ofit[ 2 ] = szy;
ofit[ 3 ] = szxy;
ofit[ 4 ] = szx2;
ofit[ 5 ] = szy2;
/* Now find the solution vector (the solution over-writes teh current
contents of "ofit"). */
palDmat( 6, mat, ofit, &det, &sing, iw );
/* If the fit failed, fill the coefficient array with bad values. */
if( sing != 0 ) {
for( i = 0; i < 6; i++ ) ofit[ i ] = AST__BAD;
result = 0;
break;
/* If the fit succeeded, update the summ of the squared residuals. */
} else {
ntot += n;
*rms += ofit[ 0 ]*ofit[ 0 ]*n +
2*ofit[ 0 ]*ofit[ 1 ]*sx +
2*ofit[ 0 ]*ofit[ 2 ]*sy +
2*( ofit[ 0 ]*ofit[ 3 ] + ofit[ 1 ]*ofit[ 2 ] )*sxy +
( 2*ofit[ 0 ]*ofit[ 4 ] + ofit[ 1 ]*ofit[ 1 ] )*sx2 +
( 2*ofit[ 0 ]*ofit[ 5 ] + ofit[ 2 ]*ofit[ 2 ] )*sy2 +
2*ofit[ 1 ]*ofit[ 4 ]*sx3 +
2*( ofit[ 1 ]*ofit[ 3 ] + ofit[ 2 ]*ofit[ 4 ] )*sx2y +
2*( ofit[ 1 ]*ofit[ 5 ] + ofit[ 2 ]*ofit[ 3 ] )*sxy2 +
2*ofit[ 2 ]*ofit[ 5 ]*sy3 +
ofit[ 4 ]*ofit[ 4 ]*sx4 +
2*ofit[ 3 ]*ofit[ 4 ]*sx3y +
( 2*ofit[ 4 ]*ofit[ 5 ] + ofit[ 3 ]*ofit[ 3 ] )*sx2y2 +
2*ofit[ 3 ]*ofit[ 5 ]*sxy3 +
ofit[ 5 ]*ofit[ 5 ]*sy4 +
sz2 - 2*(
ofit[ 0 ]*sz +
ofit[ 1 ]*szx +
ofit[ 2 ]*szy +
ofit[ 3 ]*szxy +
ofit[ 4 ]*szx2 +
ofit[ 5 ]*szy2
);
}
}
}
/* Free resources. */
pset1 = astAnnul( pset1 );
pset2 = astAnnul( pset2 );
/* Return AST__BAD if anything went wrong. */
if( !astOK || ntot == 0 ) {
result = 0;
fit[ 0 ] = AST__BAD;
*rms = AST__BAD;
/* Otherwise normalise the returned RMS. */
} else {
if( *rms > 0.0 ) {
*rms = sqrt( *rms/ntot );
} else {
*rms = 0.0;
}
}
/* Return result */
return result;
}
static double Random( long int *seed, int *status ) {
/*
* Name:
* Random
* Purpose:
* Return a pseudo-random value in the range 0 to 1.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* double Random( long int *seed, int *status );
* Class Membership:
* Mapping member function.
* Description:
* This function returns a pseudo-random double value from a PDF
* uniformly distributed in the range 0 to 1. It also updates a
* seed value so that a sequence of pseudo-random values may be
* obtained with successive invocations.
* Parameters:
* seed
* Pointer to a long int which should initially contain a
* non-zero seed value. This will be updated with a new seed
* which may be supplied on the next invocation in order to
* obtain a different pseudo-random value.
* status
* Pointer to the inherited status variable.
* Returned Value:
* The pseudo-random value.
*/
/* Local Variables: */
long int i; /* Temporary storage */
/* This a basic random number generator using constants given in
Numerical Recipes (Press et al.). */
i = *seed / 127773;
*seed = ( *seed - i * 127773 ) * 16807 - i * 2836;
if ( *seed < 0 ) *seed += 2147483647;
/* Return the result as a double value in the range 0 to 1. */
return ( (double) ( *seed - 1 ) ) / (double) 2147483646;
}
static double Rate( AstMapping *this, double *at, int ax1, int ax2,
int *status ){
/*
*+
* Name:
* astRate
* Purpose:
* Calculate the rate of change of a Mapping output.
* Type:
* Protected virtual function.
* Synopsis:
* #include "mapping.h"
* result = astRate( AstMapping *this, double *at, int ax1, int ax2 )
* Class Membership:
* Mapping method.
* Description:
* This function evaluates the rate of change of a specified output of
* the supplied Mapping with respect to a specified input, at a
* specified input position.
*
* The result is the mean gradient within a small interval centred on
* the supplied position. The interval size is selected automatically
* to minimise the error on the returned value. For large intervals,
* the error is dominated by changes in the gradient of the
* transformation. For small intervals, the error is dominated by
* rounding errors. The best interval is the one that gives the most
* consistent measure of the gradient within the interval. To find this
* consistency, each candidate interval is subdivided into eight
* sub-intervals, the mean gradient within each sub-interval is found,
* and the associated consistency measure is then the difference between
* the maximum and minimum sub-interval gradient found within the interval.
* Parameters:
* this
* Pointer to the Mapping to be applied.
* at
* The address of an array holding the axis values at the position
* at which the rate of change is to be evaluated. The number of
* elements in this array should equal the number of inputs to the
* Mapping.
* ax1
* The index of the Mapping output for which the rate of change is to
* be found (output numbering starts at 0 for the first output).
* ax2
* The index of the Mapping input which is to be varied in order to
* find the rate of change (input numbering starts at 0 for the first
* input).
* Returned Value:
* astRate()
* The rate of change of Mapping output "ax1" with respect to input
* "ax2", evaluated at "at", or AST__BAD if the value cannot be
* calculated.
* Notes:
* - A value of AST__BAD will be returned if this function is invoked
* with the global error status set, or if it should fail for any
* reason.
*-
* Implementation Notes:
* - This function implements the basic astRate method available
* via the protected interface to the Mapping class. The public
* interface to this method is provided by the astRateId_
* function.
*/
#define NN 50
/* Local Variables: */
double h0;
double h;
double mean;
double minrange;
double range0;
double range;
double ret;
double x0;
double y[2*NN+1];
double z[2*NN+1];
int ibot;
int iin;
int iret;
int itop;
int nin;
int nout;
/* Initialise */
ret = AST__BAD;
/* Check the global error status. */
if ( !astOK ) return ret;
/* Allocate resources */
RateFun( NULL, NULL, -1, 0, 0, NULL, NULL, status );
/* Obtain the numbers of input and output coordinates for the Mapping. */
nin = astGetNin( this );
nout = astGetNout( this );
/* Validate the output index. */
if ( astOK && ( ax1 < 0 || ax1 >= nout ) ) {
astError( AST__AXIIN, "astRate(%s): The supplied Mapping output "
"index (%d) is invalid; it should be in the range 1 to %d.", status,
astGetClass( this ), ax1 + 1, nout );
}
/* Validate the input index. */
if ( astOK && ( ax2 < 0 || ax2 >= nin ) ) {
astError( AST__AXIIN, "astRate(%s): The supplied Mapping input "
"index (%d) is invalid; it should be in the range 1 to %d.", status,
astGetClass( this ), ax2 + 1, nin );
}
/* Check the Mapping has a forward transformation. */
if ( astOK && !astGetTranForward( this ) ) {
astError( AST__NODEF, "astRate(%s): The supplied Mapping does not "
"have a defined forward transformation.", status,
astGetClass( this ) );
}
/* Save the central value on the Mapping input which is to be varied. */
x0 = at[ ax2 ];
/* If it is bad, return bad values. */
if( astOK && x0 != AST__BAD ) {
/* The required derivative is formed by evaluating the transformation at
two positions close to "x0", and dividing the change in y by the
change in x. The complexity comes in deciding how close to "x0" the
two points should be. If the points are too far apart, the gradient of
the function may vary significantly between the two points and so we
have little confidence that he mean gradient in the interval is a good
estimate of the gradient at "x0". On the other hand if the points are
too close together, rounding errors will make the gradient value
unreliable. The optimal interval is found by testing a number of
different intervals as follows. Each interval is split into NDIV equal
sub-intervals, and the gradient in each sub-interval is found. The max
and min gradient for any of these sub-intervals is found, and the
difference between them is used as an estimate of the reliability of the
mean gradient within the whole interval. The interval with the
greatest reliability is used to define the returned gradient.
The initial estimate of the interval size is a fixed small fraction of
the supplied "x0" value, or 1.0 if "x0" is zero. */
h0 = ( x0 != 0.0 ) ? DBL_EPSILON*1.0E9*fabs( x0 ) : 1.0;
/* Attempt to find the mean gradient, and the range of gradients, within
an interval of size "h0" centred on "x0". If this cannot be done,
increase "h0" by a factor fo ten repeatedly until it can be done, or a
silly large interval size is reached. */
mean = AST__BAD;
while( mean == AST__BAD && h0 < 1.0E-10*DBL_MAX ) {
h0 *= 10;
mean = FindGradient( this, at, ax1, ax2, x0, h0, &range0, status );
}
/* If this was not successful, return AST__BAD as the function value. */
if( mean != AST__BAD ) {
/* We now search through a range of larger interval sizes, to see if any
produce a more reliable mean gradient estimate (i.e. have a smaller range
of gradients within the interval ). After that we search through a range
of smaller interval sizes. The gradient range and mean gradient for
each interval size are stored in arrays "y" and "z" respectively. "iret"
is the index of the most reliable interval found so far (i.e. the one
with the smallest range of sub-interval gradients). The original interval
"h0" is stored in the middle element of these arrays (index "NN").
Intervals are stored in monotonic order of interval size in the arrays. */
iret = NN;
y[ NN ] = range0;
z[ NN ] = mean;
minrange = range0;
/* itop is the index of the last array elements to store calculated values. */
itop = NN;
/* Loop round increasing the interval size by a factor of four each time
round. */
h = h0;
for( iin = NN + 1; iin <= 2*NN && astOK; iin++ ){
h *= 4.0;
/* Calculate the mean gradient, and the range of gradients, using the
current interval size. */
mean = FindGradient( this, at, ax1, ax2, x0, h, &range, status );
/* If it could be done, store the values in the arrays. */
if( mean != AST__BAD ) {
itop++;
z[ itop ] = mean;
y[ itop ] = range;
/* Look for the smallest range, and note its index in the arrays. */
if( range < minrange ) {
minrange = range;
iret = itop;
/* If a range of zero is encountered, we only believe it if the previous
interval also had zero range. Otherwise, it's probably just a numerical
fluke. If the previous interval also had a range of zero, we can forget
the rest of the algorithm since the supplied transformation is linear
and we now have its gradient. So leave the loop. */
} else if( range == 0.0 && y[ iin - 1 ] == 0 ) {
iret = itop;
break;
}
/* Stop looping when the interval range is 100 times the original
interval range. */
if( range > 100*range0 ) break;
}
}
/* Record the minimum range found so far. */
range0 = minrange;
/* ibot is the index of the first array elements to store calculated values. */
ibot = NN;
/* Loop round decreasing the interval size by a factor of four each time
round. This is just like the last loop, but goes the other way, to
lower indices. */
h = h0;
for( iin = NN - 1; iin >= 0 && astOK; iin-- ){
h /= 4.0;
mean = FindGradient( this, at, ax1, ax2, x0, h, &range, status );
if( mean != AST__BAD ) {
ibot--;
z[ ibot ] = mean;
y[ ibot ] = range;
if( range < minrange ) {
minrange = range;
iret = ibot;
} else if( range == 0.0 && y[ iin + 1 ] == 0 ) {
iret = ibot;
break;
}
if( range > 100*range0 ) break;
}
}
/* If the smallest gradient range in any interval was zero, we only
believe it if the adjacent interval size also had zero range. */
if( minrange == 0.0 ) {
if( ( iret > ibot && y[ iret - 1 ] == 0 ) ||
( iret < itop && y[ iret + 1 ] == 0 ) ) {
ret = z[ iret ];
/* Otherwise, search for the smallest gradient range, ignoring values
exactly equal to zero, and return the corresponding mean interval
gradient. */
} else {
for( iin = ibot; iin <= itop; iin++ ){
if( y[ iin ] > 0.0 ){
if( minrange == 0 || y[ iin ] < minrange ) {
minrange = y[ iin ];
ret = z[ iin ];
}
}
}
}
/* If the minimum range was non-zero, we can just return the
corresponding mean gradient. */
} else {
ret = z[ iret ];
}
}
}
/* Free resources */
RateFun( NULL, NULL, -2, 0, 0, NULL, NULL, status );
/* Return the result. */
return ret;
#undef NN
}
static void RateFun( AstMapping *map, double *at, int ax1, int ax2,
int n, double *x, double *y, int *status ) {
/*
* Name:
* RateFun
* Purpose:
* Find the value of the function currently being differentiated by the
* astRate method.
* Type:
* Private function.
* Synopsis:
* #include "mapping.h"
* void RateFun( AstMapping *map, double *at, int ax1, int ax2,
* int n, double *x, double *y, int *status )
* Class Membership:
* Mapping method.
* Description:
* This is a service function for the astRate method. It evaluates the
* function being differentiated at specified axis values.
*
* This function uses static resources in order to avoid the overhead
* of creating new PointSets each time this function is called. These
* static resources which must be initialised before the first invocation
* with a given Mapping, and must be released after the final invocation.
* See "ax1".
* Parameters:
* map
* Pointer to a Mapping which yields the value of the function at x.
* The Mapping may have any number of inputs and outputs; the specific
* output representing the function value, f, is specified by ax1 and
* the specific input representing the argument, x, is specified by ax2.
* at
* A pointer to an array holding axis values at the position at which
* the function is to be evaluated. The number of values supplied
* must equal the number of inputs to the Mapping. The value supplied
* for axis "ax2" is ignored (the value of "x" is used for axis "ax2").
* ax1
* The zero-based index of the Mapping output which is to be
* differentiated. Set this to -1 to allocate, or -2 to release,
* the static resources used by this function.
* ax2
* The zero-based index of the Mapping input which is to be varied.
* n
* The number of elements in the "x" and "y" arrays. This should not
* be greater than 2*RATE_ORDER.
* x
* The value of the Mapping input specified by ax2 at which the
* function is to be evaluated. If "ax2" is set to -1, then the
* supplied value is used as flag indicating if the static resources
* used by this function should be initialised (if x >= 0 ) or
* freed (if x < 0).
* y
* An array in which to return the function values at the positions
* given in "x".
* status
* Pointer to the inherited status variable.
*/
/* Local Variables: */
astDECLARE_GLOBALS
AstPointSet *pset1;
AstPointSet *pset2;
double **ptr1;
double **ptr2;
double *oldx;
double *oldy;
double *p;
double xx;
int i;
int k;
int nin;
int nout;
/* Check the global error status. */
if ( !astOK ) return;
/* Get a pointer to the thread specific global data structure. */
astGET_GLOBALS(map);
/* Initialise variables to avoid "used of uninitialised variable"
messages from dumb compilers. */
pset2 = NULL;
/* If required, initialise things. */
if( ax1 == -1 ) {
for( i = 0; i < RATEFUN_MAX_CACHE; i++ ) {
ratefun_pset_size[ i ] = 0;
ratefun_pset1_cache[ i ] = NULL;
ratefun_pset2_cache[ i ] = NULL;
}
ratefun_next_slot = 0;
/* If required, clean up. */
} else if( ax1 == -2 ) {
for( i = 0; i < RATEFUN_MAX_CACHE; i++ ) {
ratefun_pset_size[ i ] = 0;
if( ratefun_pset1_cache[ i ] ) ratefun_pset1_cache[ i ] = astAnnul( ratefun_pset1_cache[ i ] );
if( ratefun_pset2_cache[ i ] ) ratefun_pset2_cache[ i ] = astAnnul( ratefun_pset2_cache[ i ] );
}
ratefun_next_slot = 0;
/* Otherwise do the transformations. */
} else {
/* See if we have already created PointSets of the correct size. */
pset1 = NULL;
for( i = 0; i < RATEFUN_MAX_CACHE; i++ ) {
if( ratefun_pset_size[ i ] == n ) {
pset1 = ratefun_pset1_cache[ i ];
pset2 = ratefun_pset2_cache[ i ];
break;
}
}
/* If we have not, create new PointSets now. */
if( pset1 == NULL ) {
nin = astGetNin( map );
pset1 = astPointSet( n, nin, "", status );
ptr1 = astGetPoints( pset1 );
nout = astGetNout( map );
pset2 = astPointSet( n, nout, "", status );
ptr2 = astGetPoints( pset2 );
/* Store the input position in the input PointSet. */
for( i = 0; i < nin; i++ ) {
xx = at[ i ];
p = ptr1[ i ];
for( k = 0; k < n; k++, p++ ) *p = xx;
}
/* Add these new PointSets to the cache, removing any existing
PointSets. */
if( ratefun_pset_size[ ratefun_next_slot ] > 0 ) {
(void) astAnnul( ratefun_pset1_cache[ ratefun_next_slot ] );
(void) astAnnul( ratefun_pset2_cache[ ratefun_next_slot ] );
}
ratefun_pset1_cache[ ratefun_next_slot ] = pset1;
ratefun_pset2_cache[ ratefun_next_slot ] = pset2;
ratefun_pset_size[ ratefun_next_slot ] = n;
if( ++ratefun_next_slot == RATEFUN_MAX_CACHE ) ratefun_next_slot = 0;
/* If existing PointSets were found, get there data arrays. */
} else {
ptr1 = astGetPoints( pset1 );
ptr2 = astGetPoints( pset2 );
}
/* Store the input X values in the input PointSet data array. */
oldx = ptr1[ ax2 ];
ptr1[ ax2 ] = x;
/* Store the output Y values in the output PointSet data array. */
oldy = ptr2[ ax1 ];
ptr2[ ax1 ] = y;
/* Transform the positions. */
(void) astTransform( map, pset1, 1, pset2 );
/* Re-instate the original arrays in the PointSets. */
ptr1[ ax2 ] = oldx;
ptr2[ ax1 ] = oldy;
}
}
/*
*++
* Name:
c astRebin
f AST_REBIN