summaryrefslogtreecommitdiffstats
path: root/ast/moc.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2019-05-10 18:45:23 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2019-05-10 18:45:23 (GMT)
commit0b1addf5643c828955a6add53f2a4f7a7a813e41 (patch)
tree94fe32a76399be787746c3c403cca2076a253e29 /ast/moc.c
parent75fab9d80911f74aaab738fa9ab8a4f9b0f57a6b (diff)
downloadblt-0b1addf5643c828955a6add53f2a4f7a7a813e41.zip
blt-0b1addf5643c828955a6add53f2a4f7a7a813e41.tar.gz
blt-0b1addf5643c828955a6add53f2a4f7a7a813e41.tar.bz2
upgrade ast 8.7.1
Diffstat (limited to 'ast/moc.c')
-rw-r--r--ast/moc.c10206
1 files changed, 10206 insertions, 0 deletions
diff --git a/ast/moc.c b/ast/moc.c
new file mode 100644
index 0000000..9ee1ad9
--- /dev/null
+++ b/ast/moc.c
@@ -0,0 +1,10206 @@
+/*
+*class++
+* Name:
+* Moc
+
+* Purpose:
+* An arbitrary region of the sky expressed as a collection of HEALPix
+* cells.
+
+* Constructor Function:
+c astMoc
+f AST_MOC
+
+* Description:
+* The Moc class uses the IVOA MOC (Multi-Order Coverage) recommendation
+* to describe a region on the sky. The region is made up of an
+* arbitrary collection of cells from the HEALPix sky tessellation,
+* and thus may represent any area on the sky, subject to the
+* constraint that the edges of the area correspond to edges of the
+* HEALPix cells. See the MOC recommendation for further information
+* (http://www.ivoa.net/documents/MOC/).
+*
+* The Moc class describes an arbitrary collection of cells on the sky,
+* whereas other subclasses of Region describe exact geometric shapes
+* in any arbitrary domain. This results in some differences between
+* Mocs and other types of Region, the main one being that Mocs have
+* no associated uncertainty.
+*
+* The MOC recommendation requires that a MOC always describes a sky
+* area using the ICRS coordinate system. However, the Moc class, like
+* other subclasses of Region, allows its attributes to be changed so
+* that it represents the equivalent area in any celestial coordinate
+* system that can be mapped to ICRS. See attribute Adaptive.
+*
+* In practice, to use this class an empty Moc object (i.e. a Moc
+* describing a null area of the sky) should first be created using the
+c astMoc
+f AST_MOC
+* constructor. Areas of the sky should then be added into the empty
+* Moc using one or more of the class methods.
+*
+* If it is required to write a Moc out to a FITS binary table, the
+* data value and headers to put in the table can be obtained using
+* methods
+c astGetMocData and astGetMocHeader
+f AST_GETMOCDATA and AST_GETMOCHEADER.
+* The MOC described by an existing FITS binary table can be added
+* into a Moc object using the
+c astAddMocData method.
+f AST_ADDMOCDATA method.
+*
+* Note, this class is limited to MOCs for which the number of cells
+* in the normalised MOC can be represented in a four byte signed integer.
+
+* Inheritance:
+* The Moc class inherits from the Region class.
+
+* Attributes:
+* In addition to those attributes common to all Regions, every
+* Moc also has the following attributes:
+* - MaxOrder: the highest HEALPix order used in the MOC
+* - MaxRes: the best resolution of the MOC, in arc-seconds
+* - MinOrder: the lowest HEALPix order used in the MOC
+* - MinRes: the worst resolution of the MOC, in arc-seconds
+* - MocArea: the area of the MOC
+* - MocLength: the table length used to describe a MOC in FITS
+* - MocType: the data type used to describe a MOC in FITS
+
+* Functions:
+c In addition to those functions applicable to all Regions, the
+c following functions may also be applied to all Mocs:
+f In addition to those routines applicable to all Regions, the
+f following routines may also be applied to all Mocs:
+*
+c - astAddCell: Adds a single HEALPix cell into an existing Moc
+f - AST_ADDCELL: Adds a single HEALPix cell into an existing Moc
+c - astAddMocData: Adds a FITS binary table into an existing Moc
+f - ADT_ADDMOCDATA: Adds a FITS binary table into an existing Moc
+c - astAddMocString: Adds a JSON or string-encoded MOC into an existing Moc
+f - ADT_ADDMOCSTRING: Adds a JSON or string-encoded MOC into an existing Moc
+c - astAddPixelMask<X>: Adds a pixel mask to an existing Moc
+f - AST_ADDPIXELMASK<X>: Adds a pixel mask to an existing Moc
+c - astAddRegion: Adds a Region to an existing Moc
+f - AST_ADDREGION: Adds a Region to an existing Moc
+c - astGetCell: Identify the next cell included in a Moc
+f - AST_GETCELL: Identify the next cell included in a Moc
+c - astGetMocData: Get the FITS binary table data describing a Moc
+f - AST_GETMOCDATA: Get the FITS binary table data describing a Moc
+c - astGetMocHeader: Get the FITS binary table headers describing a Moc
+f - AST_GETMOCHEADER: Get the FITS binary table headers describing a Moc
+c - astGetMocString: Get the JSON or string-encoded form of a Moc
+f - AST_GETMOCSTRING: Get the JSON or string-encoded form of a Moc
+c - astTestCell: Test if a single HEALPix cell is included in a Moc
+f - AST_TESTCELL: Test if a single HEALPix cell is included in a Moc
+
+* Copyright:
+* Copyright (C) 2018 East Asian Observatory
+* All Rights Reserved.
+
+* Licence:
+* This program is free software: you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation, either
+* version 3 of the License, or (at your option) any later
+* version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General
+* License along with this program. If not, see
+* <http://www.gnu.org/licenses/>.
+
+* Authors:
+* DSB: David S. Berry (Starlink)
+
+* History:
+* 10-OCT-2018 (DSB):
+* Original version.
+* 6-MAY-2019 (DSB):
+* Added methods astAddMocString and astGetMocString.
+* 7-MAY-2019 (DSB):
+* Modify astAddMocString and astGetMocString so that they can
+* handle JSON encoding as well as string encodiing.
+*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 Moc
+
+/* Date type identification codes */
+#define LONG_DOUBLE 0
+#define DOUBLE 1
+#define LONG_INT 2
+#define UNSIGNED_LONG_INT 3
+#define INT 4
+#define UNSIGNED_INT 5
+#define SHORT_INT 6
+#define UNSIGNED_SHORT_INT 7
+#define SIGNED_CHAR 8
+#define UNSIGNED_CHAR 9
+#define FLOAT 10
+
+/* The declination (rads) at the transition between the polar and equatorial
+ regions of the HEALPix projection - arcsin(2/3). */
+#define THETAX 0.729727656226966
+
+/* A value that is slightly less than to 0.5. Used as increments to be
+ added to the grid coords at a cell centre, in order to get the grid
+ coords at the cell edges. Using exactly 0.5 causes problems for cells
+ that are adjacent to a discontinuity. */
+#define HALF 0.49999
+
+/* The maximum "npix" value for a given order. */
+#define MaxNpix(order) (12*(1L<<(2*(order)))-1)
+
+
+/* Include files. */
+/* ============== */
+/* Interface definitions. */
+/* ---------------------- */
+
+#include "channel.h" /* I/O channels */
+#include "cmpmap.h" /* Compound mappings */
+#include "cmpregion.h" /* AST__AND/OR/XOR */
+#include "error.h" /* Error reporting facilities */
+#include "fitschan.h" /* Converts FITS headers to FrameSets */
+#include "frame.h" /* Coordinate system description */
+#include "frameset.h" /* Collections of inter-related Frames */
+#include "globals.h" /* Thread-safe global data access */
+#include "mapping.h" /* Position mappings */
+#include "matrixmap.h" /* Matrix Mappings */
+#include "memory.h" /* Memory allocation facilities */
+#include "moc.h" /* Interface definition for this class */
+#include "object.h" /* Base Object class */
+#include "permmap.h" /* Axis permutations */
+#include "polygon.h" /* AST__LE/GT etc */
+#include "region.h" /* Frame regions (parent class) */
+#include "shiftmap.h" /* Shift of origin Mappings */
+#include "skyframe.h" /* Celestial coordinate systems */
+#include "unitmap.h" /* Unit Mappings */
+#include "wcsmap.h" /* AST__DR2D */
+#include "winmap.h" /* Mappings between windows */
+#include "xphmap.h" /* Mappings between HPX projection types */
+#include "circle.h" /* Circles */
+
+/* Error code definitions. */
+/* ----------------------- */
+#include "ast_err.h" /* AST error codes */
+
+/* C header files. */
+/* --------------- */
+#include <ctype.h>
+#include <float.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+/* Type definitions. */
+/* ================= */
+typedef struct PixelMask {
+ const void *data;
+ void *value;
+ void *bad;
+ int type;
+ int nx;
+ int ny;
+ int oper;
+} PixelMask;
+
+typedef struct CellList {
+ int maxorder;
+ AstXphMap *maps[ AST__MXORDHPX + 1 ];
+ double *(ix[ AST__MXORDHPX + 1 ]);
+ double *(iy[ AST__MXORDHPX + 1 ]);
+ int len[ AST__MXORDHPX + 1 ];
+} CellList;
+
+
+struct Cell;
+struct Corner;
+
+typedef struct Cell {
+ struct Corner *bl;
+ struct Corner *tl;
+ struct Corner *tr;
+ struct Corner *br;
+ int ix;
+ int iy;
+ char interior;
+ struct Cell *prev;
+} Cell;
+
+typedef struct Corner {
+ double ra;
+ double dec;
+ double cosdec;
+ struct Cell *cells[4];
+ int ncell;
+ char interior;
+ int dist;
+ struct Corner *prev;
+} Corner;
+
+typedef struct SinkData {
+ char *string;
+ size_t mxsize;
+} SinkData;
+
+typedef struct SourceData {
+ const char *string;
+ size_t mxsize;
+} SourceData;
+
+typedef struct List {
+ int64_t nval;
+ size_t *values;
+} List;
+
+
+
+/* Module Variables. */
+/* ================= */
+
+/* Table used by log2_64 function. */
+static const int Tab64[64] = {
+ 63, 0, 58, 1, 59, 47, 53, 2,
+ 60, 39, 48, 27, 54, 33, 42, 3,
+ 61, 51, 37, 40, 49, 18, 28, 20,
+ 55, 30, 34, 11, 43, 14, 22, 4,
+ 62, 57, 46, 52, 38, 26, 32, 41,
+ 50, 36, 17, 19, 29, 10, 13, 21,
+ 56, 45, 25, 31, 35, 16, 9, 12,
+ 44, 24, 15, 8, 23, 7, 6, 5};
+
+/* Table used by log2_32 function. */
+static const int Tab32[32] = {
+ 0, 9, 1, 10, 13, 21, 2, 29,
+ 11, 14, 16, 18, 22, 25, 3, 30,
+ 8, 12, 20, 28, 15, 17, 24, 7,
+ 19, 27, 23, 6, 26, 5, 4, 31};
+
+/* 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 * );
+static int (* parent_equal)( AstObject *, AstObject *, int * );
+
+
+#ifdef THREAD_SAFE
+/* Define how to initialise thread-specific globals. */
+#define GLOBAL_inits \
+ globals->Comp_Corner_tol = 0.0; \
+ globals->Comp_Corner_exact = 0; \
+ globals->Comp_Corner_loop = 0; \
+ globals->Comp_Decra_ptr1 = NULL; \
+ globals->Comp_Decra_ptr2 = NULL; \
+ globals->Class_Init = 0; \
+ globals->GetAttrib_Buff[ 0 ] = 0;
+
+/* Create the function that initialises global data for this module. */
+astMAKE_INITGLOBALS(Moc)
+
+/* Define macros for accessing each item of thread specific global data. */
+#define class_init astGLOBAL(Moc,Class_Init)
+#define Comp_Corner_Tol astGLOBAL(Moc,Comp_Corner_tol)
+#define Comp_Corner_Exact astGLOBAL(Moc,Comp_Corner_exact)
+#define Comp_Corner_Loop astGLOBAL(Moc,Comp_Corner_loop)
+#define class_vtab astGLOBAL(Moc,Class_Vtab)
+#define getattrib_buff astGLOBAL(Moc,GetAttrib_Buff)
+#define Comp_Decra_Ptr1 astGLOBAL(Moc,Comp_Decra_ptr1)
+#define Comp_Decra_Ptr2 astGLOBAL(Moc,Comp_Decra_ptr2)
+
+
+#include <pthread.h>
+
+
+#else
+
+static char getattrib_buff[ 51 ];
+
+/* Define the class virtual function table and its initialisation flag
+ as static variables. */
+static AstMocVtab class_vtab; /* Virtual function table */
+static int class_init = 0; /* Virtual function table initialised? */
+
+/* Used to pass data to qsort comparison functions */
+static double Comp_Corner_Tol = 0.0;
+static int Comp_Corner_Exact = 0;
+static int Comp_Corner_Loop = 0;
+static double *Comp_Decra_Ptr1 = NULL;
+static double *Comp_Decra_Ptr2 = NULL;
+
+#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. */
+AstMoc *astMocId_( const char *, ... );
+
+/* Prototypes for Private Member Functions. */
+/* ======================================== */
+static AstFitsChan *GetMocHeader( AstMoc *, int * );
+static AstFrame *FindSkyAxes( AstFrame *, const char *, int * );
+static AstFrameSet *GetHPX12FrameSet( AstMoc *, int, int * );
+static AstMapping *GetCachedMapping( AstMoc *, int, const char *, int * );
+static AstPointSet *RegBaseMesh( AstRegion *, int * );
+static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
+static AstRegion *GetDefUnc( AstRegion *, int * );
+static Cell *MakeCell( int, int, int, Cell **, int * );
+static double GetMocArea( AstMoc *, int * );
+static double GetPixelArea( AstFrameSet *, const int *, int * );
+static double OrderToRes( int order );
+static int Comp_corner( const void *, const void * );
+static int Comp_decra( const void *, const void * );
+static int Comp_range( const void *, const void * );
+static int Comp_int64( const void *, const void * );
+static int Equal( AstObject *, AstObject *, int * );
+static int RegPins( AstRegion *, AstPointSet *, AstRegion *, int **, int * );
+static int RegTrace( AstRegion *, int, double *, double **, int * );
+static int ResToOrder( double );
+static int log2_32( int );
+static int log2_64( int64_t );
+static int TestCell( AstMoc *, int, int64_t, int, int * );
+static int64_t XyToNested( int, int, int );
+static void AddCell( AstMoc *, int, int, int64_t, int * );
+static void AddMocData( AstMoc *, int, int, int, int, int, const void *, int * );
+static void AddMocString( AstMoc *, int, int, int, size_t, const char *, int *, int * );
+static void AddRegion( AstMoc *, int, AstRegion *, int * );
+static void AppendChildren( AstMoc *, Cell *, int, Cell **, int *);
+static void ClearCache( AstMoc *, int * );
+static void CombineRanges( AstMoc *, int, const char *, int * );
+static void Copy( const AstObject *, AstObject *, int * );
+static void Delete( AstObject *, int * );
+static void Dump( AstObject *, AstChannel *, int * );
+static void GetCell( AstMoc *, int, int *, int64_t *, int * );
+static void GetMocData( AstMoc *, size_t, void *, int * );
+static void GetMocString( AstMoc *, int, size_t, char *, size_t *, int * );
+static void GetNorm( AstMoc *, const char *, int * );
+static void IncorporateCells( AstMoc *, CellList *, int, int, const char *, int * );
+static void MakeCorners( AstMoc *, int, Cell *, Corner **, int, int * );
+static void MergeRanges( AstMoc *, int, int * );
+static void NegateRanges( AstMoc *, int, int, int * );
+static void NestedToXy( int64_t, int, int *, int * );
+static void PutCell( AstMoc *, AstMapping **, int, int, int, CellList *, int, void *, int, const char *, int * );
+static void RegBaseBox( AstRegion *, double *, double *, int * );
+static void Sink1( void *, size_t, const char *, int * );
+static void Sink2( void *, size_t, const char *, int * );
+static const char *Source1( void *, size_t *, int * );
+static void TestPixels( PixelMask *, int *, AstPointSet *, int[9], int *);
+
+/* For debugging of astRegBaseMesh and astRegTrace......
+static void dump_cell( AstMoc *, Cell *, int );
+static void dump_corner( Corner *this, int );
+static void dump_moc( AstMoc *, const char *, int *); */
+
+static const char *GetAttrib( AstObject *, const char *, int * );
+static void ClearAttrib( AstObject *, const char *, int * );
+static void SetAttrib( AstObject *, const char *, int * );
+static int TestAttrib( AstObject *, const char *, int * );
+
+static int GetMaxOrder( AstMoc *, int * );
+static int TestMaxOrder( AstMoc *, int * );
+static void ClearMaxOrder( AstMoc *, int * );
+static void SetMaxOrder( AstMoc *, int, int * );
+
+static int GetMinOrder( AstMoc *, int * );
+static int TestMinOrder( AstMoc *, int * );
+static void ClearMinOrder( AstMoc *, int * );
+static void SetMinOrder( AstMoc *, int, int * );
+
+static int GetMocType( AstMoc *, int * );
+static int GetMocLength( AstMoc *, int * );
+
+
+
+/* Define a macro that expands to a single prototype for function
+ AddPixelMask for a given data type. Then use it to define all
+ AddPixelMask prototypes for all data types. */
+#define ADDPIXELMASK_PROTO(X,Xtype) \
+static void AddPixelMask##X( AstMoc *, int, AstFrameSet *, Xtype, int, \
+ int, Xtype, const Xtype[], const int[2], int * );
+
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+ADDPIXELMASK_PROTO(LD,long double)
+#endif
+ADDPIXELMASK_PROTO(D,double)
+ADDPIXELMASK_PROTO(L,long int)
+ADDPIXELMASK_PROTO(UL,unsigned long int)
+ADDPIXELMASK_PROTO(I,int)
+ADDPIXELMASK_PROTO(UI,unsigned int)
+ADDPIXELMASK_PROTO(S,short int)
+ADDPIXELMASK_PROTO(US,unsigned short int)
+ADDPIXELMASK_PROTO(B,signed char)
+ADDPIXELMASK_PROTO(UB,unsigned char)
+ADDPIXELMASK_PROTO(F,float)
+
+/* Define a macro that expands to a single prototype for function
+ GetSelectionBounds for a given data type and operation. */
+#define GETSELECTIONBOUNDS_PROTO0(X,Xtype,Oper) \
+static int GetSelectionBounds##Oper##X( Xtype, const Xtype[], const int[ 2 ], int *, int *, int *, int *, int * );
+
+/* Define a macro that expands to a set of prototypes for all operations
+ for function GetSelectionBounds for a given data type. */
+#define GETSELECTIONBOUNDS_PROTO(X,Xtype) \
+GETSELECTIONBOUNDS_PROTO0(X,Xtype,LT) \
+GETSELECTIONBOUNDS_PROTO0(X,Xtype,LE) \
+GETSELECTIONBOUNDS_PROTO0(X,Xtype,EQ) \
+GETSELECTIONBOUNDS_PROTO0(X,Xtype,GE) \
+GETSELECTIONBOUNDS_PROTO0(X,Xtype,GT) \
+GETSELECTIONBOUNDS_PROTO0(X,Xtype,NE)
+
+/* Use the above macros to define all GetSelectionBounds prototypes for all
+ data types and operations. */
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+GETSELECTIONBOUNDS_PROTO(LD,long double)
+#endif
+GETSELECTIONBOUNDS_PROTO(D,double)
+GETSELECTIONBOUNDS_PROTO(L,long int)
+GETSELECTIONBOUNDS_PROTO(UL,unsigned long int)
+GETSELECTIONBOUNDS_PROTO(I,int)
+GETSELECTIONBOUNDS_PROTO(UI,unsigned int)
+GETSELECTIONBOUNDS_PROTO(S,short int)
+GETSELECTIONBOUNDS_PROTO(US,unsigned short int)
+GETSELECTIONBOUNDS_PROTO(B,signed char)
+GETSELECTIONBOUNDS_PROTO(UB,unsigned char)
+GETSELECTIONBOUNDS_PROTO(F,float)
+
+
+
+/* Member functions. */
+/* ================= */
+static void AddCell( AstMoc *this, int cmode, int order, int64_t npix,
+ int *status ) {
+/*
+*++
+* Name:
+c astAddCell
+f AST_ADDCELL
+
+* Purpose:
+* Adds a single HEALPix cell into an existing Moc.
+
+* Type:
+* Public virtual function.
+
+* Synopsis:
+c #include "moc.h"
+c void astAddCell( AstMoc *this, int cmode, int order, int64_t npix )
+f CALL AST_ADDCELL( THIS, CMODE, ORDER, NPIX, STATUS )
+
+* Class Membership:
+* Moc method.
+
+* Description:
+* This function modifies a Moc by combining it with a single
+* specified HEALPix cell. The way in which they are combined is
+* determined by the
+c "cmode" parameter.
+f CMODE parameter.
+
+* Parameters:
+c this
+f THIS = INTEGER (Given)
+* Pointer to the Moc to be modified.
+c cmode
+f CMODE = INTEGER (Given)
+* Indicates how the Moc and specified cell are to be combined. Any
+* of the following values may be supplied:
+* - AST__AND: If the specified cell is included in the Moc, it is
+* removed. Otherwise the Moc is left unchanged.
+* - AST__OR: If the specified cell is not included in the Moc, it is
+* added. Otherwise the Moc is left unchanged.
+* - AST__XOR: The specified cell is toggled - it is removed from
+* the Moc if originally present, and it is added to the Moc if not
+* originally present.
+c order
+f ORDER = INTEGER (Given)
+* The HEALPix order of the cell. An error is reported if this is
+* higher than the maximum order allowed in the Moc (as given by its
+* MaxOrder attribute). If no value has been set for the MaxOrder
+* attribute, calling this method causes it to be set to the supplied
+* order value. So the highest order cells should usually be added
+* first.
+c npix
+f NPIX = INTEGER*8 (Given)
+* The "npix" value identifying the required cell (see the MOC
+* recommendation for more details).
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+*--
+*/
+
+/* Local Variables: */
+ int irange;
+ int maxorder;
+ int shift;
+ int64_t *pr;
+ int64_t ihigh;
+ int64_t ilow;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Validate */
+ if( order < 0 || order > AST__MXORDHPX ) {
+ astError( AST__INVAR, "astAddCell(%s): Invalid value (%d) "
+ "supplied for parameter 'order' - must be no greater "
+ "than %d.", status, astGetClass( this ), order,
+ AST__MXORDHPX );
+
+ } else if( npix < 0 || npix > MaxNpix( order ) ) {
+ astError( AST__INVAR, "astAddCell(%s): Invalid value (%zu) "
+ "supplied for parameter 'npix' - must be greater "
+ "than 0 and less than %zu.", status, astGetClass( this ),
+ npix, MaxNpix( order ) + 1 );
+
+/* Get MaxOrder, if set, and check "order" is no greater than MaxOrder. */
+ } else if( astTestMaxOrder( this ) ) {
+ maxorder = astGetMaxOrder( this );
+ if( maxorder < order && astOK ) {
+ astError( AST__INVAR, "astAddCell(%s): Invalid value (%d) "
+ "supplied for parameter 'order' - must be no "
+ "greater than the Moc's MaxOrder attribute (%d).",
+ status, astGetClass( this ), order, maxorder );
+ }
+
+/* If MaxOrder is not set, set it to "order". */
+ } else {
+ maxorder = order;
+ astSetMaxOrder( this, order );
+ }
+
+/* Get the upper and lower bounds of the range of cells at MaxOrder
+ that corresponds to the specified cell. */
+ if( astOK ) {
+ shift = 2*( maxorder - order );
+ ilow = ( npix << shift );
+ ihigh = ( (npix + 1 ) << shift ) - 1;
+
+/* Add this range to the array of ranges in the Moc. */
+ irange = this->nrange++;
+ this->range = astGrow( this->range, this->nrange, 2*sizeof(*(this->range)) );
+ if( astOK ) {
+ pr = this->range + 2*irange;
+ pr[ 0 ] = ilow;
+ pr[ 1 ] = ihigh;
+ }
+
+/* Normalise the Moc. */
+ astMocNorm( this, cmode == AST__AND, cmode, irange, maxorder,
+ "astAddCell" );
+ }
+}
+
+static void AddMocData( AstMoc *this, int cmode, int negate, int maxorder,
+ int len, int nbyte, const void *data, int *status ) {
+/*
+*++
+* Name:
+c astAddMocData
+f AST_ADDMOCDATA
+
+* Purpose:
+* Adds a FITS binary table into an existing Moc.
+
+* Type:
+* Public virtual function.
+
+* Synopsis:
+c #include "moc.h"
+c void astAddMocData( AstMoc *this, int cmode, int negate, int maxorder,
+c int len, int nbyte, const void *data );
+f CALL AST_ADDMOCDATA( THIS, CMODE, NEGATE, MAXORDER, LEN, NBYTE, DATA,
+f STATUS )
+
+* Class Membership:
+* Moc method.
+
+* Description:
+* This function modifies a Moc by combining it with a binary data array
+* describing a MOC read from a FITS binary table. The way in which they
+* are combined is determined by the
+c "cmode" parameter.
+f CMODE parameter.
+
+* Parameters:
+c this
+f THIS = INTEGER (Given)
+* Pointer to the Moc to be modified.
+c cmode
+f CMODE = INTEGER (Given)
+* Indicates how the Moc and data are to be combined. Any of the
+* following values may be supplied:
+* - AST__AND: The modified Moc is the intersection of the original
+* Moc and the data.
+* - AST__OR: The modified Moc is the union of the original Moc and
+* the data.
+* - AST__XOR: The modified Moc is the exclusive disjunction of the
+* original Moc and the data.
+c negate
+f NEGATE = LOGICAL (Given)
+* If
+c non-zero,
+f .FALSE.,
+* the cells added to the Moc will be those included in the
+* supplied data array.
+* If
+c zero,
+f .TRUE.,
+* the cells added to the Moc will be those not included in the
+* supplied data array.
+c maxorder
+f MAXORDER = INTEGER (Given)
+* The maximum HEALPix order to use. If a negative value is supplied,
+* the maximum order will be determined by searching the data array
+* (this will take extra time). In either case, if a value has already
+* been set for the MaxOrder attribute in the Moc, then the attribute
+* value is used in preference to the value supplied for this parameter.
+* Any HEALPix cells in the data array that refer to an order greater
+* than
+c "maxorder"
+f MAXORDER
+* are ignored.
+c len
+f LEN = INTEGER (Given)
+* The length of the supplied array (i.e. the number of 4 or 8 byte
+* integer values it contains). Note, this class only supports binary
+* MOCs with lengths that can be represented in a 4 byte signed
+* integer.
+c nbyte
+f NBYTE = INTEGER (Given)
+* The number of bytes in each integer value stored in the supplied
+* array. Must be 4 or 8.
+c data
+f DATA( * ) = BYTE (Given)
+c Pointer to the
+f The
+* data array holding a description of a MOC in the form used by
+* FITS binary tables. See the IVOA MOC recommendation for details.
+* The values in this array are signed integers, each with the
+* number of bytes specified by parameter
+c "nbyte".
+f NBYTE.
+* The number of bytes in this array should be at least
+c "len*nbyte".
+f LEN*NBYTE.
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Notes:
+* - If no value has yet been set for attribute MaxOrder, then this
+* function will automatically set it to the value supplied for
+c "Maxorder",
+f MAXORDER,
+* or to the largest order present in the supplied binary MOC if
+c "Maxorder" is negative.
+f MAXORDER is negative.
+*--
+*/
+
+/* Local Variables: */
+ const int *pni;
+ const int64_t *pnk;
+ int icell;
+ int irange;
+ int nold;
+ int order;
+ int shift;
+ int64_t *pr;
+ int64_t ihigh;
+ int64_t ilow;
+ int64_t npix;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Validate */
+ if( nbyte != 4 && nbyte != 8 ) {
+ astError( AST__INVAR, "astAddMocData(%s): Invalid value (%d) "
+ "supplied for parameter 'nbyte' - must be 4 or 8",
+ status, astGetClass( this ), nbyte );
+
+ } else if( maxorder > AST__MXORDHPX ) {
+ astError( AST__INVAR, "astAddMocData(%s): Invalid value (%d) "
+ "supplied for parameter 'maxorder' - must be no greater "
+ "than %d.", status, astGetClass( this ), maxorder,
+ AST__MXORDHPX );
+
+ } else if( len < 0 ) {
+ astError( AST__INVAR, "astAddMocData(%s): Invalid value (%d) "
+ "supplied for parameter 'len' - must be greater "
+ "than 0.", status, astGetClass( this ), len );
+
+/* If the supplied MOC is empty (i.e. len==0) then the resulting Moc
+ will be unchanged unless "cmode" is AST__AND, in which case the
+ resulting Moc will be empty. */
+ } else if( len == 0 ) {
+ if( cmode == AST__AND ) {
+ this->nrange = 0;
+ this->range = astFree( this->range );
+ ClearCache( this, status );
+ }
+
+/* Otherwise, read a MOC from the data array and combine it with the
+ supplied Moc. */
+ } else {
+
+/* If the MaxOrder attribute is set in the Moc, use it in preference to
+ the value supplied for parameter "maxorder". */
+ if( astTestMaxOrder( this ) ) {
+ maxorder = astGetMaxOrder( this );
+
+/* Otherwise, we use the supplied "maxorder" value. If "maxorder" was
+ not supplied (i.e. is negative), make an initial pass through the array
+ to determine the maximum order. */
+ } else {
+ if( maxorder < 0 ) {
+ if( nbyte == 4 ) {
+ pni = data;
+ for( icell = 0; icell < len; icell++ ) {
+ order = log2_32( *(pni++) / 4 ) / 2;
+ if( order > maxorder ) maxorder = order;
+ }
+ } else {
+ pnk = data;
+ for( icell = 0; icell < len; icell++ ) {
+ order = log2_64( *(pnk++) / 4 ) / 2;
+ if( order > maxorder ) maxorder = order;
+ }
+ }
+ }
+
+/* Use this value as the Moc's MaxOrder attribute value from now on. */
+ astSetMaxOrder( this, maxorder );
+ }
+
+/* Record the orginal number of ranges in the Moc. */
+ nold =this->nrange;
+
+/* Convert the supplied MOC data to a list of ranges of cells at
+ "maxorder" and append to the end of the ranges currently in the Moc. */
+ if( nbyte == 4 ) {
+ pni = data;
+ } else {
+ pnk = data;
+ }
+ for( icell = 0; icell < len; icell++ ) {
+
+/* Decode the data value (a "nuniq" value) to get the order and npix, using
+ a fast log2 function. */
+ if( nbyte == 4 ) {
+ order = log2_32( *pni / 4 ) / 2;
+ npix = *(pni++) - ( 1 << (2 + 2*order) );
+ } else {
+ order = log2_64( *pnk / 4 ) / 2;
+ npix = *(pnk++) - ( 1L << (2 + 2*order) );
+ }
+
+/* Ignore cells at orders higher than maxorder. */
+ if( order <= maxorder ) {
+
+/* Get the upper and lower bounds of the cells at maxorder contained
+ within this cell at order. */
+ shift = 2*( maxorder - order );
+ ilow = ( npix << shift );
+ ihigh = ( (npix + 1 ) << shift ) - 1;
+
+/* Append this as a new range to the Moc. */
+ irange = this->nrange++;
+ this->range = astGrow( this->range, this->nrange, 2*sizeof(*(this->range)) );
+ if( astOK ) {
+ pr = this->range + 2*irange;
+ pr[ 0 ] = ilow;
+ pr[ 1 ] = ihigh;
+ } else {
+ break;
+ }
+ }
+ }
+
+/* Normalise the Moc. */
+ astMocNorm( this, negate, cmode, nold, maxorder, "astAddMocData" );
+ }
+}
+
+static void AddMocString( AstMoc *this, int cmode, int negate, int maxorder,
+ size_t len, const char *string, int *json,
+ int *status ) {
+/*
+*++
+* Name:
+c astAddMocString
+f AST_ADDMOCSTRING
+
+* Purpose:
+* Adds a JSON or string-encoded MOC into an existing Moc.
+
+* Type:
+* Public virtual function.
+
+* Synopsis:
+c #include "moc.h"
+c int astAddMocString( AstMoc *this, int cmode, int negate, int maxorder,
+c size_t len, const char*string, int *json );
+f CALL AST_ADDMOCSTRING( THIS, CMODE, NEGATE, MAXORDER, LEN, STRING,
+f JSON, STATUS )
+
+* Class Membership:
+* Moc method.
+
+* Description:
+* This function modifies a Moc by combining it with the MOC described
+* by the supplied string - assumed to be encoded using either the string
+* or JSON serialisation described in the MOC recommendation. The way in
+* which they are combined is determined by the
+c "cmode" parameter.
+f CMODE parameter.
+
+* Parameters:
+c this
+f THIS = INTEGER (Given)
+* Pointer to the Moc to be modified.
+c cmode
+f CMODE = INTEGER (Given)
+* Indicates how the supplied MOC is to be combined with the
+* existing Moc. Any of the following values may be supplied:
+* - AST__AND: The modified Moc is the intersection of the original
+* Moc and the sipplied MOC.
+* - AST__OR: The modified Moc is the union of the original Moc and
+* the supplied MOC.
+* - AST__XOR: The modified Moc is the exclusive disjunction of the
+* original Moc and the supplied MOC.
+c negate
+f NEGATE = LOGICAL (Given)
+* If
+c non-zero,
+f .FALSE.,
+* the cells added to the existing Moc will be those included in the
+* supplied MOC.
+* If
+c zero,
+f .TRUE.,
+* the cells added to the existing Moc will be those not included in the
+* supplied MOC.
+c maxorder
+f MAXORDER = INTEGER (Given)
+* The maximum HEALPix order to use. If a negative value is supplied,
+* the maximum order will be determined by searching the supplied MOC
+* (this will take extra time). In either case, if a value has already
+* been set for the MaxOrder attribute in the Moc, then the attribute
+* value is used in preference to the value supplied for this parameter.
+* Any HEALPix cells in the supplied MOC that refer to an order greater
+* than
+c "maxorder"
+f MAXORDER
+* are ignored.
+c len
+f LEN = INTEGER (Given)
+* The number of characters to read from the supplied string. If
+* this is greater than the length of the string, it is ignored and the
+* whole string is read.
+c string
+f STRING = CHARACTER * ( * ) (Given)
+c Pointer to the
+f The
+* array of characters holding the supplied MOC. It should be
+* encoded using either the string or JSON serialisation described
+* in the MOC recommendation. The used serialisation is determined
+* from the first non-blank character, which should be either a
+* curly brace ('{'- JSON serialisation) or a digit (string
+* serialisation).
+c json
+f JSON = LOGICAL (Returned)
+c Pointer to an int in which to return a
+f A
+* boolean flag indicating if the supplied string was interpreted
+c using the JSON (non-zero) or string (zero) serialisation.
+f using the JSON (.TRUE.) or string (.FALSE.) serialisation.
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Notes:
+* - If no value has yet been set for attribute MaxOrder, then this
+* function will automatically set it to the value supplied for
+c "Maxorder",
+f MAXORDER,
+* or to the largest order present in the supplied string MOC if
+c "Maxorder" is negative.
+f MAXORDER is negative.
+*--
+*/
+
+/* Local Variables: */
+ int nold;
+ SourceData data;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Validate */
+ if( maxorder > AST__MXORDHPX ) {
+ astError( AST__INVAR, "astAddMocString(%s): Invalid value (%d) "
+ "supplied for parameter 'maxorder' - must be no greater "
+ "than %d.", status, astGetClass( this ), maxorder,
+ AST__MXORDHPX );
+
+/* If the supplied MOC is empty (i.e. len==0) then the resulting Moc
+ will be unchanged unless "cmode" is AST__AND, in which case the
+ resulting Moc will be empty. */
+ } else if( len == 0 ) {
+ if( cmode == AST__AND ) {
+ this->nrange = 0;
+ this->range = astFree( this->range );
+ ClearCache( this, status );
+ }
+
+/* Otherwise, read a MOC from the string and combine it with the supplied
+ Moc. */
+ } else {
+ nold = this->nrange;
+ data.string = string;
+ data.mxsize = len;
+ astAddMocText( this, maxorder, Source1, &data, "astAddMocString", json );
+ astMocNorm( this, negate, cmode, nold, astGetMaxOrder( this ),
+ "astAddMocString" );
+ }
+}
+
+void astAddMocText_( AstMoc *this, int maxorder,
+ const char *(*source)( void *, size_t *nc, int * ),
+ void *data, const char *method, int *json, int *status ){
+/*
+*+
+* Name:
+* astAddMocText
+
+* Purpose:
+* Adds a JSON or string-encoded MOC into an existing Moc but does not
+* normalise.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "moc.h"
+* void astAddMocText_( AstMoc *this, int maxorder,
+* const char *(*source)( void *, size_t *, int * ),
+* void *data, const char *method, int *json )
+
+* Class Membership:
+* Moc method.
+
+* Description:
+* This function identifies the ranges of cells at "maxorder" that are
+* included in the supplied JSON or string-encoded MOC, and appends them
+* to the cell ranges stored in the supplied Moc. It does not normalise
+* the Moc (astMocNorm should be called to do this one all ranges have
+* been added to the Moc).
+
+* Parameters:
+* this
+* Pointer to the Moc to be modified.
+* maxorder
+* The maximum HEALPix order to use. If a negative value is supplied,
+* the maximum order will be determined by searching the supplied MOC.
+* In either case, if a value has already been set for the MaxOrder
+* attribute in the Moc, then the attribute value is used in preference
+* to the value supplied for this parameter. Any HEALPix cells in the
+* supplied MOC that refer to an order greater than "maxorder" are
+* ignored.
+* source
+* A function to call to read in each section of the MOC's string
+* representation. It should have the following synopsis:
+*
+* const char *source( void *data, size_t *nc, int *status )
+*
+* The source function should return a pointer to a string holding the
+* next set of characters within the JSON or string encoded MOC. It
+* should also return the maximum number of characters (nc) to be
+* used from the string. If a null character is found in the string
+* before 'nc' characters have been read, parsing of the string
+* ends at that point. The "data" pointer can be used for any purpose.
+* A NULL pointer should be returned if there are no more characters
+* to return.
+* data
+* A pointer to an arbitrary structure to be passed to the source
+* function. Can be NULL.
+* method
+* Method name to include in error messages.
+* json
+* Pointer to an int in which to return a boolean flag indicating if
+* the supplied string was interpreted using the JSON (non-zero) or
+* string (zero) serialisation.
+
+* Notes:
+* - If no value has yet been set for attribute MaxOrder, then this
+* function will automatically set it to the value supplied for "Maxorder",
+* or to the largest order present in the supplied MOC if "Maxorder"
+* is negative.
+*-
+*/
+
+/* Local Variables: */
+ List orders[ 1 + AST__MXORDHPX ];
+ const char *text;
+ const char *pt;
+ const char *pend;
+ int first;
+ int irange;
+ int isrange;
+ int mxord;
+ int order;
+ int shift;
+ int state;
+ int64_t *pr;
+ int64_t ihigh;
+ int64_t ilow;
+ int64_t ipix;
+ int64_t nadd;
+ int64_t npix0;
+ int64_t npix;
+ int64_t nval;
+ size_t *values;
+ size_t nbyte;
+ size_t nc;
+
+/* Initialise */
+ *json = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Validate */
+ if( maxorder > AST__MXORDHPX ) {
+ astError( AST__INVAR, "%s(%s): Invalid value (%d) supplied for parameter"
+ " 'maxorder' - must be no greater than %d.", status, method,
+ astGetClass( this ), maxorder, AST__MXORDHPX );
+
+/* Otherwise, read in and process the text. */
+ } else {
+
+/* Initialise the maximum order found in the text. */
+ mxord = -1;
+
+/* Initialise the current order and npix value. */
+ order = -1;
+ npix = 0;
+
+/* Indicate we are not in a string-format range. */
+ isrange = 0;
+ npix0 = 0;
+
+/* Indicate we are currently looking for the first non-space character,
+ from which we determine the format of the supplied string. */
+ state = 0;
+
+/* Use the source function to get a pointer to a null-terminated string
+ holding the first characters to read. */
+ text = (*source)( data, &nc, status );
+
+/* Initialise the list of NPIX values at each order. */
+ for( order = 0; order <= AST__MXORDHPX; order++ ) {
+ orders[ order ].nval = 0;
+ orders[ order ].values = NULL;
+ }
+
+/* Loop to parse all available text. This loop populates the above array
+ of "Order" structures, which hold the orders used, and the NPIX values
+ at each order. It also determines the maximum order. But it does not
+ modifiy the supplied Moc structure. */
+ while( text && astOK ) {
+
+/* Process each character in the current text string, Stop when 'nc'
+ characters have been read or a null character is encountered, which ever
+ happens first. */
+ pend = text + nc;
+ pt = text;
+ while( *pt && pt < pend ){
+
+/* If we are currently looking for the first non-space character... */
+ if( state == 0 ) {
+
+/* If the first non-space character is an opening curly brace, assume the
+ text uses JSON-serialisation, and indicate we are now looking for an
+ order value. */
+ if( *pt == '{' ) {
+ *json = 1;
+ state = 1;
+
+/* If the first non-space character is a digit, assume the text uses
+ string-serialisation, and indicate we are now looking for an order
+ value. Backup by one character so that the digit is re-read on the
+ next pass. */
+ } else if( isdigit( *pt ) ) {
+ *json = 0;
+ state = 1;
+ pt--;
+
+/* If the first non-space character is anything else, report an error. */
+ } else if( !isspace( *pt ) ) {
+ astError( AST__INMOC, "%s(%s): Invalid textual MOC supplied: '%.30s...'",
+ status, method, astGetClass( this ), pt );
+ astError( AST__INMOC, "Cannot determine the serialisation from "
+ "the first character.", status );
+ break;
+ }
+
+/* First deal with JSON-serialisation. */
+ } else if( *json ) {
+
+/* If we are looking for the double quote starting an order value, skip
+ spaces until we find a double quote... */
+ if( state == 1 ) {
+ if( *pt == '"' ) {
+ order = 0;
+ state = 2;
+ } else if( !isspace( *pt ) ) {
+ astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
+ status, method, astGetClass( this ), text );
+ astError( AST__INMOC, "Expected double quote at '%.15s'.",
+ status, pt );
+ break;
+ }
+
+/* If we are looking for the digits in an order value, record digits until
+ we find a double quote... */
+ } else if( state == 2 ) {
+ if( isdigit( *pt ) ) {
+ order = ( *pt - '0' ) + 10*order;
+ } else if( *pt == '"' ) {
+ state = 3;
+ } else {
+ astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
+ status, method, astGetClass( this ), text );
+ astError( AST__INMOC, "Expected double quote or digit "
+ "at '%.15s'.", status, pt );
+ break;
+ }
+
+/* If we are looking for the colon following an order value, skip spaces
+ until we find a colon. */
+ } else if( state == 3 ) {
+ if( *pt == ':' ) {
+ state = 4;
+ } else if( !isspace( *pt ) ) {
+ astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
+ status, method, astGetClass( this ), text );
+ astError( AST__INMOC, "Expected colon at '%.15s'.",
+ status, pt );
+ break;
+ }
+
+/* If we are looking for the opening square bracket that marks the start
+ of a list of NPIX values, skip spaces until we find an opening square
+ bracket. */
+ } else if( state == 4 ) {
+ if( *pt == '[' ) {
+ first = 1;
+ state = 5;
+ } else if( !isspace( *pt ) ) {
+ astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
+ status, method, astGetClass( this ), text );
+ astError( AST__INMOC, "Expected opening square bracket "
+ "at '%.15s'.", status, pt );
+ break;
+ }
+
+/* If we are looking for the first digit in an NPIX value, skip spaces until
+ we find a digit. Record the digit. Note an empty list of NPIX values is
+ acceptable so also check for a closing square bacrket so long as we
+ have not yet found any NPIX values in this list. */
+ } else if( state == 5 ) {
+ if( isdigit( *pt ) ){
+ state = 6;
+ npix = *pt - '0';
+
+ } else if( first && *pt == ']' ){
+ if( order > mxord ) mxord = order;
+ state = 8;
+
+ } else if( !isspace( *pt ) ) {
+ astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
+ status, method, astGetClass( this ), text );
+ astError( AST__INMOC, "Expected digit at '%.15s'.",
+ status, pt );
+ break;
+ }
+
+/* If we are looking for the terminator marking the end of an NPIX value,
+ skip digits until we find a space, comma or closing square bracket.
+ Update the NPIX value with any digits. */
+ } else if( state == 6 ) {
+ first = 0;
+
+ if( isdigit( *pt ) ){
+ npix = ( *pt - '0' ) + 10*npix;
+ } else if( isspace( *pt ) ) {
+ state = 7;
+ } else if( *pt == ',' ) {
+ state = 5;
+ } else if( *pt == ']' ) {
+ state = 8;
+ } else {
+ astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
+ status, method, astGetClass( this ), text );
+ astError( AST__INMOC, "Expected digit, comma or "
+ "closing square bracket at '%.15s'.", status, pt );
+ break;
+ }
+
+/* When the NPIX value is complete append it to the current list of NPIX
+ values for the current order. */
+ if( state != 6 ) {
+ nval = orders[ order ].nval;
+ if( nval == 0 && order > mxord ) mxord = order;
+ nbyte = ( nval + 1 )*sizeof( size_t );
+ values = astGrow( orders[ order ].values, 1, nbyte );
+ if( astOK ) {
+ values[ nval ] = npix;
+ orders[ order ].values = values;
+ orders[ order ].nval = nval + 1;
+ }
+ }
+
+/* If we are looking for a comma or closing square bracket marking the
+ end of an NPIX value, skip spaces until we find one. */
+ } else if( state == 7 ) {
+ if( *pt == ',' ){
+ state = 5;
+ } else if( *pt == ']' ) {
+ state = 8;
+ } else if( !isspace( *pt ) ) {
+ astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
+ status, method, astGetClass( this ), text );
+ astError( AST__INMOC, "Expected comma or closing "
+ "square bracket at '%.15s'.", status, pt );
+ break;
+ }
+
+/* If we are looking for a comma or closing curly brace following a list
+ of NPIX values, skip spaces until we find one. */
+ } else if( state == 8 ) {
+ if( *pt == ',' ){
+ state = 1;
+ } else if( *pt == '}' ) {
+ state = 9;
+ } else if( !isspace( *pt ) ) {
+ astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
+ status, method, astGetClass( this ), text );
+ astError( AST__INMOC, "Expected comma or closing "
+ "curly brace at '%.15s'.", status, pt );
+ break;
+ }
+
+/* If we are looking for any non-space characters following the closing
+ curly brace, report an error if any are found. */
+ } else if( state == 9 ) {
+ if( !isspace( *pt ) ) {
+ astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
+ status, method, astGetClass( this ), text );
+ astError( AST__INMOC, "Illegal non-blank characters "
+ "following final closing curly brace at '%.15s'.",
+ status, pt );
+ break;
+ }
+
+/* No other state value should be encountered. */
+ } else {
+ astError( AST__INMOC, "%s(%s): Invalid JSON 'state' value "
+ "(%d) (internal AST programming error).", status,
+ method, astGetClass( this ), state );
+ break;
+ }
+
+/* Now deal with string-serialisation. */
+ } else {
+
+/* If we are looking for the first digit of an order or npix value, skip
+ spaces until we find a digit. We do not know yet whether it is an order
+ or npix value, but we use "npix" to record the value anyway. */
+ if( state == 1 ) {
+ if( isdigit( *pt ) ) {
+ npix = *pt - '0';
+ state = 2;
+ } else if( !isspace( *pt ) ) {
+ astError( AST__INMOC, "%s(%s): Invalid string MOC supplied: '%.30s...'",
+ status, method, astGetClass( this ), text );
+ astError( AST__INMOC, "Expected digit at '%.15s'.",
+ status, pt );
+ break;
+ }
+
+/* If we are looking for the second and subsequent digits in an order or
+ npix value, record digits until we find a space, comma, slash or dash... */
+ } else if( state == 2 ) {
+
+/* digit - update the order or npix value recorded in "npix". */
+ if( isdigit( *pt ) ) {
+ npix = ( *pt - '0' ) + 10*npix;
+
+/* space or comma - the value previously recorded is an npix value,
+ either a single npix value or the end of a range of npix values.
+ Record the npix values and then look for the start of the next
+ numerical value. Report an error if the order has not yet been
+ specified. */
+ } else if( isspace( *pt ) || *pt == ',') {
+ if( order < 0 ) {
+ astError( AST__INMOC, "%s(%s): Invalid string MOC supplied: '%.30s...'",
+ status, method, astGetClass( this ), text );
+ astError( AST__INMOC, "No order value found at start "
+ "of string.", status );
+ break;
+ }
+
+ if( !isrange ) {
+ npix0 = npix;
+ nadd = 1;
+ } else {
+ isrange = 0;
+ nadd = npix - npix0 + 1;
+ }
+
+ nval = orders[ order ].nval;
+ nbyte = ( nval + nadd )*sizeof( size_t );
+ values = astGrow( orders[ order ].values, 1, nbyte );
+ if( astOK ) {
+ for( ; npix0 <= npix; npix0++ ) {
+ values[ nval++ ] = npix0;
+ }
+
+ orders[ order ].values = values;
+ orders[ order ].nval = nval;
+ }
+
+ state = 1;
+
+/* slash - the value previously recorded is an order value. Update the
+ maximum order and then look for the start of the next numerical value. */
+ } else if( *pt == '/' ) {
+ order = npix;
+ if( order > mxord ) mxord = order;
+ state = 1;
+
+/* dash - the value previously recorded is an npix value marking the
+ start of a range. Remember the range start then look for the start
+ of the next numerical value. */
+ } else if( *pt == '-' ) {
+ isrange = 1;
+ npix0 = npix;
+ state = 1;
+
+ } else {
+ astError( AST__INMOC, "%s(%s): Invalid string MOC supplied: '%.30s...'",
+ status, method, astGetClass( this ), text );
+ astError( AST__INMOC, "Illegal character at '%.15s'.",
+ status, pt );
+ break;
+ }
+
+/* No other state value should be encountered. */
+ } else {
+ astError( AST__INMOC, "%s(%s): Invalid string 'state' value "
+ "(%d) (internal AST programming error).", status,
+ method, astGetClass( this ), state );
+ break;
+ }
+ }
+
+/* Advance to look at the next character in the text string. */
+ pt++;
+ }
+
+/* Use the source function to get a pointer to a null-terminated string
+ holding the next set of characters to read. NULL is returned if there
+ are no more characters to read. */
+ text = (*source)( data, &nc, status );
+ }
+
+/* Check JSON mocs are terminated properly. */
+ if( *json ) {
+ if( state != 9 && astOK ) {
+ astError( AST__INMOC, "%s(%s): Invalid JSON MOC supplied: '%.30s...'",
+ status, method, astGetClass( this ), text );
+ astError( AST__INMOC, "No closing curly brace found.", status );
+ }
+
+/* Incorporate any final numerical value in a string moc. The end of
+ string is like a terminator (space or comma) in "state 2" for
+ string mocs above. */
+ } else if( state == 2 ) {
+ if( order < 0 ) {
+ astError( AST__INMOC, "%s(%s): Invalid string MOC supplied: '%.30s...'",
+ status, method, astGetClass( this ), text );
+ astError( AST__INMOC, "No order value found at start of string.",
+ status );
+ }
+
+ if( !isrange ) {
+ npix0 = npix;
+ nadd = 1;
+ } else {
+ isrange = 0;
+ nadd = npix - npix0 + 1;
+ }
+
+ nval = orders[ order ].nval;
+ nbyte = ( nval + nadd )*sizeof( size_t );
+ values = astGrow( orders[ order ].values, 1, nbyte );
+ if( astOK ) {
+ for( ; npix0 <= npix; npix0++ ) {
+ values[ nval++ ] = npix0;
+ }
+
+ orders[ order ].values = values;
+ orders[ order ].nval = nval;
+ }
+ }
+
+/* If the MaxOrder attribute is set in the Moc, use it in preference to
+ the value supplied for parameter "maxorder". */
+ if( astTestMaxOrder( this ) ) {
+ maxorder = astGetMaxOrder( this );
+
+/* Otherwise, we use the supplied "maxorder" value unless "maxorder" was
+ not supplied (i.e. is negative), in which case we use the value
+ determind above from the supplied text. */
+ } else {
+ if( maxorder < 0 ) maxorder = mxord;
+ astSetMaxOrder( this, maxorder );
+ }
+
+/* For each order and NPIX value found during the parsing of the text
+ (except for any that have an order greater than 'maxorder', which are
+ ignored), get the upper and lower bounds of the cells at maxorder
+ contained within this cell, and append this as a new range to the Moc. */
+ for( order = 0; order <= maxorder; order++ ) {
+ nval = orders[ order ].nval;
+ values = orders[ order ].values;
+ shift = 2*( maxorder - order );
+
+ for( ipix = 0; ipix < nval; ipix++,values++ ){
+
+ ilow = ( *values << shift );
+ ihigh = ( (*values + 1 ) << shift ) - 1;
+
+ irange = this->nrange++;
+ this->range = astGrow( this->range, this->nrange, 2*sizeof(*(this->range)) );
+ if( astOK ) {
+ pr = this->range + 2*irange;
+ pr[ 0 ] = ilow;
+ pr[ 1 ] = ihigh;
+ } else {
+ break;
+ }
+ }
+
+/* Free the list of NPIX values at each order. */
+ orders[ order ].values = astFree( orders[ order ].values );
+ }
+ }
+}
+
+/*
+*++
+* Name:
+c astAddPixelMask<X>
+f AST_ADDPIXELMASK<X>
+
+* Purpose:
+* Add a set of pixels to a Moc
+
+* Type:
+* Public function.
+
+* Synopsis:
+c #include "moc.h"
+c void astAddPixelMask<X>( AstMoc *this, int cmode, AstFrameSet *wcs,
+c <Xtype> value, int oper, int flags,
+c <Xtype> badval, const <Xtype> array[],
+c const int dims[2] )
+f CALL AST_ADDPIXELMASK<X>( THIS, CMODE, WCS, VALUE, OPER, FLAGS,
+f BADVAL, ARRAY, DIMS, STATUS )
+
+* Class Membership:
+* Moc method.
+
+* Description:
+* This is a set of functions that modifies a Moc by combining it
+* with a subset of the pixel positions contained within a supplied
+* 2-dimensional array. A FrameSet must be supplied describing the World
+* Coordinate Systems associated with the array. The current Frame of
+* this FrameSet must be a SkyFrame or a CmpFrame containing a SkyFrame.
+*
+* The subset of pixels to be combined with the Moc are selected using
+c the "value" and "oper"
+f the VALUE and OPER
+* parameters. The way in which the existing Moc and the selected pixels
+* are combined together is determined by the
+c "cmode" parameter.
+f CMODE parameter.
+*
+* An adaptive alogorithm is used to find the HEALPix cells that are
+* inside the selected area in the pixel array. An initial grid,
+* corresponding to the HEALPix cells at the order given by the Moc's
+* "MinOrder" attribute, is placed over the pixel array. Each of these
+* cells is tested at 9 positions (corners, edge-centres and cell-centre).
+* If all 9 positions are inside the selected area of pixels, then the
+* whole cell is assumed to be inside. If no positions are inside the
+* selected area, then the whole cell is assumed to be outside. If there
+* is a mix of inside and outside positions, the cell is divided into
+* four sub-cells at HEALPix order "MinOrder+1", and the same test is
+* applied to each sub-cell in turn. When the HEALPix order reaches the
+* value of the Moc's "MaxOrder" attribute, each cell is tested only at
+* the cell centre, and is assumed to be inside the selected area if the
+* cell centre is inside the selected area.
+*
+* This process means that contiguous "islands" or "holes" in the
+* supplied pixel mask may be missed if they are smaller than the cell
+* size associated with HEALPix order "MinOrder".
+*
+* If no value has yet been set for the MaxOrder attribute, then this
+* function will automatically set it to the smallest value that results
+* in the cells in the Moc being no larger than half the size of the pixels
+* in the centre of the array. Note, if the value set for attribute
+* MinOrder is greater than or equal to MaxOrder, a value of
+* (MaxOrder-1) will be used in place of MinOrder.
+*
+* You should use a function which matches the numerical type of the
+* data you are processing by replacing <X> in the generic function
+* name
+c astAddPixelMask<X>
+f AST_ADDPIXELMASK<X>
+c by an appropriate 1- or 2-character type code. For example, if you
+* are procesing data with type
+c "float", you should use the function astAddPixelMaskF
+f REAL, you should use the function AST_ADDPIXELMASKR
+* (see the "Data Type Codes" section below for the codes appropriate to
+* other numerical types).
+
+* Parameters:
+c this
+f THIS = INTEGER (Given)
+* Pointer to the Moc to be modified.
+c cmode
+f CMODE = INTEGER (Given)
+* Indicates how the Moc and select pixels are to be combined. Any of the
+* following values may be supplied:
+* - AST__AND: The modified Moc is the intersection of the original
+* Moc and the selected pixels.
+* - AST__OR: The modified Moc is the union of the original Moc and
+* the selected pixels.
+* - AST__XOR: The modified Moc is the exclusive disjunction of the
+* original Moc and the selected pixels.
+c wcs
+f WCS = AstFrameSet * (Given)
+* Pointer to a FrameSet defining the World Coordinate Systems
+* associated with the image. The current Frame should be a SkyFrame
+* or a CmpFrame containing a SkyFrame. The base Frame should have
+* the same number of axes as the current Frame and should represent
+* "grid" coordinates within a pixel array (i.e. the first pixel is
+* centred at (1.0,1.0,...) and the distance between pixel centres
+* is 1.0 on both axes). The array supplied for parameter
+c "array"
+f ARRAY
+* is assumed to be a 2-dimensional slice from this array, spanned
+* by the grid axes corresponding to the SkyFrame axes.
+c value
+f VALUE = <Xtype> (Given)
+* A data value that specifies the selected pixels. See parameter
+c "oper".
+f OPER.
+c oper
+f OPER = INTEGER (Given)
+* Indicates how the
+c "value"
+f VALUE
+* parameter is used to select the required pixels. It can
+* have any of the following values:
+c - AST__LT: select pixels with value less than "value".
+c - AST__LE: select pixels with value less than or equal to "value".
+c - AST__EQ: select pixels with value equal to "value".
+c - AST__NE: select pixels with value not equal to "value".
+c - AST__GE: select pixels with value greater than or equal to "value".
+c - AST__GT: select pixels with value greater than "value".
+f - AST__LT: select pixels with value less than VALUE.
+f - AST__LE: select pixels with value less than or equal to VALUE.
+f - AST__EQ: select pixels with value equal to VALUE.
+f - AST__NE: select pixels with value not equal to VALUE.
+f - AST__GE: select pixels with value greater than or equal to VALUE.
+f - AST__GT: select pixels with value greater than VALUE.
+c flags
+f FLAGS = INTEGER (Given)
+c The bitwise OR of a set of flag values which may be used to
+f The sum of a set of flag values which may be used to
+* provide additional control over the operation. See
+* the "Control Flags" section below for a description of the
+* options available. If no flag values are to be set, a value
+* of zero should be given.
+c badval
+f BADVAL = <Xtype> (Given)
+* This parameter should have the same type as the elements of
+* the data array. It specifies the value used to flag missing
+* data (bad pixels). Such pixels are never included in the Moc.
+*
+c If the AST__USEBAD flag is set via the "flags" parameter,
+f If the AST__USEBAD flag is set via the FLAGS parameter,
+* then this value is used to test for bad pixels in the
+* supplied data array.
+c array
+f ARRAY( * ) = <Xtype> (Given)
+c Pointer to the
+f The
+* 2-dimensional data array. The numerical type of this array should
+* match the 1- or 2-character type code appended to the function name
+* (e.g. if you are using
+c astAddPixelMaskF, the type of each array element should be "float").
+f AST_ADDPIXELMASKR, the type of each array element should be REAL).
+*
+* The storage order of data within this array should be such that the
+* index of the first grid dimension varies most rapidly (i.e.
+c Fortran array indexing is used).
+f normal Fortran array storage order).
+c dims
+f DIMS( 2 ) = INTEGER (Given)
+c Pointer to an array
+f An array
+* containing the length of each pixel axis, in pixels.
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Control Flags:
+c The following flags are defined in the "ast.h" header file and
+f The following flags are defined in the AST_PAR include file and
+* may be used to provide additional control over the process.
+* Having selected a set of flags, you should supply the
+c bitwise OR of their values via the "flags" parameter:
+f sum of their values via the FLAGS parameter:
+*
+* - AST__USEBAD: Indicates that there may be bad pixels in the
+* input array which must be recognised by comparing with the
+c value given for "badval".
+f value given for BADVAL.
+* If this flag is not set, all input values are treated literally.
+
+* Data Type Codes:
+* To select the appropriate masking function, you should
+c replace <X> in the generic function name astAddPixelMask<X> with a
+f replace <X> in the generic function name AST_ADDPIXELMASK<X> with a
+* 1- or 2-character data type code, so as to match the numerical
+* type <Xtype> of the data you are processing, as follows:
+c - D: double
+c - F: float
+c - L: long int
+c - UL: unsigned long int
+c - I: int
+c - UI: unsigned int
+c - S: short int
+c - US: unsigned short int
+c - B: byte (signed char)
+c - UB: unsigned byte (unsigned char)
+f - D: DOUBLE PRECISION
+f - R: REAL
+f - I: INTEGER
+f - UI: INTEGER (treated as unsigned)
+f - S: INTEGER*2 (short integer)
+f - US: INTEGER*2 (short integer, treated as unsigned)
+f - B: BYTE (treated as signed)
+f - UB: BYTE (treated as unsigned)
+*
+c For example, astAddPixelMaskD would be used to process "double"
+c data, while astAddPixelMaskS would be used to process "short int"
+c data, etc.
+f For example, AST_ADDPIXELMASKD would be used to process DOUBLE
+f PRECISION data, while AST_ADDPIXELMASKS would be used to process
+f short integer data (stored in an INTEGER*2 array), etc.
+f
+f For compatibility with other Starlink facilities, the codes W
+f and UW are provided as synonyms for S and US respectively (but
+f only in the Fortran interface to AST).
+
+*--
+*/
+
+/* Define a macro to implement the function for a specific data type. */
+#define MAKE_ADDPIXELMASK(X,Xtype,XtypeId) \
+static void AddPixelMask##X( AstMoc *this, int cmode, AstFrameSet *wcs, \
+ Xtype value, int oper, int flags, \
+ Xtype badval, const Xtype array[], \
+ const int dims[2], int *status ) { \
+\
+/* Local Variables: */ \
+ AstCmpMap *array2proj; /* Mapping for HEALPix grid coordinates */ \
+ AstCmpMap *array2proj_min;/* Mapping for best HEALPix grid coordinates */ \
+ AstCmpMap *tempmap; /* Temporary Mapping pointer */ \
+ AstFrameSet *array2hpx12;/* Array grid coords -> HPX12 grid coords */ \
+ AstFrameSet *fs; /* Connects grid and sky using HPX projection */ \
+ AstFrameSet *fsconv; /* Conversion FrameSet */ \
+ AstFrameSet *picked; /* WCS FrameSet with 2-dimensional current Frame */ \
+ AstMapping *maps[ AST__MXORDHPX + 1 ]; /* hpx->grid mappings for all orders */ \
+ AstMapping *tempmap2; /* Temporary Mapping */ \
+ AstWinMap *incmap; /* Mapping from grid coords at order N+1 to order N */ \
+ AstXphMap *xphmap; /* "iproj" grid coords -> HPX12 grid coords */ \
+ CellList clist; /* Staging area for list of cells in the Moc */ \
+ PixelMask pixelmask; /* Strucure defining the pixel mask */ \
+ double delta; /* Width of safety margin */ \
+ double glbnd[2]; /* Lower bounds of region in grid coords */ \
+ double gubnd[2]; /* Upper bounds of region in grid coords */ \
+ double ina[2]; /* Bottom left corner of input box */ \
+ double inb[2]; /* Top right corner of input box */ \
+ double lbnd_in[ 2 ]; /* Grid coord bounds of selected array pixels */ \
+ double outa[2]; /* Bottom left corner of output box */ \
+ double outb[2]; /* Top right corner of output box */ \
+ double pixarea; /* Area of array pixel in square arc-sec */ \
+ double res; /* Mean pixel resolution in arc-sec */ \
+ double ubnd_in[ 2 ]; /* Grid coord bounds of selected array pixels */ \
+ int glbnd_min[2]; /* Best lower bounds of region in grid coords */ \
+ int gubnd_min[2]; /* Best upper bounds of region in grid coords */ \
+ int hx; /* Upper bound on 1st grid axis of selection box */ \
+ int hy; /* Upper bound on 2ns grid axis of selection box */ \
+ int i; /* Loop count */ \
+ int iax; /* Axis index */ \
+ int iproj; /* Identifier for type of HEALPix projection */ \
+ int iproj_min; /* Identifier for best type of HEALPix projection */ \
+ int j; /* Loop count */ \
+ int lx; /* Lower bound on 1st grid axis of selection box */ \
+ int ly; /* Lower bound on 2nd grid axis of selection box */ \
+ int maxorder; /* MOC order for final grid */ \
+ int minorder; /* MOC order for initial grid */ \
+ int ok; /* At least one selected pixel? */ \
+ size_t npix; /* Number of pixels in bounding box */ \
+ size_t npix_min; /* Number of pixels in smallest bounding box */ \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return; \
+\
+/* Validate. */ \
+ if( !astIsAFrameSet( wcs ) ) { \
+ astError( AST__BDCLS, "astAddPixelMask"#X"(%s): A %s was supplied for " \
+ "parameter 'wcs' but a FrameSet is required.", status, \
+ astGetClass(this), astGetClass(wcs) ); \
+ } \
+\
+ if( dims[ 0 ] < 1 || dims[ 1 ] < 1 ) { \
+ astError( AST__INVAR, "astAddPixelMask"#X"(%s): Invalid values " \
+ "(%d,%d) supplied for parameter 'dims'.", status, \
+ astGetClass(this), dims[ 0 ], dims[ 1 ] ); \
+ } \
+\
+/* The current Frame of the supplied FrameSet must contain a SkyFrame \
+ (possibly plus other Frames). Identify the sky axes in the FrameSet, \
+ and create a FrameSet by picking the associated pixel axes from the \
+ base Frame axes. */ \
+ picked = (AstFrameSet *) FindSkyAxes( (AstFrame *) wcs, "astAddPixelMask"#X, \
+ status ); \
+\
+/* We proceed only if a SkyFrame was found. */ \
+ if( picked ) { \
+\
+/* If the MaxOrder attribute has not been set, set it now to the smallest \
+ value that gives cells that are no larger than half the pixel size at \
+ centre of the array (i.e 0.25 of the pixel area). */ \
+ if( !astTestMaxOrder( this ) ) { \
+ pixarea = GetPixelArea( picked, dims, status ); \
+ res = sqrt( 0.25*pixarea ); \
+ maxorder = ResToOrder( res ); \
+ res = OrderToRes( maxorder ); \
+ if( res*res > 0.25*pixarea ) maxorder++; \
+ astSetMaxOrder( this, maxorder ); \
+ } else { \
+ maxorder = astGetMaxOrder( this ); \
+ } \
+\
+/* Get the minimum order that defines the spacing of the initial grid. \
+ Bounded holes within selected areas that are smaller than a cell of \
+ this grid may be missed (i.e. "filled in"). */ \
+ minorder = astGetMinOrder( this ); \
+\
+/* Ensure we do not start at a higher order than we can handle. */ \
+ if( minorder >= maxorder ) minorder = maxorder - 1; \
+\
+/* Invert the WCS FrameSet for the picked (i.e. sky) axes, so that the \
+ current Frame represents grid coords in the array and the base Frame \
+ represents sky coords. */ \
+ astInvert( picked ); \
+\
+/* Get the integer grid coordinate bounds of the region within the array \
+ that contains the selected pixels. */ \
+ if( oper == AST__LT ) { \
+ ok = GetSelectionBoundsLT##X( value, array, dims, &lx, &hx, \
+ &ly, &hy, status ); \
+ } else if( oper == AST__LE ) { \
+ ok = GetSelectionBoundsLE##X( value, array, dims, &lx, &hx, \
+ &ly, &hy, status ); \
+ } else if( oper == AST__EQ ) { \
+ ok = GetSelectionBoundsEQ##X( value, array, dims, &lx, &hx, \
+ &ly, &hy, status ); \
+ } else if( oper == AST__NE ) { \
+ ok = GetSelectionBoundsNE##X( value, array, dims, &lx, &hx, \
+ &ly, &hy, status ); \
+ } else if( oper == AST__GE ) { \
+ ok = GetSelectionBoundsGE##X( value, array, dims, &lx, &hx, \
+ &ly, &hy, status ); \
+ } else if( oper == AST__GT ) { \
+ ok = GetSelectionBoundsGT##X( value, array, dims, &lx, &hx, \
+ &ly, &hy, status ); \
+ } else if( astOK ){ \
+ astError( AST__OPRIN, "astAddPixelMask"#X"(%s): Invalid operation " \
+ "code (%d) supplied (programming error).", status, \
+ astGetClass( this ), oper ); \
+ } \
+\
+/* If no pixels are selected, the resulting Moc will be unchanged unless \
+ "cmode" is AST__AND, in which case the resulting Moc will be empty. */ \
+ if( !ok ) { \
+ if( cmode == AST__AND ) { \
+ this->nrange = 0; \
+ this->range = astFree( this->range ); \
+ ClearCache( this, status ); \
+ } \
+\
+/* Otherwise, get a FrameSet describing a map of the whole sky using an \
+ HPX12 projection, with the maximum MOC order. Invert it so that the base \
+ frame represents ICRS and the current Frame represents HEALPix grid \
+ coordinates. Each pixel in the grid corresponds to a HEALPix cell on \
+ the sky. */ \
+ } else { \
+ fs = GetHPX12FrameSet( this, maxorder, status ); \
+ astInvert( fs ); \
+\
+/* Get the Mapping from the array's grid coords to HPX12 grid coords at \
+ maxorder. */ \
+ fsconv = astConvert( picked, fs, "" ); \
+ array2hpx12 = astGetMapping( fsconv, AST__BASE, AST__CURRENT ); \
+ fsconv = astAnnul( fsconv ); \
+ fs = astAnnul( fs ); \
+\
+/* We need to map the array into a HEALPix projection, of which there \
+ are four: HPX centred on RA=0 (HPX0), HPX centred on RA=12h (HPX12), \
+ XPH centred on the north pole (XPHN), XPH centred on the south pole \
+ (XPHS). Problems arise if the array spans any of discontinuities in \
+ the projection, so loop round all four projections and find the one \
+ that maps the array onto the smallest box in HEALPix grid coords. */ \
+ npix_min = 0; \
+ iproj_min = -1; \
+ array2proj_min = NULL; \
+ for( iproj = 0; iproj <= AST__MXPRJHPX; iproj++ ) { \
+\
+/* Extend the array2hpx12 Mapping to get a Mapping from the array's grid \
+ coords to grid coords in the current variant HPX projection. The \
+ "xphmap" Mapping goes from grid coords in the projection given by \
+ "iproj" into grid coords within an HPX12 projection. */ \
+ xphmap = astXphMap( maxorder, iproj, " ", status ); \
+ astInvert( xphmap ); \
+ array2proj = astCmpMap( array2hpx12, xphmap, 1, "", status ); \
+ xphmap = astAnnul( xphmap ); \
+\
+/* Use this Mapping to determine the bounding box of the selected pixels \
+ within HEALPix grid coords in the projection given by "iproj" at \
+ "maxorder. */ \
+ lbnd_in[ 0 ] = lx - HALF; \
+ ubnd_in[ 0 ] = hx + HALF; \
+ lbnd_in[ 1 ] = ly - HALF; \
+ ubnd_in[ 1 ] = hy + HALF; \
+\
+ for( iax = 0; iax < 2; iax++ ){ \
+ astMapBox( array2proj, lbnd_in, ubnd_in, 1, iax, glbnd + iax, \
+ gubnd + iax, NULL, NULL ); \
+ } \
+\
+/* Get the number of HEALPix cells in the bounding box. */ \
+ npix = (int)( ( gubnd[ 0 ] - glbnd[ 0 ] + 1 )* \
+ ( gubnd[ 1 ] - glbnd[ 1 ] + 1 ) ); \
+\
+/* If this is the smallest bounding box found so far, record its details, \
+ increasing its size by 5% at each edge for safety. */ \
+ if( iproj == 0 || npix < npix_min ) { \
+ delta = 0.05*( gubnd[0] - glbnd[0] + 1 ); \
+ glbnd_min[ 0 ] = (int) ( glbnd[ 0 ] - delta ); \
+ gubnd_min[ 0 ] = (int) ( gubnd[ 0 ] + delta ); \
+ delta = 0.05*( gubnd[1] - glbnd[1] + 1 ); \
+ glbnd_min[ 1 ] = (int) ( glbnd[ 1 ] - delta ); \
+ gubnd_min[ 1 ] = (int) ( gubnd[ 1 ] + delta ); \
+ npix_min = npix; \
+ iproj_min = iproj; \
+ if( array2proj_min ) array2proj_min = astAnnul( array2proj_min ); \
+ array2proj_min = astClone( array2proj ); \
+ } \
+\
+/* Free resources. */ \
+ array2proj = astAnnul( array2proj ); \
+ } \
+\
+/* Report an error if the pixel mask could not be mapped into a \
+ defined region using any of the available HPX-like projections. */ \
+ if( !array2proj_min && astOK ) { \
+ astError( AST__NODEF, "astAddPixelMask"#X"(%s): The supplied " \
+ "pixel array cannot be mapped to HEALPix coordinates.", \
+ status, astGetClass( this ) ); \
+ } \
+\
+/* Store information defining the pixel mask in a structure so that it \
+ can be passed easily to other functions. */ \
+ pixelmask.data = array; \
+ pixelmask.value = astStore( NULL, &value, sizeof(value) ); \
+ if( flags & AST__USEBAD ) { \
+ pixelmask.bad = astStore( NULL, &badval, sizeof(badval) ); \
+ } else { \
+ pixelmask.bad = NULL; \
+ } \
+ pixelmask.type = XtypeId; \
+ pixelmask.nx = dims[ 0 ]; \
+ pixelmask.ny = dims[ 1 ]; \
+ pixelmask.oper = oper; \
+\
+ if( astOK ) { \
+\
+/* Generate Mappings for all required orders. Each of these Mappings goes \
+ from grid coords in the HPX like projection at order N to grid coords \
+ in the pixel array. The Mapping for order (N-1) is derived from the \
+ Mapping for order N by dividing each grid cell at order (N-1) into four. \
+ This is done by prepending a suitable WinMap to the start of the Mapping \
+ for order N. The winmap maps the bottom left 2x2 box of pixels at order \
+ N (0.5:2.5,0.5:2.5) onto the bottom left pixel at order (N-1) \
+ (0.5:1.5,0.5:1.5). */ \
+ ina[ 0 ] = 0.5; \
+ ina[ 1 ] = 0.5; \
+ inb[ 0 ] = 1.5; \
+ inb[ 1 ] = 1.5; \
+ outa[ 0 ] = 0.5; \
+ outa[ 1 ] = 0.5; \
+ outb[ 0 ] = 2.5; \
+ outb[ 1 ] = 2.5; \
+ incmap = astWinMap( 2, ina, inb, outa, outb, "", status ); \
+ astInvert( array2proj_min ); \
+ for( i = maxorder; i >= 0; i-- ) { \
+ if( i < minorder ) { \
+ maps[ i ] = NULL; \
+ } else { \
+ maps[ i ] = astSimplify( array2proj_min ); \
+ tempmap = astCmpMap( incmap, array2proj_min, \
+ 1, "", status ); \
+ (void) astAnnul( array2proj_min ); \
+ array2proj_min = tempmap; \
+ } \
+ } \
+ incmap = astAnnul( incmap ); \
+\
+/* Get the Mapping from hpx-like grid coords at order maxorder to \
+ hpx-like grid coords at order minorder. */ \
+ astInvert( maps[minorder] ); \
+ tempmap = astCmpMap( maps[maxorder], maps[minorder], 1, \
+ " ", status ); \
+ astInvert( maps[minorder] ); \
+ tempmap2 = astSimplify( tempmap ); \
+ tempmap = astAnnul( tempmap ); \
+\
+/* Use this Mapping to transform the bounding box from maxorder to \
+ minorder. Add a one pixel safety margin. */ \
+ ina[ 0 ] = glbnd_min[ 0 ]; \
+ inb[ 0 ] = glbnd_min[ 1 ]; \
+ ina[ 1 ] = gubnd_min[ 0 ]; \
+ inb[ 1 ] = gubnd_min[ 1 ]; \
+ astTran2( tempmap2, 2, ina, inb, 1, outa, outb ); \
+ glbnd_min[ 0 ] = (int)( outa[ 0 ] + 0.5 ) - 1; \
+ glbnd_min[ 1 ] = (int)( outb[ 0 ] + 0.5 ) - 1; \
+ gubnd_min[ 0 ] = (int)( outa[ 1 ] + 0.5 ) + 1; \
+ gubnd_min[ 1 ] = (int)( outb[ 1 ] + 0.5 ) + 1; \
+ tempmap2 = astAnnul( tempmap2 ); \
+\
+/* Initialise a CellList structure holding the grid coords of the cells \
+ to add into the Moc at each order. This also holds Mappings from \
+ projection "iproj_min" to HPX12 at each order. */ \
+ memset( &clist, 0, sizeof(clist) ); \
+ clist.maxorder = maxorder; \
+ for( i = minorder; i <= maxorder; i++ ) { \
+ clist.maps[ i ] = astXphMap( i, iproj_min, " ", status ); \
+ } \
+\
+/* Loop over all cells in the best bounding box. */ \
+ for( j = glbnd_min[ 1 ]; j <= gubnd_min[ 1 ]; j++ ) { \
+ for( i = glbnd_min[ 0 ]; i <= gubnd_min[ 0 ]; i++ ) { \
+\
+/* Add to the Moc any parts of the current cell that are within the \
+ selected areas. */ \
+ PutCell( this, maps, i, j, minorder, &clist, 1, \
+ &pixelmask, cmode, "astAddPixelMask"#X, status ); \
+ } \
+ } \
+ } \
+\
+/* Convert all the grid coords stored in "clist" into nested indices at \
+ order "maxorder" and incorporate them into the current contents of the \
+ Moc. */ \
+ IncorporateCells( this, &clist, 0, cmode, "astAddPixelMask"#X, \
+ status ); \
+\
+/* Free resources. */ \
+ for( i = minorder; i <= maxorder; i++ ) { \
+ maps[ i ] = astAnnul( maps[ i ] ) ; \
+ clist.ix[ i ] = astFree( clist.ix[ i ] ); \
+ clist.iy[ i ] = astFree( clist.iy[ i ] ); \
+ clist.maps[ i ] = astAnnul( clist.maps[ i ] ); \
+ } \
+ for( i = 0; i <= maxorder; i++ ) { \
+ if( maps[ i ] ) maps[ i ] = astAnnul( maps[ i ] ) ; \
+ } \
+ pixelmask.value = astFree( pixelmask.value ); \
+ pixelmask.bad = astFree( pixelmask.bad ); \
+ array2hpx12 = astAnnul( array2hpx12 ); \
+ array2proj_min = astAnnul( array2proj_min ); \
+ } \
+\
+ picked = astAnnul( picked ); \
+ } \
+}
+
+/* Expand the above macro to generate a function for each required
+ data type. */
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+MAKE_ADDPIXELMASK(LD,long double,LONG_DOUBLE)
+#endif
+MAKE_ADDPIXELMASK(D,double,DOUBLE)
+MAKE_ADDPIXELMASK(L,long int,LONG_INT)
+MAKE_ADDPIXELMASK(UL,unsigned long int,UNSIGNED_LONG_INT)
+MAKE_ADDPIXELMASK(I,int,INT)
+MAKE_ADDPIXELMASK(UI,unsigned int,UNSIGNED_INT)
+MAKE_ADDPIXELMASK(S,short int,SHORT_INT)
+MAKE_ADDPIXELMASK(US,unsigned short int,UNSIGNED_SHORT_INT)
+MAKE_ADDPIXELMASK(B,signed char,SIGNED_CHAR)
+MAKE_ADDPIXELMASK(UB,unsigned char,UNSIGNED_CHAR)
+MAKE_ADDPIXELMASK(F,float,FLOAT)
+
+/* Undefine the macro. */
+#undef MAKE_ADDPIXELMASK
+
+
+
+static void AddRegion( AstMoc *this, int cmode, AstRegion *region, int *status ){
+/*
+*++
+* Name:
+c astAddRegion
+f AST_ADDREGION
+
+* Purpose:
+* Add a Region into a Moc.
+
+* Type:
+* Public virtual function.
+
+
+* Synopsis:
+c #include "moc.h"
+c void astAddRegion( AstMoc *this, int cmode, AstRegion *region )
+f CALL AST_ADDREGION( THIS, CMODE, REGION, STATUS )
+
+* Class Membership:
+* Moc method.
+
+* Description:
+* This function modifies a Moc by combining it with a supplied Region.
+* The Region must be defined within a SkyFrame, or within a CmpFrame that
+* contains a SkyFrame. The Region will be converted to ICRS before being
+* combined with the Moc. The way in which they are combined is determined
+* by the
+c "cmode" parameter.
+f CMODE parameter.
+*
+* Note, since Moc is a subclass of Region this method can be used to add
+* a Moc into another Moc. In such cases, the data is transferred from
+* one Moc to another directly. For other classes of Region an adaptive
+* algorithm is used to find the HEALPix cells that are inside the Region.
+* An initial grid, corresponding to the HEALPix cells at the order given
+* by the Moc's "MinOrder" attribute, is placed over the bounding box of
+* the supplied Region. Each of these cells is tested at 9 positions
+* (corners, edge-centres and cell-centre). If all 9 positions are inside
+* the supplied Region, then the whole cell is assumed to be inside the
+* Region. If no positions are inside the supplied Region, then the whole
+* cell is assumed to be outside the Region. If there is a mix of inside
+* and outside positions, the cell is divided into four sub-cells at
+* HEALPix order "MinOrder+1", and the same test is applied to each
+* sub-cell in turn. When the HEALPix order reaches the value of the
+* Moc's "MaxOrder" attribute, each cell is tested only at the cell
+* centre, and is assumed to be inside the Region if the cell centre is
+* in the Region.
+*
+* This process means that contiguous "islands" or "holes" in the
+* supplied region may be missed if they are smaller than the cell size
+* associated with HEALPix order "MinOrder".
+
+* Parameters:
+c this
+f THIS = INTEGER (Given)
+* Pointer to the Moc to be modified.
+c cmode
+f CMODE = INTEGER (Given)
+* Indicates how the Moc and Region are to be combined. Any of the
+* following values may be supplied:
+* - AST__AND: The modified Moc is the intersection of the original
+* Moc and the Region.
+* - AST__OR: The modified Moc is the union of the original Moc and
+* the Region.
+* - AST__XOR: The modified Moc is the exclusive disjunction of the
+* original Moc and the Region.
+c region
+f REGION = INTEGER (Given)
+* Pointer to the Region to be combined with the Moc.
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Notes:
+* - When combining the Region with the Moc, it is assumed that the Moc
+* has not been inverted (i.e. the current value of the Moc's 'Negated'
+* attribute is ignored).
+* - If no value has yet been set for attribute MaxOrder, then this
+* function will automatically set it to a value that depends on the
+* class of Region being added. If the Region being added is another
+* Moc, the MaxOrder attribute of the Moc is used. For other classes
+* of Region, the value used corresponds to the resolution closest to
+* 0.1% of the linear size of the Region being added (determined using
+* method astGetRegionDisc).
+*--
+*/
+
+/* Local Variables: */
+ AstCmpMap *reg2proj; /* Region coords -> "iproj" grid coords */
+ AstCmpMap *tempmap; /* Temporary Mapping pointer */
+ AstFrame *gridframe; /* Pointer to a Frame describing grid coords */
+ AstFrame *picked_frame; /* Frame in which picked Region is defined */
+ AstFrameSet *fs; /* Connects grid and sky using HPX projection */
+ AstFrameSet *fsconv; /* Conversion FrameSet */
+ AstFrameSet *reg2hpx12; /* Connects Region coords and HPX12 grid coords */
+ AstMapping *hpx2region; /* HEALPix grid coordinates -> mapped region */
+ AstMapping *maps[ AST__MXORDHPX + 1 ]; /* hpx->grid mappings for all orders */
+ AstMapping *tempmap2; /* Temporary Mapping pointer */
+ AstMoc *that; /* The Moc being added to "this" */
+ AstRegion *mapped; /* Region Mapped into HEALPix grid coordinates */
+ AstRegion *mapped_min; /* Region Mapped into best HEALPix grid coordinates */
+ AstRegion *picked; /* Region spanning just sky axes */
+ AstWinMap *incmap; /* Mapping from grid coords at order N+1 to order N */
+ AstXphMap *xphmap; /* "iproj" grid coords -> HPX12 grid coords */
+ CellList clist; /* Staging area for list of cells in the Moc */
+ double centre[2]; /* Centre of bounding disc */
+ double delta; /* Width of safety margin */
+ double glbnd[2]; /* Lower bounds of region in grid coords */
+ double gubnd[2]; /* Upper bounds of region in grid coords */
+ double ina[2]; /* Bottom left corner of input box */
+ double inb[2]; /* Top right corner of input box */
+ double outa[2]; /* Bottom left corner of output box */
+ double outb[2]; /* Top right corner of output box */
+ double radius; /* Radius of bounding disc */
+ int glbnd_min[2]; /* Best lower bounds of region in grid coords */
+ int gubnd_min[2]; /* Best upper bounds of region in grid coords */
+ int i; /* Loop count */
+ int iproj; /* Identifier for type of HEALPix projection */
+ int iproj_min; /* Identifier for best type of HEALPix projection */
+ int irange; /* Index of range in "that" */
+ int ix; /* X grid index */
+ int iy; /* Y grid index */
+ int maxorder; /* Maximum HEALPix order */
+ int minorder; /* Minimum HEALPix order */
+ int negated; /* Is the Region negated? */
+ int nold; /* Number of ranges originally in "this" */
+ int shift; /* No. of bits to shift from that_order to maxorder */
+ int that_order; /* Order of Moc being added to "this" */
+ int64_t *pr2; /* Point to next range in "this" */
+ int64_t *pr; /* Point to next range in "that" */
+ int64_t ihigh; /* High bound of range at maxorder */
+ int64_t ilow; /* Low bound of range at maxorder */
+ size_t npix; /* Number of pixels in bounding box */
+ size_t npix_min; /* Number of pixels in smallest bounding box */
+
+/* Check inherited status */
+ if( !astOK ) return;
+
+/* First handle cases where a Moc is being added. */
+ if( astIsAMoc( region ) ) {
+
+/* Get a pointer to the Moc being added. */
+ that = (AstMoc *) region;
+
+/* If the Moc being added ("that") is empty then the resulting Moc will be
+ unchanged unless "cmode" is AST__AND, in which case the resulting Moc
+ will be empty. */
+ if( that->nrange == 0 ) {
+ if( cmode == AST__AND ) {
+ this->nrange = 0;
+ this->range = astFree( this->range );
+ ClearCache( this, status );
+ }
+
+/* If "that" is not empty, append its cell ranges to "this". */
+ } else {
+
+/* Get the order of the Moc being added. */
+ that_order = astGetMaxOrder( that );
+
+/* Get the HEALPix order of the Moc being modified. Set it to the MaxOrder
+ value of the Moc being added if it has not already been set. */
+ if( astTestMaxOrder( this ) ){
+ maxorder = astGetMaxOrder( this );
+ } else {
+ maxorder = that_order;
+ astSetMaxOrder( this, that_order );
+ }
+
+/* Record the original number of ranges in "this". */
+ nold = this->nrange;
+
+/* Append each cell range in "that" to the end of the array of cell
+ ranges in "this", converting them to MaxOrder first. */
+ shift = 2*( maxorder - that_order );
+ pr = that->range;
+ for( irange = 0; irange < that->nrange; irange++, pr += 2 ) {
+
+/* Convert the bounds of the curent range from "that_order" to
+ "maxorder". */
+ if( shift > 0 ) {
+ ilow = ( pr[ 0 ] << shift );
+ ihigh = ( ( pr[ 1 ] + 1 ) << shift ) - 1;
+ } else {
+ ilow = ( pr[ 0 ] >> -shift );
+ ihigh = ( pr[ 1 ] >> -shift );
+ }
+
+/* Append it to the end of the array of ranges in "this". */
+ this->range = astGrow( this->range, this->nrange + 1, 2*sizeof(*(this->range)) );
+ if( astOK ) {
+ pr2 = this->range +2*(this->nrange++);
+ pr2[ 0 ] = ilow;
+ pr2[ 1 ] = ihigh;
+ } else {
+ break;
+ }
+ }
+
+/* Normalise the Moc. */
+ astMocNorm( this, astGetNegated( that ), cmode, nold, maxorder,
+ "astAddRegion" );
+ }
+
+/* Mow handle cases where the Region being added is not a Moc. */
+ } else {
+
+/* The supplied Region must contain a SkyFrame (possibly plus other
+ Frames). Identify the sky axes in the Region and attempt to create
+ a new Region containing just the sky axes. */
+ picked = (AstRegion *) FindSkyAxes( (AstFrame *) region, "astAddRegion",
+ status );
+
+/* We proceed only if a SkyFrame was found. */
+ if( picked ) {
+
+/* Record the Nagated flag for the Region then set it false. */
+ negated = astGetNegated( picked );
+ astClearNegated( picked );
+
+/* Get the maximum HEALPix order. This defines the finest resolution of
+ the resulting MOC. Set it to 0.1 % of the diameter of the Region if it
+ has not already been set. */
+ if( astTestMaxOrder( this ) ){
+ maxorder = astGetMaxOrder( this );
+ } else {
+ astGetRegionDisc( picked, centre, &radius );
+ maxorder = ResToOrder( 0.001*radius*AST__DR2D*3600.0 );
+ astSetMaxOrder( this, maxorder );
+ }
+
+/* Get the minimum order that defines the spacing of the initial grid.
+ Bounded holes within selected areas that are smaller than a cell of
+ this grid may be missed (i.e. "filled in"). */
+ minorder = astGetMinOrder( this );
+
+/* Ensure we do not start at a higher order than we can handle. */
+ if( minorder >= maxorder ) minorder = maxorder - 1;
+
+/* Get a pointer to the Frame in which the Region is defined. We use this
+ with astConvert below, rather than the original Region, so that the
+ FrameSet returned by astConvert will not include the masking effects of
+ the Region. */
+ picked_frame = astRegFrame( picked );
+
+/* Get a FrameSet describing a map of the whole sky in ICRS using an
+ HPX12 projection, with the maximum MOC order. Invert it so that the
+ base frame represents ICRS and the current Frame represents HPX12 grid
+ coordinates. Each pixel in the grid corresponds to a HEALPix cell on
+ the sky (order "maxorder"). We use an HPX12 projection rather than
+ HPX0 because the grid indices in an HPX12 projection are more easily
+ converted to a HEALPix nested index (one less shift on each axis). */
+ fs = GetHPX12FrameSet( this, maxorder, status );
+ gridframe = astGetFrame( fs, AST__BASE );
+ astInvert( fs );
+
+/* Get the Mapping from the Region's coords to HPX12 grid coords at
+ "maxorder". */
+ fsconv = astConvert( picked_frame, fs, "" );
+ reg2hpx12 = astGetMapping( fsconv, AST__BASE, AST__CURRENT );
+ fsconv = astAnnul( fsconv );
+ fs = astAnnul( fs );
+
+/* We need to map the region into a HEALPix projection, of which there
+ are four variants: HPX centred on RA=0, HPX centred on RA=12h, XPH
+ centred on the north pole, XPH centred on the south pole. Problems
+ arise if the region spans any of discontinuities in the projection, so
+ loop round all four projections and find the one that maps the region
+ onto the smallest box in grid coords. */
+ npix_min = 0;
+ iproj_min = -1;
+ mapped_min = NULL;
+ for( iproj = 0; iproj <= AST__MXPRJHPX; iproj++ ) {
+
+/* Extend the reg2hpx12 Mapping to get a Mapping from the region's
+ coords to grid coords in the current variant HPX projection. The
+ "xphmap" Mapping goes from grid coords in the projection given by
+ "iproj" into grid coords within an HPX12 projection so invert it
+ before using it. */
+ xphmap = astXphMap( maxorder, iproj, " ", status );
+ astInvert( xphmap );
+ reg2proj = astCmpMap( reg2hpx12, xphmap, 1, "", status );
+ xphmap = astAnnul( xphmap );
+
+/* Use this FrameSet to map the picked region into grid coords within the
+ projection specified by "iproj". If the Region is undefined in the
+ current projection, annull the error so that we can skip to the
+ next projection. */
+ if( astOK ) {
+ int rep = astReporting( 0 );
+ mapped = astMapRegion( picked, reg2proj, gridframe );
+ if( astStatus == AST__NODEF ) astClearStatus;
+ astReporting( rep );
+ }
+
+/* Find the bounds of the Region in grid coords. Get the number of pixels
+ in the bounding box. */
+ if( mapped ) {
+ astGetRegionBounds( mapped, glbnd, gubnd );
+ npix = (int)( ( gubnd[0] - glbnd[0] + 1 )*( gubnd[1] - glbnd[1] + 1 ) );
+
+/* If this is the smallest bounding box found so far, record its details,
+ increasing its size by 5% at each edge for safety. */
+ if( iproj == 0 || npix < npix_min ) {
+ delta = 0.05*( gubnd[0] - glbnd[0] + 1 );
+ glbnd_min[ 0 ] = (int) ( glbnd[ 0 ] + HALF - delta );
+ gubnd_min[ 0 ] = (int) ( gubnd[ 0 ] + HALF + delta );
+ delta = 0.05*( gubnd[1] - glbnd[1] + 1 );
+ glbnd_min[ 1 ] = (int) ( glbnd[ 1 ] + HALF - delta );
+ gubnd_min[ 1 ] = (int) ( gubnd[ 1 ] + HALF + delta );
+ npix_min = npix;
+ iproj_min = iproj;
+ if( mapped_min ) mapped_min = astAnnul( mapped_min );
+ mapped_min = astClone( mapped );
+ }
+
+/* Free resources. */
+ mapped = astAnnul( mapped );
+ }
+ reg2proj = astAnnul( reg2proj );
+ }
+
+/* Report an error if the supplied Region could not be mapped into a
+ defined region using any of the available HPX-like projections. */
+ if( !mapped_min && astOK ) {
+ astError( AST__NODEF, "astAddRegion(%s): The supplied %s cannot "
+ "be mapped to HEALPix coordinates.", status,
+ astGetClass( this ), astGetClass( region ) );
+ }
+
+/* Generate Mappings for all required orders. Each of these Mappings goes
+ from grid coords in the HPX like projection at order N to grid coords
+ in the pixel array. The Mapping for order (N-1) is derived from the
+ Mapping for order N by dividing each grid cell at order (N-1) into four.
+ This is done by prepending a suitable WinMap to the start of the Mapping
+ for order N. The winmap maps the bottom left 2x2 box of pixels at order
+ N (0.5:2.5,0.5:2.5) onto the bottom left pixel at order (N-1)
+ (0.5:1.5,0.5:1.5). */
+ ina[ 0 ] = 0.5;
+ ina[ 1 ] = 0.5;
+ inb[ 0 ] = 1.5;
+ inb[ 1 ] = 1.5;
+ outa[ 0 ] = 0.5;
+ outa[ 1 ] = 0.5;
+ outb[ 0 ] = 2.5;
+ outb[ 1 ] = 2.5;
+ incmap = astWinMap( 2, ina, inb, outa, outb, "", status );
+ hpx2region = (AstMapping *) astUnitMap( 2, "", status );
+ for( i = maxorder; i >= 0; i-- ) {
+ if( i < minorder ) {
+ maps[ i ] = NULL;
+ } else {
+ maps[ i ] = astSimplify( hpx2region );
+ tempmap = astCmpMap( incmap, hpx2region, 1, "", status );
+ (void) astAnnul( hpx2region );
+ hpx2region = (AstMapping *) tempmap;
+ }
+ }
+ incmap = astAnnul( incmap );
+ hpx2region = astAnnul( hpx2region );
+
+/* Get the Mapping from hpx-like grid coords at order maxorder to
+ hpx-like grid coords at order minorder. */
+ astInvert( maps[minorder] );
+ tempmap = astCmpMap( maps[maxorder], maps[minorder], 1, " ", status );
+ astInvert( maps[minorder] );
+ tempmap2 = astSimplify( tempmap );
+ tempmap = astAnnul( tempmap );
+
+/* Use this Mapping to transform the bounding box from maxorder to
+ minorder. Add a one pixel safety margin. */
+ ina[ 0 ] = glbnd_min[ 0 ];
+ inb[ 0 ] = glbnd_min[ 1 ];
+ ina[ 1 ] = gubnd_min[ 0 ];
+ inb[ 1 ] = gubnd_min[ 1 ];
+ astTran2( tempmap2, 2, ina, inb, 1, outa, outb );
+ glbnd_min[ 0 ] = (int)( outa[ 0 ] + 0.5 ) - 1;
+ glbnd_min[ 1 ] = (int)( outb[ 0 ] + 0.5 ) - 1;
+ gubnd_min[ 0 ] = (int)( outa[ 1 ] + 0.5 ) + 1;
+ gubnd_min[ 1 ] = (int)( outb[ 1 ] + 0.5 ) + 1;
+ tempmap2 = astAnnul( tempmap2 );
+
+/* Initialise a CellList structure holding the grid coords of the cells
+ to add into the Moc at each order. This also holds Mappings from
+ projection "iproj_min" to HPX12 at each order. */
+ memset( &clist, 0, sizeof(clist) );
+ clist.maxorder = maxorder;
+ for( i = minorder; i <= maxorder; i++ ) {
+ clist.maps[ i ] = astXphMap( i, iproj_min, " ", status );
+ }
+
+/* Loop over all cells in the best bounding box at minorder. */
+ for( iy = glbnd_min[ 1 ]; iy <= gubnd_min[ 1 ]; iy++ ) {
+ for( ix = glbnd_min[ 0 ]; ix <= gubnd_min[ 0 ]; ix++ ) {
+
+/* Add to the CellList any parts of the current cell that are within the
+ selected areas. */
+ PutCell( this, maps, ix, iy, minorder, &clist, 0, mapped_min,
+ cmode, "astAddRegion", status );
+ }
+ }
+
+/* Convert all the grid coords stored in "clist" into nested indices at
+ order "maxorder" and incorporate them into the current contents of the
+ Moc. */
+ IncorporateCells( this, &clist, negated, cmode, "astAddRegion", status );
+
+/* Reinstate the original value of the Nagated flag for the Region. */
+ astSetNegated( picked, negated );
+
+/* Free resources. */
+ for( i = minorder; i <= maxorder; i++ ) {
+ maps[ i ] = astAnnul( maps[ i ] ) ;
+ clist.ix[ i ] = astFree( clist.ix[ i ] );
+ clist.iy[ i ] = astFree( clist.iy[ i ] );
+ clist.maps[ i ] = astAnnul( clist.maps[ i ] );
+ }
+ picked = astAnnul( picked );
+ mapped_min = astAnnul( mapped_min );
+ picked_frame = astAnnul( picked_frame );
+ reg2hpx12 = astAnnul( reg2hpx12 );
+ gridframe = astAnnul( gridframe );
+ }
+ }
+}
+
+static void AppendChildren( AstMoc *this, Cell *cell, int order,
+ Cell **cell_foot, int *status ){
+/*
+* Name:
+* AppendChildren
+
+* Purpose:
+* Create Cells for the direct children of a specified Cell.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void AppendChildren( AstMoc *this, Cell *cell, int order,
+* Cell **cell_foot, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function creates Cell structures to describe the four direct
+* children of the supplied cell, and appends them to the chain of
+* linked Cells for order (order+1). If the parent cell is flagged as
+* an interior cell, then all the children are also flagged as interior.
+
+* Parameters:
+* this
+* The Moc.
+* cell
+* The Cell structure describing the cell to be subdivided.
+* order
+* The order of the cell to be subdivided.
+* cell_foot
+* Pointer to an array with (AST__MXORDHPX+1) elements, one for
+* each order. Ech element is a pointer to the last Cell structure
+* in a chain of linked structures holding information about the cells
+* at the corresponding order.
+* status
+* Pointer to inherited status.
+
+*/
+
+/* Local Variables: */
+ Cell *new_cell;
+ int gx0;
+ int gy0;
+ int ix;
+ int iy;
+
+/* Check inherited status */
+ if( !astOK ) return;
+
+/* Increment the order to get the order for the new child cells. */
+ order++;
+
+/* Get the grid coords in an HPX12 projection at the centre of the bottom
+ left child cell at the new order. */
+ gx0 = 2*cell->ix - 1;
+ gy0 = 2*cell->iy - 1;
+
+/* Loop over the four direct children of the supplied cell. */
+ for( iy = 0; iy < 2; iy++ ) {
+ for( ix = 0; ix < 2; ix++ ) {
+
+/* Create the new Cell, appending it to the chain of Cells for the new
+ order. */
+ new_cell = MakeCell( gx0 + ix, gy0 + iy, order, cell_foot, status );
+
+/* If the parent Cell is interior, the child cells are also interior. */
+ new_cell->interior = cell->interior;
+ }
+ }
+}
+
+static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* ClearAttrib
+
+* Purpose:
+* Clear an attribute value for a Moc.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void ClearAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* Moc member function (over-rides the astClearAttrib protected
+* method inherited from the Region class).
+
+* Description:
+* This function clears the value of a specified attribute for a
+* Moc, so that the default value will subsequently be used.
+
+* Parameters:
+* this
+* Pointer to the Moc.
+* 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: */
+ AstMoc *this; /* Pointer to the Moc structure */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the Moc structure. */
+ this = (AstMoc *) this_object;
+
+/* Check the attribute name and clear the appropriate attribute. */
+
+/* MaxOrder and MaxRes. */
+/* -------------------- */
+ if ( !strcmp( attrib, "maxorder" ) || !strcmp( attrib, "maxres" ) ) {
+ if( astTestMaxOrder( this ) ) {
+ astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" "
+ "value for a %s.", status, attrib, astGetClass( this ) );
+ astError( AST__NOWRT, "The previously set value cannot be changed." ,
+ status);
+ } else {
+ astClearMaxOrder( this );
+ }
+
+/* MinOrder. */
+/* --------- */
+ } else if ( !strcmp( attrib, "minorder" ) ) {
+ astClearMinOrder( this );
+
+/* MinRes. */
+/* ------- */
+ } else if ( !strcmp( attrib, "minres" ) ) {
+ astClearMinOrder( 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, "moctype" ) ||
+ !strcmp( attrib, "moclength" ) ||
+ !strcmp( attrib, "mocarea" ) ){
+ 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 ClearCache( AstMoc *this, int *status ){
+/*
+*
+* Name:
+* ClearCache
+
+* Purpose:
+* Clear the cached values stored in a Moc structure.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void ClearCache( AstMoc *this, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* Clears the cached values stored in a Moc structure. It should be
+* called whenever anythiong modifies the region represented by the Moc.
+
+* Parameters:
+* this
+* The Moc.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Delete the normalised form so that it is recalculated when it is
+ next required. */
+ this->inorm = astFree( this->inorm );
+ this->knorm = astFree( this->knorm );
+
+/* Delete the Moc's base mesh and the index of the base mesh that
+ orders the mesh points around the perimeter. */
+ if( this->basemesh ) this->basemesh = astAnnul( this->basemesh );
+ this->meshdist = astFree( this->meshdist );
+
+/* Indicate the bounding box needs to be recalculated. */
+ this->lbnd[ 0 ] = AST__BAD;
+ this->lbnd[ 1 ] = AST__BAD;
+ this->ubnd[ 0 ] = AST__BAD;
+ this->ubnd[ 1 ] = AST__BAD;
+
+/* Indicate the Moc's area needs to be recalculated. */
+ this->mocarea = AST__BAD;
+
+/* The default uncertainty. */
+ if( this->unc ) this->unc = astAnnul( this->unc );
+}
+
+static void ClearMaxOrder( AstMoc *this, int *status ){
+/*
+*
+* Name:
+* ClearMaxOrder
+
+* Purpose:
+* Clear the value for the MaxOrder attribute
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void ClearMaxOrder( AstMoc *this, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* Clears the value of the MaxPlot attribute.
+
+* Parameters:
+* this
+* The Moc.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Clear the value */
+ this->maxorder = -INT_MAX;
+
+/* Clear cached items that depend on the max order value. */
+ ClearCache( this, status );
+}
+
+static void CombineRanges( AstMoc *this, int cmode, const char *method,
+ int *status ){
+/*
+* Name:
+* CombineRanges
+
+* Purpose:
+* Combine the ranges of nested indices currently stored in a Moc.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void CombineRanges( AstMoc *this, int cmode, const char *method,
+* int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function combines the separate ranges of nested index stored
+* in a Moc, using the specified combination method.
+
+* Parameters:
+* this
+* Pointer to the Moc.
+* cmode
+* Indicates how the ranges are to be combined. Any of the following
+* values may be supplied:
+* - AST__AND: The modified Moc is the intersection of the original
+* Moc and the cell list.
+* - AST__OR: The modified Moc is the union of the original Moc and
+* the cell list.
+* - AST__XOR: The modified Moc is the exclusive disjunction of the
+* original Moc and the cell list.
+* method:
+* Name of calling method.
+* status
+* The inherited status.
+
+*/
+
+/* Local Variables: */
+ int irange;
+ int nnew;
+ int64_t *newlist;
+ int64_t *pcur;
+ int64_t *pnew;
+ int64_t *pr;
+ int64_t *pw;
+ int64_t cur_end;
+ int64_t cur_start;
+
+/* Check inherited status */
+ if( !astOK ) return;
+
+/* Nothing to do if there are fewer than 2 ranges in the Moc. */
+ if( this->nrange > 1 ) {
+
+/* Now sort all the ranges currently stored in the Moc into increasing order
+ of lower bound. */
+ qsort( this->range, this->nrange, 2*sizeof(*(this->range)), Comp_range );
+
+/* Result is union of old and new */
+/* ------------------------------ */
+ if( cmode == AST__OR ){
+
+/* Merge overlapping ranges. "pnew" points to each range in the Moc in
+ turn. If it overlaps the previous range (the "current" range), then
+ the previous range is extended to incorprate it. */
+ nnew = 1;
+ cur_start = this->range[0];
+ cur_end = this->range[1];
+ pnew = this->range + 2;
+ pw = this->range;
+ for( irange = 1; irange < this->nrange; irange++,pnew += 2 ) {
+
+/* Gap between new range and current range. New range becomes current
+ range. Increment the number of new ranges. */
+ if( pnew[ 0 ] > cur_end + 1 ) {
+ pw[ 0 ] = cur_start;
+ pw[ 1 ] = cur_end;
+ pw += 2;
+ cur_start = pnew[ 0 ];
+ cur_end = pnew[ 1 ];
+ nnew++;
+
+/* New range extends the current range. The upper bound of the current
+ range is set to the upper bound of the new range. */
+ } else if( pnew[ 1 ] > cur_end ){
+ cur_end = pnew[ 1 ];
+
+/* New range is contained within the current range. Do nothing. */
+ }
+ }
+
+/* Add in the final range. */
+ pw[ 0 ] = cur_start;
+ pw[ 1 ] = cur_end;
+
+/* Update the number of ranges in the Moc. */
+ this->nrange = nnew;
+
+/* Result is intersection of old and new */
+/* ------------------------------ */
+ } else if( cmode == AST__AND ){
+
+/* Look for overlaps of adjcent ranges. "pnew" points to each range in the Moc in
+ turn. If it overlaps the "current" range, then append the overlap
+ range to a new list of ranges. */
+ nnew = 0;
+ newlist = NULL;
+ pcur = this->range;
+ pnew = this->range + 2;
+ for( irange = 1; irange < this->nrange; irange++,pnew += 2 ) {
+
+/* Gap between new range and current range, so no intersection and
+ nothing to store. New range becomes current range. */
+ if( pnew[ 0 ] > pcur[ 1 ] ) {
+ pcur = pnew;
+
+/* New range overlaps the current range. Store the overlap in the list of
+ new ranges. New range becomes the current range if it extends beyond
+ the current range. */
+ } else {
+ newlist = astGrow( newlist, nnew + 1, 2*sizeof(*newlist) );
+ if( astOK ) {
+ pr = newlist +2*(nnew++);
+ pr[ 0 ] = pnew[ 0 ];
+ if( pnew[ 1 ] > pcur[ 1 ] ) {
+ pr[ 1 ] = pcur[ 1 ];
+ pcur = pnew;
+ } else {
+ pr[ 1 ] = pnew[ 1 ];
+ }
+ }
+ }
+ }
+
+/* Update the ranges in the Moc. */
+ (void) astFree( this->range );
+ this->range = newlist;
+ this->nrange = nnew;
+
+/* Result is exclusive disjunction of old and new */
+/* ---------------------------------------------- */
+ } else if( cmode == AST__XOR ){
+
+/* Look for overlaps of adjcent ranges. "pnew" points to each range in the Moc in
+ turn. If it overlaps the "current" range, then append the earlier non-overlap
+ range to a new list of ranges. */
+ nnew = 0;
+ newlist = NULL;
+ pcur = this->range;
+ pnew = this->range + 2;
+ for( irange = 1; irange < this->nrange; irange++,pnew += 2 ) {
+
+/* Gap between new range and current range, so no intersection. Add the
+ current range to the new range list. The new range then becomes the
+ current range. */
+ if( pnew[ 0 ] > pcur[ 1 ] ) {
+ newlist = astGrow( newlist, nnew + 1, 2*sizeof(*newlist) );
+ if( astOK ) {
+ pr = newlist +2*(nnew++);
+ pr[ 0 ] = pcur[ 0 ];
+ pr[ 1 ] = pcur[ 1 ];
+ }
+ pcur = pnew;
+
+/* New range overlaps the current range. Store the earlier section of the
+ current range (before the new range starts) as a new range. */
+ } else {
+ newlist = astGrow( newlist, nnew + 1, 2*sizeof(*newlist) );
+ if( astOK ) {
+ pr = newlist +2*(nnew++);
+ pr[ 0 ] = pcur[ 0 ];
+ pr[ 1 ] = pnew[ 0 ] - 1;
+ }
+
+/* If the new range ends after the current range ends, modify the lower bound
+ of the new range to be adjacent to the upper bound of the current
+ range. Then use the new range as the current range. */
+ if( pcur[ 1 ] < pnew[ 1 ] ) {
+ pnew[ 0 ] = pcur[ 1 ] + 1;
+ pcur = pnew;
+
+/* If the new range ends before the current range ends, modify the lower bound
+ of the current range to be adjacent to the upper bound of the new range.
+ The current range pointer remains unchanged. */
+ } else {
+ pcur[ 0 ] = pnew[ 1 ] + 1;
+ }
+ }
+ }
+
+/* Add in the final range. */
+ newlist = astGrow( newlist, nnew + 1, 2*sizeof(*newlist) );
+ if( astOK ) {
+ pr = newlist +2*(nnew++);
+ pr[ 0 ] = pcur[ 0 ];
+ pr[ 1 ] = pcur[ 1 ];
+ }
+
+/* Update the ranges in the Moc. */
+ (void) astFree( this->range );
+ this->range = newlist;
+ this->nrange = nnew;
+
+/* Unknown operation new */
+/* --------------------- */
+ } else if( astOK ){
+ astError( AST__BDPAR, "%s(%s): Bad value (%d) suppied for "
+ "parameter 'cmode'.", status, method,
+ astGetClass(this), cmode );
+ }
+
+/* Check the new ranges do not overlap and are not reversed. */
+ if( astOK && this->nrange > 0 ) {
+ pcur = this->range;
+ if( pcur[ 1 ] < pcur[ 0 ] ) {
+ astError( AST__INTER, "CombineRanges(%s): Range 0 [%zu:%zu]"
+ " - upper bound lower than lower bound (internal "
+ "programming error).", status, astGetClass(this),
+ pcur[ 0 ], pcur[ 1 ] );
+ } else {
+ pnew = this->range + 2;
+ for( irange = 1; irange < this->nrange; irange++,pnew += 2 ) {
+ if( pnew[ 1 ] < pnew[ 0 ] ) {
+ astError( AST__INTER, "CombineRanges(%s): Range %d [%zu:%zu]"
+ " - upper bound lower than lower bound (internal "
+ "programming error).", status, astGetClass(this),
+ irange, pnew[ 0 ], pnew[ 1 ] );
+ break;
+ } else if( pcur[ 1 ] >= pnew[ 0 ] ) {
+ astError( AST__INTER, "CombineRanges(%s): Range %d [%zu:%zu]"
+ " overlaps range %d [%zu:%zu] (internal programming "
+ "error).", status, astGetClass(this), irange - 1,
+ pcur[ 0 ], pcur[ 1 ], irange, pnew[ 0 ], pnew[ 1 ] );
+ break;
+ }
+ pcur = pnew;
+ }
+ }
+ }
+ }
+
+/* Clear the cached information stored in the Moc structure so that it is
+ re-calculated when next needed. */
+ ClearCache( this, status );
+}
+
+static int Comp_corner( const void *a, const void *b ){
+/*
+* Name:
+* Comp_corner
+
+* Purpose:
+* A comparison function for use with the qsort function.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* int Comp_corner( const void *a, const void *b )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function compares two sky positions (held in two Corner
+* structures) and returns a value indicating which comes first.
+* Positions are sorted first by Dec and then by RA.
+*
+* Values that are close to each other in Dec and RA are considered
+* to be co-incident. The tolerance in Dec is given by Comp_Corner_Tol.
+* The tolerance in RA depends on the value of Comp_Corner_Exact:
+* 0 - The RA equivalent of the arc-distance given by Comp_Corner_Tol
+* 1 - Zero tolerance (i.e. RA values must match exactly to be
+* considered co-incident).
+
+* Parameters:
+* a
+* Pointer to the address of the first Corner.
+* b
+* Pointer to the address of the second Corner.
+
+* Returned Value:
+* An integer less than, equal to, or greater than zero if the
+* first parameter is considered to be respectively less than,
+* equal to, or greater than the second.
+
+*/
+ astDECLARE_GLOBALS
+ astGET_GLOBALS(NULL);
+ double ratol;
+
+ Corner *ac = *((Corner **) a);
+ Corner *bc = *((Corner **) b);
+
+ double ra1 = ac->ra;
+ double dec1 = ac->dec;
+ double ra2 = bc->ra;
+ double dec2 = bc->dec;
+
+/* If required, shift the RA values from [0,2.PI[ to [-PI,+PI[. */
+ if( Comp_Corner_Loop != 0 ) {
+ while( ra1 < -AST__DPI ) ra1 += 2*AST__DPI;
+ while( ra2 < -AST__DPI ) ra2 += 2*AST__DPI;
+ while( ra1 >= AST__DPI ) ra1 -= 2*AST__DPI;
+ while( ra2 >= AST__DPI ) ra2 -= 2*AST__DPI;
+ }
+
+/* Get the tolerance to allow in RA. */
+ if( Comp_Corner_Exact ) {
+ ratol = 0.0;
+ } else if( ac->cosdec > bc->cosdec ){
+ ratol = Comp_Corner_Tol/ac->cosdec;
+ } else {
+ ratol = Comp_Corner_Tol/bc->cosdec;
+ }
+
+/* Compare Dec, then RA. Allow some tolerance on Dec so that the cells on
+ a single HEALPix ring (constant Dec) get sorted by RA together. */
+ if( fabs( dec1 - dec2 ) <= Comp_Corner_Tol ) {
+ if( fabs( ra1 - ra2 ) <= ratol ) {
+ return 0;
+ } else if( ra1 < ra2 ) {
+ return -1;
+ } else {
+ return 1;
+ }
+ } else if( dec1 < dec2 ) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+
+static int Comp_decra( const void *a, const void *b ){
+/*
+* Name:
+* Comp_decra
+
+* Purpose:
+* A comparison function for use with the qsort function.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* int Comp_decra( const void *a, const void *b )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function compares two sky positions (held in two double
+* arrays) and returns a value indicating which comes first.
+* Positions are sorted first by Dec and then by RA.
+
+* Parameters:
+* a
+* Pointer to the integer index of the first position within
+* the arrays pointed to by Comp_Decra_Ptr1/2. (Ptr1 holds RA
+* values, Ptr2 holds Dec values).
+* b
+* Pointer to the index of the second position.
+
+* Returned Value:
+* An integer less than, equal to, or greater than zero if the
+* first parameter is considered to be respectively less than,
+* equal to, or greater than the second.
+
+*/
+ astDECLARE_GLOBALS
+ astGET_GLOBALS(NULL);
+
+/* Get the integer indices of the two points */
+ int ia = *((int *) a);
+ int ib = *((int *) b);
+
+/* Get the RA and Dec of the two points */
+ double ra1 = Comp_Decra_Ptr1[ ia ];
+ double dec1 = Comp_Decra_Ptr2[ ia ];
+ double ra2 = Comp_Decra_Ptr1[ ib ];
+ double dec2 = Comp_Decra_Ptr2[ ib ];
+
+/* Compare Dec, then RA. */
+ if( dec1 == dec2 ) {
+ if( ra1 == ra2 ) {
+ return 0;
+ } else if( ra1 < ra2 ) {
+ return -1;
+ } else {
+ return 1;
+ }
+ } else if( dec1 < dec2 ) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+
+static int Comp_range( const void *a, const void *b ){
+/*
+* Name:
+* Comp_range
+
+* Purpose:
+* A comparison function for use with the qsort function.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* int Comp_range( const void *a, const void *b )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function compares two Moc ranges and returns a
+* value indicating which has the larger lower bound. A
+* range is a pair of consecutive int64_t values, in the
+* order (lower bound, upper bound).
+
+* Parameters:
+* a
+* Pointer to the first range.
+* b
+* Pointer to the second range.
+
+* Returned Value:
+* An integer less than, equal to, or greater than zero if the
+* first parameter is considered to be respectively less than,
+* equal to, or greater than the second.
+
+*/
+ if( *(int64_t *) a < *(int64_t *) b ) {
+ return -1;
+ } else if( *(int64_t *) a > *(int64_t *) b ) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int Comp_int64( const void *a, const void *b ){
+/*
+* Name:
+* Comp_int64
+
+* Purpose:
+* A comparison function for use with the qsort function.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* int Comp_int64( const void *a, const void *b )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function compares two signed 64 bit integers and returns a
+* value indicating which is larger.
+
+* Parameters:
+* a
+* Pointer to the first uint_64 value.
+* b
+* Pointer to the second uint_64 value.
+
+* Returned Value:
+* An integer less than, equal to, or greater than zero if the
+* first parameter is considered to be respectively less than,
+* equal to, or greater than the second.
+
+*/
+ if( *(int64_t *) a < *(int64_t *) b ) {
+ return -1;
+ } else if( *(int64_t *) a > *(int64_t *) b ) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int Equal( AstObject *this_object, AstObject *that_object, int *status ) {
+/*
+* Name:
+* Equal
+
+* Purpose:
+* Test if two Objects are equivalent.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "object.h"
+* int Equal( AstObject *this_object, AstObject *that_object, int *status )
+
+* Class Membership:
+* Moc member function (over-rides the astEqual protected
+* method inherited from the Region class).
+
+* Description:
+* This function returns a boolean result (0 or 1) to indicate whether
+* two Mocs are equivalent.
+
+* Parameters:
+* this
+* Pointer to the first Moc.
+* that
+* Pointer to the second Moc.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* One if the Mocs 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: */
+ AstMoc *that;
+ AstMoc *this;
+ int irange;
+ int result;
+ int64_t *p1;
+ int64_t *p2;
+
+/* Initialise. */
+ result = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Invoke the Equal method inherited from the parent Region class. This checks
+ that the Objects are both of the same class, and have the same Negated
+ and Closed flags (amongst other things). */
+ if( (*parent_equal)( this_object, that_object, status ) ) {
+
+/* Obtain pointers to the two Moc structures. */
+ this = (AstMoc *) this_object;
+ that = (AstMoc *) that_object;
+
+/* Test their MaxOrder values are equal. */
+ if( astGetMaxOrder(this) == astGetMaxOrder(that) ) {
+
+/* Test the ranges of cells included in both are equal. */
+ if( this->nrange == that->nrange ) {
+ result = 1;
+ p1 = this->range;
+ p2 = that->range;
+ for( irange = 0; irange < this->nrange; irange++ ) {
+ if( p1[ 0 ] != p2[ 0 ] ||
+ p1[ 1 ] != p2[ 1 ] ) {
+ result = 0;
+ break;
+ }
+ p1 += 2;
+ p2 += 2;
+ }
+ }
+ }
+ }
+
+/* If an error occurred, clear the result value. */
+ if ( !astOK ) result = 0;
+
+/* Return the result, */
+ return result;
+}
+
+static AstFrame *FindSkyAxes( AstFrame *frame, const char *method,
+ int *status ){
+/*
+* Name:
+* FindSkyAxes
+
+* Purpose:
+* Extract the sky axes from a supplied Frame.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* AstFrame *FindSkyAxes( AstFrame *frame, const char *method,
+* int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function searches the supplied Frame for a SkyFrame. If found,
+* it creates a new Frame containing just the sky axes and returns a
+* pointer to the new Frame. If the supplied Frame is itself a SkyFrame,
+* then the returned pointer is just a clone of the supplied pointer.
+*
+* The supplied Frame must be either a Region or a FrameSet. If it is
+* a Region, an error will be reported if the returned Frame is not
+* also a Region. If it is a FrameSet, the returned Frame will be a
+* FrameSet in which the current Frame is a SkyFrame, and the base Frame
+* holds the two corresponding axes from the supplied baseFrame. An
+* error will be reported if the sky axes do not correspond to a
+* distinct pair of axes in the supplied FrameSet.
+
+* Parameters:
+* frame
+* Pointer to the Frame - a Region with any number of axes, or a
+* FrameSet with a 2D base Frame (the current frame can have any
+* number of axes).
+* method
+* The name of the calling method.
+* status
+* Inherited status value.
+
+* Returned Value:
+* Pointer to the new Frame.
+
+*/
+
+/* Local Variables: */
+ AstFrame *bfrm;
+ AstFrame *check_frame;
+ AstFrame *frm;
+ AstFrame *result = NULL;
+ AstMapping *map2;
+ AstMapping *map;
+ AstPermMap *pm;
+ int *perm;
+ int iax;
+ int nax;
+ int pax;
+ int skyaxes[ 2 ];
+
+/* Check the global error status. */
+ if( !astOK ) return result;
+
+/* Get the Frame to check for sky axes. */
+ if( astIsARegion( frame ) ){
+ check_frame = astRegFrame( frame );
+ } else if( astIsAFrameSet( frame ) ){
+ check_frame = astGetFrame( frame, AST__CURRENT );
+ } else {
+ check_frame = astClone( frame );
+ }
+
+/* Get the indices of the sky axes in the supplied Frame. */
+ skyaxes[ 0 ] = -1;
+ skyaxes[ 1 ] = -1;
+ nax = astGetNaxes( check_frame );
+ for( iax = 0; iax < nax; iax++ ) {
+ astPrimaryFrame( check_frame, iax, &frm, &pax );
+ if( astIsASkyFrame( frm ) ){
+ if( pax == 0 ){
+ skyaxes[ 0 ] = iax;
+ } else {
+ skyaxes[ 1 ] = iax;
+ }
+ }
+ frm = astAnnul( frm );
+ }
+ check_frame = astAnnul( check_frame );
+
+/* Report an error if a pair of skyaxes was not found. */
+ if( skyaxes[ 0 ] == -1 || skyaxes[ 1 ] == -1 ) {
+ if( astOK ) {
+ astError( AST__NOSKY, "%s(Moc): The supplied %s does not contain "
+ "a pair of celestial coordinate axes.", status, method,
+ astGetClass(frame) );
+ }
+
+/* We proceed only if a SkyFrame was found. First deal with Regions. */
+ } else if( astIsARegion( frame ) ){
+
+/* The Region may also contain other axes in addition to the sky axes. So
+ attempt to pick the sky axes from the Region. Report an error if this is
+ not possible (as indicated by the returned object not being a Region). */
+ result = astPickAxes( frame, 2, skyaxes, NULL );
+ if( !astIsARegion( result ) && astOK ){
+ astError( AST__BDSKY, "%s(Moc): The region of sky described by the "
+ "supplied %s cannot be determined.", status, method,
+ astGetClass(frame) );
+ }
+
+/* Now deal with FrameSets. */
+ } else if( astIsAFrameSet( frame ) ){
+
+/* The astMapSplit has no "invert" parameter and so can only pick inputs, not
+ outputs. But we want to pick the sky axis outputs, so we need to invert
+ the FrameSet before calling astMapSplit. */
+ astInvert( frame );
+
+/* Split the mapping in parallel to obtain a Mapping from the output sky
+ axes to the corresponding two inputs. This may or may not be possible
+ depending on the nature of the Mapping. */
+ perm = astMapSplit( (AstMapping *) frame, 2, skyaxes, &map );
+
+/* Re-invert the FrameSet to bring it back top its original state. */
+ astInvert( frame );
+
+/* Check if the above map split was successful. */
+ if( map && astGetNout( map ) == 2 ) {
+
+/* Create the returned FrameSet, and initialise it to hold a copy of the
+ required two axes from the base Frame in the supplied FrameSet. */
+ bfrm = astGetFrame( frame, AST__BASE );
+ frm = astPickAxes( bfrm, 2, skyaxes, NULL );
+ result = (AstFrame *) astFrameSet( frm, "", status );
+ frm = astAnnul( frm );
+ bfrm = astAnnul( bfrm );
+
+/* Invert the Mapping returned by astMapSplit so that it goes from grid
+ coords to sky coords. */
+ astInvert( map );
+
+/* Create PermMap that permutes the grid coords from the order used in
+ the supplied FrameSet to the order expected for the inputs of "map".
+ No need to do this if there is no actual change of axes. */
+ if( perm[ 0 ] != 0 ) {
+ pm = astPermMap( 2, perm, 2, perm, NULL, "", status );
+ map2 = (AstMapping *) astCmpMap( pm, map, 1, "", status );
+ pm = astAnnul( pm );
+ astAnnul( map );
+ map = map2;
+ }
+ perm = astFree( perm );
+
+/* Get a Frame containing the sky axes picked from the supplied current
+ Frame. The Frame will be a SkyFrame. Add it into the returned
+ FrameSet, using the above Mapping to connect it to the base Frame. It
+ becomes the new current Frame. */
+ frm = astPickAxes( frame, 2, skyaxes, NULL );
+ astAddFrame( result, AST__BASE, map, frm );
+ frm = astAnnul( frm );
+ map = astAnnul( map );
+
+/* Report an error if the sky axes are not independent of the other axes. */
+ } else if( astOK ) {
+ astError( AST__BDSKY, "%s(Moc): The sky axes in the supplied WCS "
+ "FrameSet cannot be split from the other axes.", status,
+ method );
+ }
+
+/* Report an error for any other class of Frame. */
+ } else if( astOK ) {
+ astError( AST__BDCLS, "FindSkyAxes(Moc): A %s was supplied but a "
+ "Region or FrameSet is required.", status,
+ astGetClass( frame ) );
+ }
+
+/* Return the resulting Frame. */
+ 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 Moc.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* const char *GetAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* Moc member function (over-rides the protected astGetAttrib
+* method inherited from the Region class).
+
+* Description:
+* This function returns a pointer to the value of a specified
+* attribute for a Moc, formatted as a character string.
+
+* Parameters:
+* this
+* Pointer to the Moc.
+* 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 Moc, 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 Moc. 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: */
+ AstMoc *this; /* Pointer to the Moc structure */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ const char *result; /* Pointer value to return */
+ double dval; /* Floating point attribute value */
+ int ival; /* Integer 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 Moc structure. */
+ this = (AstMoc *) 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. */
+
+/* MaxOrder. */
+/* --------- */
+ if ( !strcmp( attrib, "maxorder" ) ) {
+ ival = astGetMaxOrder( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%d", ival );
+ result = getattrib_buff;
+ }
+
+/* MaxRes. */
+/* ------- */
+ } else if ( !strcmp( attrib, "maxres" ) ) {
+ if( !astTestMaxOrder( this ) ) {
+ dval = 0.0;
+ } else {
+ ival = astGetMaxOrder( this );
+ dval = OrderToRes( ival );
+ }
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval );
+ result = getattrib_buff;
+ }
+
+/* MinOrder. */
+/* --------- */
+ } else if ( !strcmp( attrib, "minorder" ) ) {
+ ival = astGetMinOrder( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%d", ival );
+ result = getattrib_buff;
+ }
+
+/* MinRes. */
+/* ------- */
+ } else if ( !strcmp( attrib, "minres" ) ) {
+ ival = astGetMinOrder( this );
+ dval = OrderToRes( ival );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval );
+ result = getattrib_buff;
+ }
+
+/* MocArea. */
+/* -------- */
+ } else if ( !strcmp( attrib, "mocarea" ) ) {
+ dval = astGetMocArea( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%.*g", AST__DBL_DIG, dval );
+ result = getattrib_buff;
+ }
+
+/* MocType. */
+/* -------- */
+ } else if ( !strcmp( attrib, "moctype" ) ) {
+ ival = astGetMocType( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%d", ival );
+ result = getattrib_buff;
+ }
+
+/* MocLength. */
+/* ---------- */
+ } else if ( !strcmp( attrib, "moclength" ) ) {
+ ival = astGetMocLength( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%d", ival );
+ result = getattrib_buff;
+ }
+
+/* If the attribute name was not recognised, pass it on to the parent
+ method for further interpretation. */
+ } else {
+ result = (*parent_getattrib)( this_object, attrib, status );
+ }
+
+/* Return the result. */
+ return result;
+
+}
+
+static AstMapping *GetCachedMapping( AstMoc *this, int order,
+ const char *method, int *status ){
+/*
+* Name:
+* GetCachedMapping
+
+* Purpose:
+* Get a Mapping from ICRS to HPX12 grid coords.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* AstMapping *GetCachedMapping( AstMoc *this, int order,
+* const char *method, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function returns a Mapping that goes from ICRS to grid
+* coordinates within an all-sky HPX12 projection.
+
+* Parameters:
+* this
+* The Moc.
+* order
+* The required HEALPix order.
+* method
+* A string holding the name of the calling methid. Used in error
+* messages.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The Mapping. This is a direct copy of the cached pointer, so it
+* should not be annulled by the calling function when no longer needed.
+
+*/
+
+/* Local Variables: */
+ AstFrameSet *fs;
+
+/* Check the global error status. */
+ if ( !astOK ) return NULL;
+
+/* If the cache does not contain a Mapping for the requested order,
+ create one now. */
+ if( !this->cached_maps[ order ] ) {
+
+/* Get a FrameSet describing a map of the whole sky in ICRS using an
+ HPX12 projection, with the maximum MOC order. The current frame
+ represents ICRS and the base Frame represents HPX12 grid coordinates.
+ Each pixel in the grid corresponds to a HEALPix cell on the sky. */
+ fs = GetHPX12FrameSet( this, order, status );
+
+/* Get the Mapping and store the cache. */
+ this->cached_maps[ order ] = astGetMapping( fs, AST__CURRENT,
+ AST__BASE );
+/* Free resources */
+ fs = astAnnul( fs );
+ }
+
+/* Return the cached Mapping. */
+ return this->cached_maps[ order ];
+}
+
+static void GetCell( AstMoc *this, int icell, int *order, int64_t *npix,
+ int *status ) {
+/*
+*++
+* Name:
+c astGetCell
+f AST_GETCELL
+
+* Purpose:
+* Identify the next cell in a normalised Moc.
+
+* Type:
+* Public virtual function.
+
+* Synopsis:
+c #include "moc.h"
+c void astGetCell( AstMoc *this, int icell, int *order, int64_t *npix )
+f CALL AST_GETCELL( THIS, ICELL, ORDER, NPIX, STATUS )
+
+* Class Membership:
+* Moc method.
+
+* Description:
+* This function returns the order and "npix" value for the cell at a
+* specified index in the normalised Moc. See the MOC recommendation
+* for more information about "npix" values and MOC normalisation.
+
+* Parameters:
+c this
+f THIS = INTEGER (Given)
+* Pointer to the Moc to be modified.
+c icell
+f ICELL = INTEGER (Given)
+* The index of the cell for which information is required. The
+* first cell has index
+c zero.
+f one.
+* An error will be reported if the supplied value is greater than
+c or equal to
+* the value of the MocLength attribute.
+c order
+f ORDER = INTEGER (Returned)
+* Returned holding the HEALPix order of the cell at the requested
+* index.
+c npix
+f NPIX = INTEGER*8 (Returned)
+* Returned holding the "npix" value of the cell at the requested
+* index.
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+*--
+*/
+
+/* Local Variables: */
+ int moclen;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Ensure we have the normalised form available in the Moc structure. */
+ GetNorm( this, "astGetCell", status );
+
+/* Validate. */
+ moclen = astGetMocLength( this );
+ if( icell < 0 ) {
+ astError( AST__INVAR, "astGetCell(%s): Invalid value (%d "
+ "zero-based) supplied for parameter 'icell' - "
+ "must be greater than or equal to zero.", status,
+ astGetClass( this ), icell );
+
+ } else if( icell >= moclen ) {
+ astError( AST__INVAR, "astGetCell(%s): Invalid value (%d "
+ "zero-based) supplied for parameter 'icell' - "
+ "must be less than %d.", status, astGetClass( this ),
+ icell, moclen );
+
+/* Decode the nuniq value to get the order and npix, using a fast log2
+ function. */
+ } else {
+ if( this->inorm ) {
+ *order = log2_32( (this->inorm)[ icell ] / 4 ) / 2;
+ *npix = (this->inorm)[ icell ] - ( 1 << (2 + 2*(*order)) );
+ } else {
+ *order = log2_64( (this->knorm)[ icell ] / 4 ) / 2;
+ *npix = (this->knorm)[ icell ] - ( 1L << (2 + 2*(*order)) );
+ }
+ }
+}
+
+static AstRegion *GetDefUnc( AstRegion *this_region, int *status ) {
+/*
+* Name:
+* GetDefUnc
+
+* Purpose:
+* Obtain a pointer to the default uncertainty Region for a mOC.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "region.h"
+* AstRegion *GetDefUnc( AstRegion *this, int *status )
+
+* Class Membership:
+* Moc 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 Moc. The returned Region refers to the base Frame within the
+* FrameSet encapsulated by the Moc (always ICRS).
+
+* Parameters:
+* this
+* Pointer to the Moc.
+* 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: */
+ AstFrame *bfrm;
+ AstMoc *this;
+ AstRegion *result;
+ double centre[ 2 ];
+ double res;
+
+/* Initialise */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Get a pointer to the Moc structure. */
+ this = (AstMoc *) this_region;
+
+/* If the default uncertainty Region is not available within the Moc
+ structure, create it now. */
+ if( !this->unc ) {
+
+/* Get the resolution in rads. */
+ res = AST__DD2R*OrderToRes( astGetMaxOrder( this ) )/3600.0;
+
+/* Create a Circle centred at the origin of ICRS with a radius equal to
+ the resolution. The base Frame of the FrameSet encapsulated by the
+ parent Region structure will always be ICRS. */
+ bfrm = astGetFrame( this_region->frameset, AST__BASE );
+ centre[ 0 ] = 0.0;
+ centre[ 1 ] = 0.0;
+ this->unc = (AstRegion *) astCircle( bfrm, 1, centre, &res, NULL,
+ " ", status );
+ bfrm = astAnnul( bfrm );
+ }
+
+/* Return a pointer to the cached unertainty Region. */
+ result = astClone( this->unc );
+
+/* Return NULL if an error occurred. */
+ if( !astOK ) result = astAnnul( result );
+
+/* Return the required pointer. */
+ return result;
+}
+
+static AstFrameSet *GetHPX12FrameSet( AstMoc *this, int order, int *status ){
+/*
+* Name:
+* GetHPX12FrameSet
+
+* Purpose:
+* Get a WCS FrameSet for an all-sky HPX12 projection.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* AstFrameSet *GetHPX12FrameSet( AstMoc *this, int order, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function returns a FrameSet containing two Frames. The base
+* Frame represents grid coordinates in an all-sky HPX12 projection.
+* The current Frame represents ICRS.
+
+* Parameters:
+* this
+* The Moc.
+* order
+* The HEALPix order.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The WCS FrameSet.
+
+*/
+
+/* Local Variables: */
+ AstCmpMap *map1;
+ AstCmpMap *map2;
+ AstCmpMap *map3;
+ AstFrame *gf;
+ AstFrameSet *result;
+ AstMatrixMap *mm;
+ AstShiftMap *sm1;
+ AstShiftMap *sm2;
+ AstSkyFrame *sf;
+ AstWcsMap *wm;
+ double m;
+ double matrix[ 4 ];
+ double shift[ 2 ];
+ int nppf;
+
+/* Check the global error status. */
+ if ( !astOK ) return NULL;
+
+/* Get the number of pixels along one edge of a facet. */
+ nppf = ( 1 << order );
+
+/* A ShiftMap that puts the origin at the centre of the array. The array
+ of values has dimensions (5*nppf,5*nppf). */
+ shift[ 0 ] = -0.5*( 1.0 + 5*nppf );
+ shift[ 1 ] = shift[ 0 ];
+ sm1 = astShiftMap( 2, shift, " ", status );
+
+/* A MatrixMap that converts grid coordinates into radians and rotates by
+ 45 degrees. */
+ m = AST__DPI/( 4.0*nppf );
+ matrix[ 0 ] = m;
+ matrix[ 1 ] = m;
+ matrix[ 2 ] = -m;
+ matrix[ 3 ] = m;
+ mm = astMatrixMap( 2, 2, 0, matrix, " ", status );
+
+/* The HPX projection, with "LONPOLE" set to 180 degrees. This needs to
+ be inverted so it goes from cartesian to spherical coords. Set the
+ protected LonCheck attribute so that the projection is not clipped for
+ longitude values outside the primary range. */
+ wm = astWcsMap( 2, AST__HPX, 1, 2, "PV1_3=180", status );
+ astSetLonCheck( wm, 0 );
+ astInvert( wm );
+
+/* A shiftmap to put the origin of native spherical coords at 12 hours. */
+ shift[ 0 ] = AST__DPI;
+ shift[ 1 ] = 0.0;
+ sm2 = astShiftMap( 2, shift, " ", status );
+
+/* Join them all together in series to get the mapping form grid coords
+ to ICRS (lon,lat). */
+ map1 = astCmpMap( sm1, mm, 1, " ", status );
+ sm1 = astAnnul( sm1 );
+ mm = astAnnul( mm );
+
+ map2 = astCmpMap( map1, wm, 1, " ", status );
+ map1 = astAnnul( map1 );
+ wm = astAnnul( wm );
+
+ map3 = astCmpMap( map2, sm2, 1, " ", status );
+ map2 = astAnnul( map2 );
+ sm2 = astAnnul( sm2 );
+
+/* Create a basic 2D Frame to represent grid coords. */
+ gf = astFrame( 2, "Domain=GRID", status );
+
+/* Create a SkyFrame representing ICRS. */
+ sf = astSkyFrame( "System=ICRS", status );
+
+/* Create the required FrameSet, initially containing just the grid frame. */
+ result = astFrameSet( gf, " ", status );
+ gf = astAnnul( gf );
+
+/* Add in the ICRS Frame. */
+ astAddFrame( result, AST__BASE, map3, sf );
+ map3 = astAnnul( map3 );
+ sf = astAnnul( sf );
+
+/* Return the FrameSet. */
+ return result;
+}
+
+static void GetMocData( AstMoc *this, size_t mxsize, void *data, int *status ) {
+/*
+*++
+* Name:
+c astGetMocData
+f AST_GETMOCDATA
+
+* Purpose:
+* Get the FITS binary table data describing a Moc
+
+* Type:
+* Public function.
+
+* Synopsis:
+c #include "moc.h"
+c void astGetMocData( AstMoc *this, size_t mxsize, void *data )
+f CALL AST_GETMOCDATA( THIS, MXSIZE, DATA, STATUS )
+
+* Class Membership:
+* Moc method.
+
+* Description:
+* This function retrieves the data values that form the FITS binary
+* table representation of the MOC and stores them in a supplied array.
+* Such a table contains a single scalar-valued column in which each
+* row holds a signed integer identifier for a single HEALPix cell,
+* following the scheme described in the MOC recommendation. Depending
+* on the order of the Moc, these integers may be 4 bytes or 8 bytes.
+*
+* The number of rows in the table and the required integer data type
+* are available through the MocType and MocLength attributes of the
+* Moc class.
+*
+* The FITS headers to store in the FITS binary table can be obtained
+* using function
+c astGetMocHeader.
+f AST_GETMOCHEADER.
+
+* Parameters:
+c this
+f THIS = INTEGER (Given)
+* Pointer to the Moc to be modified.
+c mxsize
+f MXSIZE = INTEGER (Given)
+* The length of the supplied array in bytes. An error will be reported
+* if this value is smaller than the number required to describe the
+* Moc (the product of the MocType and MocLength attributes).
+c data
+f DATA( * ) = BYTE (Returned)
+c Pointer to the
+f The
+* area of memory in which to return the signed integer cell
+* identifiers. This area is assumed to contain at least
+c "mxsize" bytes.
+f MXSIZE bytes.
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+*--
+*/
+
+/* Local Variables: */
+ size_t nbyte;
+ int moclen;
+ int type;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Ensure we have the normalised form available in the Moc structure. */
+ GetNorm( this, "astGetMocData", status );
+
+/* Validate. */
+ type = astGetMocType( this );
+ moclen = astGetMocLength( this );
+ nbyte = type*moclen;
+ if( mxsize < nbyte && astOK ) {
+ astError( AST__BADSIZ, "astGetMocData(%s): The supplied array "
+ "has %zu bytes but %zu are required.", status,
+ astGetClass(this), mxsize, nbyte );
+
+/* Copy the data into the supplied array. */
+ } else {
+ if( this->inorm ) {
+ memcpy( data, this->inorm, nbyte );
+ } else {
+ memcpy( data, this->knorm, nbyte );
+ }
+ }
+}
+
+static AstFitsChan *GetMocHeader( AstMoc *this, int *status ){
+/*
+*++
+* Name:
+c astGetMocHeader
+f AST_GETMOCHEADER
+
+* Purpose:
+* Get the FITS binary table headers describing a Moc
+
+* Type:
+* Public virtual function.
+
+* Synopsis:
+c #include "moc.h"
+c AstFitsCHan *astGetMocHeader( AstMoc *this )
+f RESULT = AST_GETMOCHEADER( THIS, STATUS )
+
+* Class Membership:
+* Moc method.
+
+* Description:
+* This function returns a FitsChan holding the headers that should be
+* stored in a FITS binary table extension describing the supplied Moc.
+* The data values for the extension can be obtained using method
+c astGetMocData.
+f AST_GETMOCDATA.
+
+* Parameters:
+c this
+f THIS = INTEGER (Given)
+* Pointer to the Moc to be modified.
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+*--
+*/
+
+/* Local Variables: */
+ AstFitsChan *result;
+ int maxorder;
+ int type;
+ int moclen;
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Create an empty FitsChan. */
+ result = astFitsChan( NULL, NULL, "", status );
+
+/* Get the Moc data type, length and highest order. */
+ type = astGetMocType( this );
+ moclen = astGetMocLength( this );
+ maxorder = astGetMaxOrder( this );
+
+/* Store the required values in it. */
+ astSetFitsS( result, "XTENSION", "BINTABLE", "HEALPix Multi Order"
+ " Coverage map", 1 );
+ astSetFitsI( result, "BITPIX", 8, NULL, 1 );
+ astSetFitsI( result, "NAXIS", 2, NULL, 1 );
+ astSetFitsI( result, "NAXIS1", type, "Bytes per row", 1 );
+ astSetFitsI( result, "NAXIS2", moclen, "No. of rows", 1 );
+ astSetFitsI( result, "PCOUNT", 0, NULL, 1 );
+ astSetFitsI( result, "GCOUNT", 1, NULL, 1 );
+ astSetFitsI( result, "TFIELDS", 1, NULL, 1 );
+ astSetFitsS( result, "TFORM1", (type==4)?"1J":"1K", NULL, 1 );
+ astSetFitsS( result, "TTYPE1", "UNIQ", "HEALPix UNIQ pixel number", 1 );
+ astSetFitsS( result, "PIXTYPE", "HEALPIX", "HEALPix magic code", 1 );
+ astSetFitsS( result, "ORDERING", "NUNIQ", "NUNIQ coding method", 1 );
+ astSetFitsS( result, "COORDSYS", "C", "ICRS coordinates", 1 );
+ astSetFitsI( result, "MOCORDER", maxorder, "MOC resolution (best order)", 1 );
+
+/* If an error occurred, annul the returned FitsChan. */
+ if ( !astOK ) result = astAnnul( result );
+
+/* Return the result. */
+ return result;
+}
+
+static double GetMocArea( AstMoc *this, int *status ){
+/*
+* Name:
+* GetMocArea
+
+* Purpose:
+* Get the value of the MocArea attribute.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* double GetMocArea( AstMoc *this, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function returns the value of the MocArea attribute.
+
+* Parameters:
+* this
+* Pointer to the Moc.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The MocArea value.
+
+*/
+
+/* Local Variables; */
+ double res;
+ int irange;
+ int64_t *pr;
+ int64_t count;
+
+/* Check the global error status. */
+ if ( !astOK ) return 0.0;
+
+/* If the area of the moc has not yet been calculated, calculate it now. */
+ if( this->mocarea == AST__BAD ) {
+
+/* All cells in a MOC have equal area, so first Count the number of cells
+ in the Moc. These are all at HEALPix order given by the MaxOrder
+ attribute. The "this->range" array contains the lower and upper bounds
+ of a set of contiguous ranges in nested index at order MaxOrder. */
+ count = 0;
+ pr = this->range;
+ for( irange = 0; irange < this->nrange; irange++, pr += 2 ) { \
+
+/* Increment count by the length of the current range of nested index. */
+ count += ( pr[ 1 ] - pr[ 0 ] + 1 );
+ }
+
+/* Get the mean resolution of the MOC, in arc-mins. This is the square root
+ of the cell area. */
+ res = OrderToRes( astGetMaxOrder( this ) )/60.0;
+
+/* Get the total area. */
+ this->mocarea = res*res*count;
+ }
+
+/* Return the MocArea value. */
+ return this->mocarea;
+}
+
+static int GetMocLength( AstMoc *this, int *status ){
+/*
+* Name:
+* GetMocLength
+
+* Purpose:
+* Get the value of the MocLength attribute.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* int GetMocLength( AstMoc *this, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function returns the value of the MocLength attribute.
+
+* Parameters:
+* this
+* Pointer to the Moc.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The Moclength value.
+
+*/
+
+
+/* Check the global error status. */
+ if ( !astOK ) return 0;
+
+/* Ensure the the normalised form of the MOC is available. */
+ GetNorm( this, "astGetMocLength", status );
+
+/* Return the MocLength value. */
+ return this->moclength;
+}
+
+static void GetMocString( AstMoc *this, int json, size_t mxsize, char *string,
+ size_t *size, int *status ){
+/*
+*++
+* Name:
+c astGetMocString
+f AST_GETMOCSTRING
+
+* Purpose:
+* Get the JSON or string-encoded representation of a Moc
+
+* Type:
+* Public function.
+
+* Synopsis:
+c #include "moc.h"
+c void astGetMocString( AstMoc *this, int json, size_t mxsize,
+c char *string, size_t *size, int *status )
+f CALL AST_GETMOCSTRING( THIS, JSON, MXSIZE, STRING, SIZE, STATUS )
+
+* Class Membership:
+* Moc method.
+
+* Description:
+* This function stores the JSON or string-encoded representation of
+* the supplied Moc in the supplied string buffer.
+
+* Parameters:
+c this
+f THIS = INTEGER (Given)
+* Pointer to the Moc.
+c json
+f JSON = LOGICAL (Given)
+c If non-zero,
+f If .TRUE.,
+* the Moc is encoded using JSON serialisation. Otherwise it is
+* encoded using string-serialisation.
+c mxsize
+f MXSIZE = INTEGER (Given)
+* The length of the supplied string buffer in bytes. An error will
+* be reported if this value is smaller than the number required to
+* describe the Moc. However, if zero is supplied, the buffer will
+* be ignored - no string will be returned but the required size of
+* the buffer will still be returned in
+c 'size'.
+f SIZE.
+c string
+f STRING( * ) = BYTE (Returned)
+c Pointer to the
+f The
+* area of memory in which to return the JSON or string-encoded
+* representation of the Moc. This area is assumed to contain at least
+c 'mxsize' bytes. Only used if 'mxsize' is greater than zero.
+f MXSIZE bytes. Only used if MXSIZE is greater than zero.
+c Note, the string is not null-terminated.
+c size
+f SIZE = INTEGER*8 (Returned)
+* Returned holding the number of bytes needed to store the complete
+* JSON or string-encoded representation of the Moc.
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+*--
+*/
+
+/* Local Variables: */
+ SinkData data2;
+ void *data;
+ void (*sink)( void *, size_t, const char *, int * );
+ size_t buflen;
+
+/* Initialise */
+ *size = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* If a buffer size of zero has been supplied, use a sink function that
+ just accumulates the length of the strings. */
+ if( mxsize == 0 ) {
+ data = size;
+ sink = Sink1;
+
+/* Otherwise use a sink function that appends the strings to the supplied
+ buffer. */
+ } else {
+ data2.string = string;
+ data2.mxsize = mxsize;
+ data = &data2;
+ sink = Sink2;
+ }
+
+/* Do the work. The sink function is called to write out the buffer each
+ time the buffer is filled. */
+ buflen = ( mxsize > 0 ) ? mxsize : 80;
+ astGetMocText( this, json, buflen, sink, data, "astGetMocString" );
+
+/* Return the used size, if it is not already there. */
+ if( mxsize > 0 ) *size = mxsize - data2.mxsize;
+}
+
+void astGetMocText_( AstMoc *this, int json, size_t buflen,
+ void (*sink)( void *, size_t, const char *, int * ),
+ void *data, const char *method, int *status ){
+/*
+*+
+* Name:
+* astGetMocText
+
+* Purpose:
+* Gets a JSON or string-encoded representation of the supplied Moc.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "moc.h"
+* void astGetMocText( AstMoc *this, int json, size_t buflen,
+* void (*sink)( void *, size_t, const char *, int * ),
+* void *data, const char *method, int *status )
+
+* Class Membership:
+* Moc method.
+
+* Description:
+* This function converts the supplied Moc to a text string using the
+* JSON or string serialisation described in the MOC recommendation.
+
+* Parameters:
+* this
+* Pointer to the Moc.
+* json
+* If non-zero, the Moc is encoded using JSON serialisation. Otherwise
+* it is encoded using string-serialisation.
+* buflen
+* The maximum number of characters sent to the sink function in each
+* call.
+* sink
+* A function to call to write out each section of the MOC's JSON or
+* string representation. It should have the following synopsis:
+*
+* void sink( void *data, size_t bufsize, const char *buf, int *status )
+*
+* The sink function should write out the first "bufsize" bytes
+* from "buf" to the appropriate external destination. The "data"
+* pointer can be used for any purpose. The text supplied to the
+* sink function in the buffer will not be null terminated. A NULL
+* pointer may be supplied, in which case the text will be written
+* out to standard output.
+* data
+* A pointer to an arbitrary structure to be passed to the sink
+* function. Can be NULL.
+* method
+* Method name to include in error messages.
+*-
+*/
+
+/* Macro to append a token to the buffer, flushing the buffer using the
+ sink function if the buffer fills up. */
+#define TOKEN_WRITE \
+ ptok = token; \
+ while( nc > nleft ) { \
+ if( nleft ) memcpy( pwrite, ptok, nleft ); \
+ if( sink ) { \
+ (*sink)( data, buflen, buf, status ); \
+ } else { \
+ printf( "%.*s", (int) buflen, buf ); \
+ } \
+ ptok += nleft; \
+ nc -= nleft; \
+ nleft = buflen; \
+ pwrite = buf; \
+ } \
+ if( nc > 0 ) { \
+ memcpy( pwrite, ptok, nc ); \
+ pwrite += nc; \
+ nleft -= nc; \
+ }
+
+/* Local Variables: */
+ char *buf;
+ char *ptok;
+ char *pwrite;
+ char token[ 30 ];
+ int first;
+ int icell;
+ int maxorder;
+ int moclen;
+ int neworder;
+ int order;
+ int64_t npix;
+ int64_t npix_start;
+ int64_t npix_prev;
+ size_t nc;
+ size_t nleft;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Ensure we have the normalised form available in the Moc structure. */
+ GetNorm( this, method, status );
+
+/* Allocate a suitable buffer */
+ buf = astMalloc( buflen );
+
+/* Initialise a pointer into the buffer at which to store the next
+ character, and the number of elements currently left in the buffer. */
+ pwrite = buf;
+ nleft = buflen;
+
+/* Loop over all cells in the Moc. */
+ first = 1;
+ order = -1;
+ npix_start = 0;
+ npix_prev = 0;
+ moclen = astGetMocLength( this );
+ for( icell = 0; icell < moclen; icell++ ){
+
+/* Decode the nuniq value to get the order and npix, using a fast log2
+ function. */
+ if( this->inorm ) {
+ neworder = log2_32( (this->inorm)[ icell ] / 4 ) / 2;
+ npix = (this->inorm)[ icell ] - ( 1 << (2 + 2*(neworder)) );
+ } else {
+ neworder = log2_64( (this->knorm)[ icell ] / 4 ) / 2;
+ npix = (this->knorm)[ icell ] - ( 1L << (2 + 2*(neworder)) );
+ }
+
+/* First do JSON serialisatioon... */
+ if( json ) {
+
+/* If the order has changed, complete any previous pixlist, and start a
+ new one. */
+ if( neworder != order){
+ order = neworder;
+ nc = sprintf( token, first?"{\"%d\":[%zu":"],\"%d\":[%zu",
+ order, npix );
+ first = 0;
+ TOKEN_WRITE;
+
+/* If the order has not changed, just append the new npix value to the
+ existing pixlist. */
+ } else {
+ nc = sprintf( token, ",%zu", npix );
+ TOKEN_WRITE;
+ }
+
+/* Now do string serialisation... */
+ } else {
+
+/* If the order has changed, finish any previous pixlist, record the new
+ order then append " <order>/<npix>" to the sink. */
+ if( neworder != order){
+ if( npix_start < npix_prev ) {
+ nc = sprintf( token, "-%zu", npix_prev );
+ TOKEN_WRITE;
+ }
+ order = neworder;
+ npix_start = npix;
+ nc = sprintf( token, first?"%d/%zu":" %d/%zu", order, npix );
+ first = 0;
+ TOKEN_WRITE;
+
+/* If we have reached the first non-contiguous npix value, write out the
+ end of the previous range. */
+ } else if( npix > npix_prev + 1 ) {
+ if( npix_start < npix_prev ) {
+ nc = sprintf( token, "-%zu", npix_prev );
+ TOKEN_WRITE;
+ }
+
+/* Then write the (potential) start of the new range. */
+ nc = sprintf( token, ",%zu", npix );
+ TOKEN_WRITE;
+ npix_start = npix;
+ }
+ npix_prev = npix;
+ }
+ }
+
+/* Terminate the last pixlist. */
+ if( json ) {
+ nc = sprintf( token, "]" );
+ TOKEN_WRITE;
+ } else if( npix_start < npix ) {
+ nc = sprintf( token, "-%zu", npix );
+ TOKEN_WRITE;
+ }
+
+/* If the Moc's maximum order has not yet been reached, append it
+ explicity. */
+ maxorder = astGetMaxOrder( this );
+ if( order < maxorder ) {
+ if( json ) {
+ nc = sprintf( token, ",\"%d\":[]", maxorder );
+ } else {
+ nc = sprintf( token, " %d/", maxorder );
+ }
+ TOKEN_WRITE;
+ }
+
+/* Terminate the complete JSON string. */
+ if( json ) {
+ nc = sprintf( token, "}" );
+ TOKEN_WRITE;
+ }
+
+/* Flush anything remaining in the buffer. */
+ if( pwrite > buf ) {
+ if( sink ) {
+ (*sink)( data, pwrite - buf, buf, status );
+ } else {
+ printf( "%.*s", (int)( pwrite - buf ), buf );
+ }
+ }
+
+/* Free the buffer. */
+ buf = astFree( buf );
+}
+
+#undef TOKEN_WRITE
+
+static int GetMocType( AstMoc *this, int *status ){
+/*
+* Name:
+* GetMocType
+
+* Purpose:
+* Get the value of the MocType attribute.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* int GetMocType( AstMoc *this, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function returns the value of the MocType attribute.
+
+* Parameters:
+* this
+* Pointer to the Moc.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The MocType value - 4 or 8.
+
+*/
+
+
+/* Check the global error status. */
+ if ( !astOK ) return 4;
+
+/* Ensure the the normalised form of the MOC is available. */
+ GetNorm( this, "astGetMocType", status );
+
+/* Return 4 or 8, depending on whether "iorm" or "knorm" is used to store
+ the normalised data within the moc structure. */
+ return ( this->inorm == NULL ) ? 8 : 4;
+}
+
+
+static void GetNorm( AstMoc *this, const char *method, int *status ){
+/*
+* Name:
+* GetNorm
+
+* Purpose:
+* Get the normalised form of a Moc.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void GetNorm( AstMoc *this, const char *method, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function ensures that a normalised form of the Moc is
+* available within the Moc structure. It uses the "unmap"
+* algorithm described in the MOC recommendation.
+
+* Parameters:
+* this
+* Pointer to the Moc.
+* method
+* The name of the calling method, for error messages.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ int maxorder;
+ int order;
+ int irange;
+ int moclen;
+ size_t nnew;
+ size_t nrange;
+ size_t shift;
+ int64_t *newranges;
+ int64_t *pr;
+ int64_t *ranges;
+ int64_t m1;
+ int64_t m2;
+ int64_t max;
+ int64_t min;
+ int64_t npix;
+ int64_t offset;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Do nothing if the normalised form is already available. */
+ if( !this->inorm && !this->knorm ) {
+ nrange = this->nrange;
+ ranges = astStore( NULL, this->range, 2*nrange*sizeof(*ranges));
+ moclen = 0;
+ maxorder = astGetMaxOrder( this );
+ if( astOK ) {
+
+/* Define a macro to do the work for a generic data type. */
+#define UNMAP \
+ norm = NULL; \
+ for( order = 0; order <= maxorder && nrange > 0; order++ ) { \
+ nuniq_offset = ( 1L << 2*( order + 1 ) ); \
+\
+ shift = 2*( maxorder - order ); \
+ offset = ( 1L << shift ) - 1; \
+\
+ newranges = NULL; \
+ nnew = 0; \
+ pr = ranges; \
+ for( irange = 0; irange < nrange; irange++ ) { \
+ min = *(pr++); \
+ max = *(pr++); \
+ m1 = ( ( min + offset ) >> shift ); \
+ m2 = ( ( max + 1 ) >> shift ); \
+ if( m2 > m1 ) { \
+ if( INT_MAX - moclen < m2 - m1 + 2 ) { \
+ astError( AST__BGMOC, "%s(%s): The normalised MOC " \
+ "has too many elements.", status, method, \
+ astGetClass( this ) ); \
+ break; \
+ } else { \
+ for( npix = m1; npix < m2; npix++ ) { \
+ norm = astGrow( norm, moclen+1, sizeof(*norm) ); \
+ if( astOK ) { \
+ norm[ moclen++ ] = nuniq_offset + npix; \
+ } \
+ } \
+ } \
+ m1 = ( m1 << shift ); \
+ m2 = ( m2 << shift ) - 1; \
+ if( m1 <= min ) { \
+ if( m2 >= max ) { \
+ /* Nothing to do if whole range has been removed */ \
+ } else { \
+ newranges = astGrow( newranges, nnew+1, 2*sizeof(*newranges) ); \
+ if( astOK ) { \
+ newranges[ 2*nnew ] = m2 + 1; \
+ newranges[ 2*nnew + 1 ] = max; \
+ nnew++; \
+ } \
+ } \
+ } else { \
+ if( m2 >= max ) { \
+ newranges = astGrow( newranges, nnew+1, 2*sizeof(*newranges) ); \
+ if( astOK ) { \
+ newranges[ 2*nnew ] = min; \
+ newranges[ 2*nnew + 1 ] = m1 - 1; \
+ nnew++; \
+ } \
+ } else { \
+ newranges = astGrow( newranges, nnew+2, 2*sizeof(*newranges) ); \
+ if( astOK ) { \
+ newranges[ 2*nnew ] = min; \
+ newranges[ 2*nnew + 1 ] = m1 - 1; \
+ newranges[ 2*nnew + 2 ] = m2 + 1; \
+ newranges[ 2*nnew + 3 ] = max; \
+ nnew += 2; \
+ } \
+ } \
+ } \
+ } else { \
+ newranges = astGrow( newranges, nnew+1, 2*sizeof(*newranges) ); \
+ if( astOK ) { \
+ newranges[ 2*nnew ] = min; \
+ newranges[ 2*nnew + 1 ] = max; \
+ nnew++; \
+ } \
+ } \
+ } \
+\
+ ranges = astFree( ranges ); \
+ ranges = newranges; \
+ nrange = nnew; \
+ } \
+\
+ ranges = astFree( ranges ); \
+ this->moclength = moclen;
+
+/* Use the above macro to do the work for 4 byte identifiers... */
+ if( maxorder < 14 ) {
+ int *norm, nuniq_offset;
+ UNMAP
+ this->inorm = norm;
+ this->knorm = astFree( this->knorm );
+
+/* Use the above macro to do the work for 8 byte identifiers... */
+ } else {
+ int64_t *norm, nuniq_offset;
+ UNMAP
+ this->knorm = norm;
+ this->inorm = astFree( this->inorm );
+ }
+ }
+ }
+
+#undef UNMAP
+
+}
+
+static double GetPixelArea( AstFrameSet *wcs, const int *dims, int *status ){
+/*
+* Name:
+* GetPixelArea
+
+* Purpose:
+* Get the area of a pixel in the centre of a 2D array.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* double GetPixelArea( AstFrameSet *wcs, const int *dims, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function returns the area of a pixel at the centre of the
+* supplied 2-dimensional array, in square arc-seconds.
+
+* Parameters:
+* wcs
+* Pointer to the WCS FrameSet describing the 2D array. The base
+* Frame is assumed to describe grid coordinates. The current Frame
+* should be a SkyFrame.
+* dims
+* Pointer to an array holding the dimensions of the array, in
+* pixels.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* The pixel area, in square arc-seconds.
+
+*/
+
+/* Local Variables: */
+ double a[ 3 ];
+ double b[ 3 ];
+ double d1;
+ double d2;
+ double p1[ 2];
+ double p2[ 2];
+ double result;
+ double x[ 3 ];
+ double y[ 3 ];
+
+/* Initialise. */
+ result = AST__BAD;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Transform the central grid coords, plus the grid coords at positions
+ offset one pixel along each grid axis, into sky coords. */
+ x[ 0 ] = 0.5*( dims[ 0 ] + 1.0 );
+ x[ 1 ] = x[ 0 ] + 1.0;
+ x[ 2 ] = x[ 0 ];
+ y[ 0 ] = 0.5*( dims[ 1 ] + 1.0 );
+ y[ 1 ] = y[ 0 ];
+ y[ 2 ] = y[ 0 ] + 1.0;
+ astTran2( wcs, 3, x, y, 1, a, b );
+
+/* Find the arc-distances, in radians, from the centre to the other two
+ positions. */
+ p1[ 0 ] = a[ 0 ];
+ p1[ 1 ] = b[ 0 ];
+ p2[ 0 ] = a[ 1 ];
+ p2[ 1 ] = b[ 1 ];
+ d1 = astDistance( wcs, p1, p2 );
+ p2[ 0 ] = a[ 2 ];
+ p2[ 1 ] = b[ 2 ];
+ d2 = astDistance( wcs, p1, p2 );
+
+/* Calculate the area, converting to square arc-seconds. */
+ if( d1 != AST__BAD && d2 != AST__BAD ) {
+ d1 *= AST__DR2D*3600.0;
+ d2 *= AST__DR2D*3600.0;
+ result = d1*d2;
+ }
+
+/* Return the pixel size. */
+ return result;
+}
+
+/*
+* Name:
+* GetSelectionBounds
+
+* Purpose:
+* Get the grid bounds of a box containing all selected pixels.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* int GetSelectionBounds<Oper><X>( <Xtype> value, const <Xtype> array[],
+* const int dims[ 2 ], int *lx, int *hx,
+* int *ly, int *hy, int *status );
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* The bounds of a box in grid coordinates that contain all selected
+* pixels is returned.
+
+* Parameters:
+* value
+* The data value defining selected pixels.
+* array
+* The data array.
+* dims
+* The number of pixels along each pixel axis.
+* lx
+* Returned holding the lowest index on the first grid axis
+* of the selected pixels.
+* hx
+* Returned holding the highest index on the first grid axis
+* of the selected pixels.
+* ly
+* Returned holding the lowest index on the second grid axis
+* of the selected pixels.
+* hy
+* Returned holding the highest index on the second grid axis
+* of the selected pixels.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A flag indicating if one or more selected pixels were found. Zero
+* is returned if no selected pixels were found.
+
+* Notes:
+* - <Oper> must be one of LT, LE, EQ, NE, GE, GT.
+
+*/
+
+/* Define a macro to implement the function for a specific data
+ type and operation. */
+#define MAKE_GETSELECTIONBOUNDS(X,Xtype,Oper,OperI) \
+static int GetSelectionBounds##Oper##X( Xtype value, const Xtype array[], \
+ const int dims[ 2 ], int *lx, int *hx, \
+ int *ly, int *hy, int *status ){ \
+\
+/* Local Variables: */ \
+ const Xtype *pv; /* Pointer to next data value to test */ \
+ int ix; /* Pixel column */ \
+ int iy; /* Pixel row */ \
+\
+/* Initialise the bounding box. */ \
+ *lx = INT_MAX; \
+ *hx = -INT_MAX; \
+ *ly = INT_MAX; \
+ *hy = -INT_MAX; \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return 0; \
+\
+/* Loop round all pixels in the array. */ \
+ pv = array; \
+ for( iy = 1; iy <= dims[ 1 ]; iy++ ) { \
+ for( ix = 1; ix <= dims[ 0 ]; ix++,pv++ ) { \
+\
+/* Test the pixel value, extending the bounding box if the test is \
+ passed. This relies on the compiler optimisation to remove the \
+ "if" statements for all but the operation appropriate to the \
+ function being defined. */ \
+ if( OperI == AST__LT ) { \
+ if( *pv < value ) EXTEND_BOX; \
+ } else if( OperI == AST__LE ) { \
+ if( *pv <= value ) EXTEND_BOX; \
+ } else if( OperI == AST__EQ ) { \
+ if( *pv == value ) EXTEND_BOX; \
+ } else if( OperI == AST__NE ) { \
+ if( *pv != value ) EXTEND_BOX; \
+ } else if( OperI == AST__GE ) { \
+ if( *pv >= value ) EXTEND_BOX; \
+ } else if( OperI == AST__GT ) { \
+ if( *pv > value ) EXTEND_BOX; \
+ } \
+ } \
+ } \
+\
+/* Return the appropriate flag value. */ \
+ return ( *lx <= *hx ) && ( *ly <= *hy ); \
+}
+
+
+
+
+/* Define a macro to extend the bound box. */
+#define EXTEND_BOX { \
+ if( ix < *lx ) *lx = ix; \
+ if( ix > *hx ) *hx = ix; \
+ if( iy < *ly ) *ly = iy; \
+ if( iy > *hy ) *hy = iy; \
+}
+
+/* Define a macro that uses the above macros to to create implementations
+ of GetSelectionBounds for all operations. */
+#define MAKEALL_GETSELECTIONBOUNDS(X,Xtype) \
+MAKE_GETSELECTIONBOUNDS(X,Xtype,LT,AST__LT) \
+MAKE_GETSELECTIONBOUNDS(X,Xtype,LE,AST__LE) \
+MAKE_GETSELECTIONBOUNDS(X,Xtype,EQ,AST__EQ) \
+MAKE_GETSELECTIONBOUNDS(X,Xtype,GE,AST__GE) \
+MAKE_GETSELECTIONBOUNDS(X,Xtype,GT,AST__GT) \
+MAKE_GETSELECTIONBOUNDS(X,Xtype,NE,AST__NE)
+
+/* Expand the above macro to generate a function for each required
+ data type and operation. */
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+MAKEALL_GETSELECTIONBOUNDS(LD,long double)
+#endif
+MAKEALL_GETSELECTIONBOUNDS(D,double)
+MAKEALL_GETSELECTIONBOUNDS(L,long int)
+MAKEALL_GETSELECTIONBOUNDS(UL,unsigned long int)
+MAKEALL_GETSELECTIONBOUNDS(I,int)
+MAKEALL_GETSELECTIONBOUNDS(UI,unsigned int)
+MAKEALL_GETSELECTIONBOUNDS(S,short int)
+MAKEALL_GETSELECTIONBOUNDS(US,unsigned short int)
+MAKEALL_GETSELECTIONBOUNDS(B,signed char)
+MAKEALL_GETSELECTIONBOUNDS(UB,unsigned char)
+MAKEALL_GETSELECTIONBOUNDS(F,float)
+
+/* Undefine the macros. */
+#undef MAKE_GETSELECTIONBOUNDS
+#undef MAKEALL_GETSELECTIONBOUNDS
+#undef EXTEND_BOX
+
+
+static void IncorporateCells( AstMoc *this, CellList *clist, int negate,
+ int cmode, const char *method, int *status ){
+/*
+* Name:
+* IncorporateCells
+
+* Purpose:
+* Incorporate a list of HEALPix cells into the given Moc.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void IncorporateCells( AstMoc *this, CellList *clist, int negate,
+* int cmode, const char *method, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function incorporates the supplied list of HEALPix cells into
+* the Moc.
+
+* Parameters:
+* this
+* Pointer to the Moc.
+* clist
+* Structure holding information about the cells to be incorporated
+* in the Moc.
+* negate
+* If zero, the list of HEALPix cells to incorporate into the
+* Moc are those included in "clist". If non-zero, the list of
+* HEALPix cells to incorporate into the Moc are those NOT included
+* in "clist".
+* cmode
+* Indicates how the Moc is to be combined with the incorporated
+* cells. Any of the following values may be supplied:
+* - AST__AND: The modified Moc is the intersection of the original
+* Moc and the cell list.
+* - AST__OR: The modified Moc is the union of the original Moc and
+* the cell list.
+* - AST__XOR: The modified Moc is the exclusive disjunction of the
+* original Moc and the cell list.
+* method:
+* Name of calling method.
+* status
+* The inherited status.
+
+*/
+
+/* Local Variables: */
+ AstPointSet *ps1;
+ AstPointSet *ps2;
+ double **ptr2;
+ double *ptr1[ 2 ];
+ double *px;
+ double *py;
+ int i;
+ int irange;
+ int nold;
+ int order;
+ int shift;
+ int64_t *nested;
+ int64_t *pend;
+ int64_t *pn;
+ int64_t *pr;
+ int64_t ihigh;
+ int64_t ilow;
+
+/* Check inherited status */
+ if( !astOK ) return;
+
+/* Record the original number of ranges of nested indices in the Moc. */
+ nold = this->nrange;
+
+/* Loop over each order present in the CellList structure. */
+ for( order = 0; order <= clist->maxorder; order++ ) {
+ if( clist->len[ order ] > 0 ) {
+ nested = astMalloc( clist->len[ order ]*sizeof( *nested ) );
+
+/* Transform the grid coords at the current order from their own
+ HPX-like projection to the HPX12 projection. */
+ ps1 = astPointSet( clist->len[ order ], 2, "", status );
+ ptr1[ 0 ] = clist->ix[ order ];
+ ptr1[ 1 ] = clist->iy[ order ];
+ astSetPoints( ps1, ptr1 );
+ ps2 = astTransform( clist->maps[ order ], ps1, 1, NULL );
+ ptr2 = astGetPoints( ps2 );
+ if( astOK ) {
+
+/* Round each one to the nearest pixel centre and convert to a nested
+ HEALPix index at the current order. */
+ pn = nested;
+ px = ptr2[ 0 ];
+ py = ptr2[ 1 ];
+ for( i = 0; i < clist->len[ order ]; i++ ) {
+ if( *px != AST__BAD && *py != AST__BAD ) {
+ *(pn++) = XyToNested( order, (int)( *(px++) + 0.5 ),
+ (int)( *(py++) + 0.5 ) );
+ } else if( astOK ) {
+ astError( AST__INTER, "%s(%s): Bad HPX12 grid coord "
+ "element %d order %d (internal programming "
+ "error).", status, method, astGetClass(this ),
+ i, order );
+ break;
+ }
+ }
+ }
+
+/* Check no errors above. */
+ if( astOK ) {
+
+/* Sort them into ascending order. */
+ qsort( nested, clist->len[ order ], sizeof(*nested),
+ Comp_int64 );
+
+/* Look for ranges within the sorted list of indices. */
+ shift = 2*( clist->maxorder - order );
+ pn = nested;
+ pend = nested + clist->len[ order ];
+ while( pn < pend ) {
+ ilow = *pn;
+ ihigh = ilow;
+ while( ++pn < pend && *pn == ihigh + 1 ) ihigh++;
+
+/* Process index range ilow->ihigh. Get the equivalent range at order
+ "maxorder", and append to the list of ranges in the Moc. */
+ irange = this->nrange++;
+ this->range = astGrow( this->range, this->nrange, 2*sizeof(*(this->range)) );
+ if( astOK ) {
+ pr = this->range + 2*irange;
+ pr[ 0 ] = ( ilow << shift );
+ pr[ 1 ] = ( ( ihigh + 1 ) << shift ) - 1;
+ } else {
+ break;
+ }
+ }
+ }
+
+/* Free resources. */
+ ps1 = astAnnul( ps1 );
+ ps2 = astAnnul( ps2 );
+ nested = astFree( nested );
+ }
+ }
+
+/* Normalise the Moc. */
+ astMocNorm( this, negate, cmode, nold, clist->maxorder, method );
+}
+
+void astInitMocVtab_( AstMocVtab *vtab, const char *name, int *status ) {
+/*
+*+
+* Name:
+* astInitMocVtab
+
+* Purpose:
+* Initialise a virtual function table for a Moc.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "moc.h"
+* void astInitMocVtab( AstMocVtab *vtab, const char *name )
+
+* Class Membership:
+* Moc vtab initialiser.
+
+* Description:
+* This function initialises the component of a virtual function
+* table which is used by the Moc 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 */
+ 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 astIsAMoc) 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->AddRegion = AddRegion;
+ vtab->AddMocData = AddMocData;
+ vtab->AddMocString = AddMocString;
+ vtab->GetMocString = GetMocString;
+ vtab->AddCell = AddCell;
+ vtab->GetCell = GetCell;
+ vtab->TestCell = TestCell;
+
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+ vtab->AddPixelMaskLD = AddPixelMaskLD;
+#endif
+ vtab->AddPixelMaskB = AddPixelMaskB;
+ vtab->AddPixelMaskD = AddPixelMaskD;
+ vtab->AddPixelMaskF = AddPixelMaskF;
+ vtab->AddPixelMaskI = AddPixelMaskI;
+ vtab->AddPixelMaskL = AddPixelMaskL;
+ vtab->AddPixelMaskS = AddPixelMaskS;
+ vtab->AddPixelMaskUB = AddPixelMaskUB;
+ vtab->AddPixelMaskUI = AddPixelMaskUI;
+ vtab->AddPixelMaskUL = AddPixelMaskUL;
+ vtab->AddPixelMaskUS = AddPixelMaskUS;
+
+ vtab->GetMocArea = GetMocArea;
+ vtab->GetMocData = GetMocData;
+ vtab->GetMocType = GetMocType;
+ vtab->GetMocLength = GetMocLength;
+ vtab->GetMocHeader = GetMocHeader;
+
+ vtab->ClearMaxOrder = ClearMaxOrder;
+ vtab->GetMaxOrder = GetMaxOrder;
+ vtab->SetMaxOrder = SetMaxOrder;
+ vtab->TestMaxOrder = TestMaxOrder;
+
+ vtab->ClearMinOrder = ClearMinOrder;
+ vtab->GetMinOrder = GetMinOrder;
+ vtab->SetMinOrder = SetMinOrder;
+ vtab->TestMinOrder = TestMinOrder;
+
+/* 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;
+
+ 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;
+
+ parent_transform = mapping->Transform;
+ mapping->Transform = Transform;
+
+ region->RegBaseBox = RegBaseBox;
+ region->RegBaseMesh = RegBaseMesh;
+ region->RegPins = RegPins;
+ region->GetDefUnc = GetDefUnc;
+ region->RegTrace = RegTrace;
+
+/* Declare the copy constructor, destructor and class dump
+ functions. */
+ astSetDump( vtab, Dump, "Moc", "IVOA Multi-Order Coverage map" );
+ astSetDelete( vtab, Delete );
+ astSetCopy( vtab, Copy );
+
+/* 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 log2_64( int64_t svalue ) {
+/*
+* Name:
+* log2_64
+
+* Purpose:
+* Fast log2 function for signed 64 bit integers.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* int log2_64( int64_t svalue )
+
+* Description:
+* This function returns the log base 2 of the supplied 64 bit integer
+* value. By Desmond Hume.
+
+*/
+ uint64_t value = svalue;
+ value |= value >> 1;
+ value |= value >> 2;
+ value |= value >> 4;
+ value |= value >> 8;
+ value |= value >> 16;
+ value |= value >> 32;
+ return Tab64[((uint64_t)((value - (value >> 1))*0x07EDD5E59A4E28C2)) >> 58];
+}
+
+static int log2_32( int svalue ) {
+/*
+* Name:
+* log2_32
+
+* Purpose:
+* Fast log2 forunction for 32 bit integers.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* int log2_32( int svalue )
+
+* Description:
+* This function returns the log base 2 of the supplied 32 bit integer
+* value. By Desmond Hume.
+
+*/
+ unsigned int value = svalue;
+ value |= value >> 1;
+ value |= value >> 2;
+ value |= value >> 4;
+ value |= value >> 8;
+ value |= value >> 16;
+ return Tab32[(unsigned int)(value*0x07C4ACDD) >> 27];
+}
+
+static Cell *MakeCell( int ix, int iy, int order, Cell **foot, int *status ){
+/*
+* Name:
+* MakeCell
+
+* Purpose:
+* Create a structure holding a descripion of a single HEALPix cell.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* Cell *MakeCell( int ix, int iy, int order, Cell **foot, int *status )
+
+* Description:
+* This function creates a Cell structure, initialising its contents
+* to null values, and then stores the supplied grid indices in the
+* Cell and adds it to the end of the linked list of Cell structures.
+
+* Parameters:
+* ix
+* The one-based integer grid X index for the HPX12 pixel to be
+* described by the Cell.
+* iy
+* The one-based integer grid Y index for the HPX12 pixel to be
+* described by the Cell.
+* order
+* The HEALPix order at which the pixel is defined.
+* foot
+* Address of the last existing Cell pointer currently on the
+* linked list of Cell pointers. It is updated on exit to point to
+* the new Cell.
+* status
+* Pointer to the inherited status.
+
+* Returned Value:
+* Pointer to the new Cell. It should be freed using astFree when no
+* longer needed.
+
+*/
+
+/* Local Variables: */
+ Cell *new;
+
+/* Check inherited status */
+ if( !astOK ) return NULL;
+
+/* Allocate new structure, initialising it to all zeros to nullify all
+ fields. */
+ new = astCalloc( 1, sizeof( *new ) );
+ if( new ) {
+
+/* Store supplied values in the new Cell. */
+ new->ix = ix;
+ new->iy = iy;
+
+/* Append it to the end of the chain of Cells at the same order. */
+ new->prev = foot[ order ];
+ foot[ order ] = new;
+ }
+
+ return new;
+}
+
+static void MakeCorners( AstMoc *this, int order, Cell *cell_foot,
+ Corner **corner_foot, int sort, int *status ){
+/*
+* Name:
+* MakeCorners
+
+* Purpose:
+* Incorporate cell information into a linked list of structures
+* holding information about corners.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void MakeCorners( AstMoc *this, int order, Cell *cell_foot,
+* Corner **corner_foot, int sort, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function finds the four corner positions of each Cell
+* in the chain specified by "cell_foot", and for each one registers
+* the cell with the corresponding Corner structure. New Corner
+* structures are created for any corner positions that do not
+* already have a corresponding structure, and appended to the
+* end of the supplied linked list of Corner structures. Co-incident
+* Corner structures are then merged.
+
+* Parameters:
+* this
+* The Moc.
+* order
+* The HEALPix order of the supplied cells.
+* cell_foot
+* Pointer to the last Cell structure in a chain of linked structures
+* holding information about the cells for which corners are to be
+* created.
+* corner_foot
+* Pointer to a Corner pointer, in which to return a pointer to the
+* last Corner created by this function. This is the last Corner in
+* a chain of linked Corner structures holding information about
+* the corners at the corresponding order. All RA values are in the
+* range [0,2.PI[.
+* sort
+* If non-zero, the created chain of Corner structures is sorted,
+* first by Dec then by RA.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables; */
+ AstMapping *map;
+ AstPointSet *ps1;
+ AstPointSet *ps2;
+ Cell **pcell;
+ Cell *cell;
+ Corner *root;
+ Corner **index;
+ Corner *foot;
+ Corner *prev;
+ Corner *next;
+ Corner *corner;
+ Corner **pi;
+ astDECLARE_GLOBALS
+ double **ptr2;
+ double **ptr1;
+ double *p1;
+ double *p0;
+ double dectol;
+ double ratol;
+ int cross;
+ int icell;
+ int icorner;
+ int lim;
+ int ncell;
+ int ncorner;
+ int nppf;
+ int s1;
+ int s2;
+ int sxy;
+
+/* Check inherited status */
+ if( !astOK ) return;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(this);
+
+/* Initialise */
+ foot = NULL;
+
+/* Count the number of Cells in the supplied chain of cells. Create four
+ new empty Corner structures for each one, and append them to the end
+ of the chain of Corners. */
+ ncell = 0;
+ cell = cell_foot;
+ while( cell ) {
+ ncell++;
+ cell = cell->prev;
+ for( icorner = 0; icorner < 4; icorner++ ) {
+ corner = astCalloc( 1, sizeof( *corner ) );
+ corner->prev = foot;
+ foot = corner;
+ }
+ }
+
+/* The number of new corners - four per cell. */
+ if( ncell > INT_MAX/4 - 2 ) {
+ astError( AST__BGMOC, "astRegBaseMesh(%s): The normalised MOC has "
+ "too many elements.", status, astGetClass( this ) );
+ } else {
+ ncorner = 4*ncell;
+ }
+
+/* Get a pointset to hold the grid coords at the four corners of each
+ cell. */
+ ps1 = astPointSet( ncorner, 2, " ", status );
+ ptr1 = astGetPoints( ps1 );
+
+/* Get a pointset to hold the corresponding sky coords at the four
+ corners of each cell. */
+ ps2 = astPointSet( ncorner, 2, " ", status );
+ ptr2 = astGetPoints( ps2 );
+
+/* Check all pointers are safe to use. */
+ if( astOK ) {
+
+/* Get the sums of ix and iy that correspond to the RA=0h/24h line. */
+ nppf = ( 1 << order );
+ s1 = nppf + 1;
+ s2 = 9*nppf + 1;
+ lim = nppf;
+
+/* Get the grid coords at the corners, within an HPX12 projection at the
+ specified order. Note, to avoid problems at discontinuities, the corner
+ positions are very slighly inside the cell rather than being exactly
+ on the boundary. Also get a flag ("cross") which indicates if any cell
+ falls on the RA=0h/RA=24h discontinuity. */
+ cross = 0;
+ p0 = ptr1[ 0 ];
+ p1 = ptr1[ 1 ];
+ cell = cell_foot;
+ for( icell = 0; icell < ncell; icell++ ) {
+ *(p0++) = cell->ix - HALF;
+ *(p0++) = cell->ix - HALF;
+ *(p0++) = cell->ix + HALF;
+ *(p0++) = cell->ix + HALF;
+ *(p1++) = cell->iy - HALF;
+ *(p1++) = cell->iy + HALF;
+ *(p1++) = cell->iy + HALF;
+ *(p1++) = cell->iy - HALF;
+
+ sxy = cell->ix + cell->iy;
+ if( abs( sxy - s1 ) <= lim || abs( sxy - s2 ) <= lim ) cross = 1;
+
+ cell = cell->prev;
+ }
+
+/* Get a Mapping that goes from ICRS to grid coordinates in an HPX12
+ projection of the whole sky at the specified order. Do not annul
+ the Mapping pointer since it has not been cloned in GetCachedMapping. */
+ map = GetCachedMapping( this, order, "astRegBaseMesh", status );
+
+/* Use the inverse transformation of this mapping to convert the grid
+ coords at the cell corners into sky coords. */
+ (void) astTransform( map, ps1, 0, ps2 );
+
+/* Ensure all RA values are in range [0,2PI[ and store the (RA,Dec) values
+ in the relevant Corner structures. Set up the links between the new
+ Corners and the Cells to which they refer. */
+ p0 = ptr2[ 0 ];
+ p1 = ptr2[ 1 ];
+ cell = cell_foot;
+ corner = foot;
+ for( icell = 0; icell < ncell && astOK; icell++ ){
+
+ for( icorner = 0; icorner < 4; icorner++,p0++,p1++ ){
+ if( *p0 == AST__BAD || *p1 == AST__BAD ) {
+ astError( AST__INTER, "astRegBaseMesh(%s): Bad sky coords "
+ "at corner (internal programming error).", status,
+ astGetClass( this ) );
+ break;
+ }
+
+ while( *p0 >= 2*AST__DPI ) *p0 -= 2*AST__DPI;
+ while( *p0 < 0 ) *p0 += 2*AST__DPI;
+
+ corner->ra = *p0;
+ corner->dec = *p1;
+ corner->cosdec = cos( *p1 );
+ corner->ncell = 1;
+ corner->cells[ 0 ] = cell;
+
+ if( icorner == 0 ) {
+ cell->bl = corner;
+ } else if( icorner == 1 ) {
+ cell->tl = corner;
+ } else if( icorner == 2 ) {
+ cell->tr = corner;
+ } else {
+ cell->br = corner;
+ }
+ corner = corner->prev;
+ }
+
+ cell = cell->prev;
+ }
+ }
+
+/* Free resources. */
+ ps1 = astAnnul( ps1 );
+ ps2 = astAnnul( ps2 );
+
+/* If any cells cross the RA=0/24 line, we need to sort and merge adjacent
+ corners twice, once centred on RA=12 and once centred on RA=0. Otherwise
+ adjacent corners on either side of the RA=0/24 line will not be merged. */
+ for( Comp_Corner_Loop = 0; Comp_Corner_Loop <= cross; Comp_Corner_Loop++ ) {
+
+/* We now sort the corners, using DEC as the primary key and Ra as the
+ secondary key (Dec is primary because the HEALPix cells form a number
+ of rings on the sky, each at constant Dec). First create an index of
+ the Corners in the linked list. This index is simply an array of
+ pointers to the Corners in their initial positions within the linked
+ list. */
+ index = astMalloc( ncorner*sizeof(*index) );
+ if( astOK ) {
+ pi = index;
+ icorner = 0;
+ corner = foot;
+ while( corner ) {
+ icorner++;
+ *(pi++) = corner;
+ corner = corner->prev;
+ }
+
+/* Sanity check. */
+ if( icorner != ncorner && astOK ) {
+ astError( AST__INTER, "astRegBaseMesh(%s): Expected %d corners "
+ "but found %d (internal programming error).", status,
+ astGetClass(this), ncorner, icorner );
+ }
+
+/* Sort the index. For Comp_Corner_Loop==0 they are sorted around RA=12h.
+ For Comp_Corner_Loop==1 they are sorted around RA=0/24h. Sort them
+ exactly in RA (i.e. zero tolerance in RA) */
+ Comp_Corner_Exact = 1;
+ if( astOK ) qsort( index, ncorner, sizeof(*index), Comp_corner );
+
+/* Merge corners that are co-incident. To begin with, use the first
+ corner as the reference (root) corner and compare each subsequent
+ corner to it. Allow some tolerance in RA. */
+ Comp_Corner_Exact = 0;
+ root = index[ 0 ];
+ pi = index + 1;
+ for( icorner = 1; icorner < ncorner; icorner++,pi++ ) {
+
+/* If the current Corner is co-incident with the root corner, transfer
+ information from the current Corner to the root Corner, and then mark
+ the current Corner as unused by setting its RA bad. */
+ if( Comp_corner( &root, pi ) == 0 ) {
+
+/* Check the merged Corner will not be used by more than four cells. */
+ if( root->ncell + (*pi)->ncell > 4 ) {
+ astError( AST__INTER, "astRegBaseMesh(%s): Corner is used "
+ "more than 4 times (internal programming error).",
+ status, astGetClass( this ) );
+ break;
+
+/* Merge the cells from the current Corner into the root Corner. Update
+ the pointers within each cell to point to the root corner instead of
+ the current corner. */
+ } else {
+ pcell = (*pi)->cells;
+ for( icell = 0; icell < (*pi)->ncell; icell++,pcell++ ) {
+ root->cells[ (root->ncell)++ ] = *pcell;
+
+ if( (*pcell)->bl == *pi ) {
+ (*pcell)->bl = root;
+ } else if( (*pcell)->tl == *pi ) {
+ (*pcell)->tl = root;
+ } else if( (*pcell)->tr == *pi ) {
+ (*pcell)->tr = root;
+ } else if( (*pcell)->br == *pi ) {
+ (*pcell)->br = root;
+ } else {
+ astError( AST__INTER, "astRegBaseMesh(%s): Orphaned "
+ "Corner (internal programming error).", status,
+ astGetClass( this ) );
+ break;
+ }
+ }
+ (*pi)->ra = AST__BAD;
+ }
+
+/* If the current cell is not co-incident with the root cell, use the
+ current cell as the root cell from now on. */
+ } else {
+ root = *pi;
+ }
+ }
+
+/* If required, re-order the Corner structures within the chain so that
+ they follow the same order as the index (i.e. sorted into increasing
+ Dec). */
+ if( sort ) {
+ pi = index + ncorner - 1;
+ foot = *pi;
+ foot->prev = NULL;
+ for( icorner = 1; icorner < ncorner; icorner++ ) {
+ corner = *(--pi);
+ corner->prev = foot;
+ foot = corner;
+ }
+ }
+
+/* Remove corners that have been flagged with a bad RA value, and fix up
+ the links between the corners on either side. */
+ ncorner = 0;
+ next = NULL;
+ corner = foot;
+ while( corner ) {
+ prev = corner->prev;
+
+ if( corner->ra == AST__BAD ) {
+ if( next ) next->prev = prev;
+ if( corner == foot ) foot = prev;
+ corner = astFree( corner );
+ } else {
+ ncorner++;
+ next = corner;
+ }
+
+ corner = prev;
+ }
+ }
+
+/* Free resources. */
+ index = astFree( index );
+ }
+
+/* Indicate if each remaining corner is an interior position (i.e. surrounded
+ by cells on all sides OR is at the corner of at least one interior cell
+ from an earlier order). */
+ corner = foot;
+ while( corner ) {
+
+/* If the corner touches a cell that has been flagged as interior at a
+ previous order, then it must be interior at this order too. */
+ pcell = corner->cells;
+ for( icell = 0; icell < corner->ncell; icell++,pcell++ ) {
+ if( (*pcell)->interior ) {
+ corner->interior = 1;
+ break;
+ }
+ }
+
+/* In general, each corner of a cell is coincident with three other
+ corners from three neighbouring cells. So each corner position occurs
+ four times at an interior position. However, there are 8 corner
+ positions where only three cells meet. These are at the southern and
+ northern corners of the equatorial facets. If the current corner is
+ used three times, we need to see if this is a four-corner position
+ that is missing a corner, or a three-corner position which is not
+ missing a corner (i.e. an interior position). */
+ if( !corner->interior ) {
+ if( corner->ncell == 3 ) {
+ dectol = Comp_Corner_Tol;
+ ratol = dectol/cos(THETAX);
+ if( fabs( corner->ra ) < ratol ||
+ fabs( corner->ra - AST__DPIBY2 ) < ratol ||
+ fabs( corner->ra - AST__DPI ) < ratol ||
+ fabs( corner->ra - 3*AST__DPIBY2 ) < ratol ||
+ fabs( corner->ra - 2*AST__DPI ) < ratol ) {
+ if( fabs( corner->dec - THETAX ) < dectol ||
+ fabs( corner->dec + THETAX ) < dectol ) {
+ corner->interior = 1;
+ }
+ }
+
+/* Other corners are interior if they are used 4 times. */
+ } else if( corner->ncell == 4 ) {
+ corner->interior = 1;
+ }
+ }
+
+
+/* Initialise the distance of the corner around the perimeter to indicate
+ no distance has yet been found. */
+ corner->dist = INT_MAX;
+
+/* Move on to the next corner in the chain. */
+ corner = corner->prev;
+ }
+
+/* Return a pointer to the corner that's now at the foot of the chain. */
+ *corner_foot = foot;
+
+}
+
+static void MergeRanges( AstMoc *this, int start, int *status ){
+/*
+* Name:
+* MergeRanges
+
+* Purpose:
+* Merge contiguous cell ranges in a Moc.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void MergeRanges( AstMoc *this, int start, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function merges any conntiguous ranges of cells within a Moc,
+* starting at a specified range (earlier ranges are left unchanged).
+* The merged ranges are also ordered into increasing lower bound.
+
+* Parameters:
+* this
+* The Moc to use.
+* start
+* The index of the first range to check.
+* status
+* Pointer to the inherited status.
+
+*/
+
+/* Local Variables: */
+ int irange;
+ int nnew;
+ int64_t *pr;
+ int64_t *pw;
+
+/* Check inherited status */
+ if( !astOK ) return;
+
+/* Nothing to do if the Moc is empty or has only one range, or if the
+ first range to be merged is the last range. */
+ if( this->nrange > 1 && start < this->nrange - 1 ) {
+
+/* Sort the specified ranges into increasing order of lower bound. */
+ qsort( this->range + 2*start, this->nrange - start,
+ 2*sizeof(*(this->range)), Comp_range );
+
+/* "nnew" is the number of ranges in the Moc after the merge. Initialise
+ it to the number of ranges not being merged, plus one (because the
+ two or more ranges being merged must produce at least one merged
+ range). */
+ nnew = start + 1;
+
+/* Merge contiguous or overlapping ranges. */
+ pw = this->range + 2*start;
+ pr = this->range + 2*start + 2;
+ for( irange = nnew; irange < this->nrange; irange++, pr += 2 ) {
+
+/* If the new range starts at or before the end of the current range, and
+ the new range ends after the end of the current range, extend the
+ current range to include the new range. */
+ if( pr[ 0 ] <= pw[ 1 ] + 1 ) {
+ if( pr[ 1 ] > pw[ 1 ] ) {
+ pw[ 1 ] = pr[ 1 ];
+ }
+
+/* If the new range starts after the end of the current range, increment
+ the current range and initialise it to the bounds of the current
+ range. */
+ } else {
+ pw += 2;
+ pw[ 0 ] = pr[ 0 ];
+ pw[ 1 ] = pr[ 1 ];
+ nnew++;
+ }
+ }
+
+/* Store the new number of ranges in the Moc. This may be smaller than
+ the original number, but will never be greater. Then realloc the
+ ranges array to save space. */
+ this->nrange = nnew;
+ this->range = astRealloc( this->range, 2*nnew*sizeof(*(this->range)) );
+ }
+
+/* Clear the cached information stored in the Moc structure so that it is
+ re-calculated when next needed. */
+ ClearCache( this, status );
+}
+
+void astMocNorm_( AstMoc *this, int negate, int cmode, int nold,
+ int maxorder, const char *method, int *status ){
+/*
+*+
+* Name:
+* astMocNorm
+
+* Purpose:
+* Normalise the supplied Moc.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "moc.h"
+* void astMocNorm( AstMoc *this, int negate, int cmode, int nold,
+* int maxorder, const char *method )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function normalises the supplied Moc. It is assumed that the
+* ranges of HEALPix cell indices within Moc structure are in two
+* groups: 1) range zero to range 'nold-1' are assumed to be already
+* normalised, 2) ranges 'nold' to the end are assumed not be be
+* normalised.
+
+* Parameters:
+* this
+* Pointer to the Moc.
+* negate
+* If non-zero, the HEALPix cells in ranges "nold" to the end are
+* negated before being merged with the earlier ranges.
+* cmode
+* Indicates how ranges 'nold' to the end are to be combined with the
+* earlier ranges. Any of the following values may be supplied:
+* - AST__AND: The modified Moc is the intersection of the original
+* Moc and the cell list.
+* - AST__OR: The modified Moc is the union of the original Moc and
+* the cell list.
+* - AST__XOR: The modified Moc is the exclusive disjunction of the
+* original Moc and the cell list.
+* nold
+* The number of cell ranges that are already normalised.
+* maxorder
+* The maximum HEALPix order.
+* method:
+* Name of calling method to include in error messages.
+* status
+* The inherited status.
+
+*/
+
+/* Check inherited status */
+ if( !astOK ) return;
+
+/* Sort the new ranges into increasing order of lower bound. */
+ MergeRanges( this, nold, status );
+
+/* If the cell list is to be inverted, we replace the new ranges
+ with the gaps between the new ranges. */
+ if( negate ) NegateRanges( this, nold, maxorder, status );
+
+/* Combine all the ranges using the specified combination method. */
+ CombineRanges( this, cmode, method, status );
+}
+
+static void NegateRanges( AstMoc *this, int start, int order,
+ int *status ){
+/*
+* Name:
+* NegateRanges
+
+* Purpose:
+* Negate the ranges of contiguous cells in a Moc.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void NegateRanges( AstMoc *this, int start, int order,
+* int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function negates the specified ranges of cells within a Moc
+* by replacing the ranges with new ranges that describe the gaps
+* between the original ranges. Ranges before "start" are left
+* unchanged.
+*
+* Note, it is assumed that the ranges being negated are sorted into
+* increasing order of lower bound and that they do not overlap or
+* touch. These conditions can be achieved by calling CombineRanges
+* before calling this function.
+
+* Parameters:
+* this
+* The Moc to use.
+* start
+* The index of the first range to negate.
+* order
+* The HEALPix order to which the ranges refer.
+* status
+* Pointer to the inherited status.
+
+*/
+
+/* Local Variables: */
+ int irange;
+ int64_t *pr;
+ int64_t istart;
+ int64_t max_nest;
+ int64_t next_start;
+
+/* Check inherited status */
+ if( !astOK ) return;
+
+/* Nothing to do if the Moc is empty. */
+ if( this->nrange > 0 ) {
+
+/* Get the maximum number of nested indices at the highest order in use. */
+ max_nest = 12*( 1L << 2*order );
+
+/* Does the first range start at zero? If so, there is no gap before it. */
+ if( this->range[ 2*start ] == 0 ) {
+
+/* Loop round all the new ranges stored in the Moc above. Replace each
+ range with the gap between it and the following range. */
+ pr = this->range + 2*start;
+ for( irange = start; irange < this->nrange; irange++ ) {
+
+/* The start of the next "gap" is the end of the current "range". */
+ pr[ 0 ] = pr[ 1 ] + 1;
+
+/* Check that the current range does not extend all the way to the last
+ nested index value. If it does not, stoe the end of the current gap.
+ If it does, break out of the range loop. */
+ if( pr[ 0 ] < max_nest ) {
+ pr[ 1 ] = pr[ 2 ] - 1;
+ } else {
+ break;
+ }
+
+/* Increment the pointer to the next range. */
+ pr += 2;
+ }
+
+/* If the last range extended to the last nested index value, we will end
+ up with one fewer range than we had to begin with. */
+ this->nrange = irange;
+
+/* Now handle cases where the first range does not start at zero. There
+ will be a gap before it. */
+ } else {
+
+/* Loop round all the new ranges stored in the Moc above. Replace each
+ range with the gap between it and the preceding range. */
+ istart = 0;
+ pr = this->range + 2*start;
+ for( irange = start; irange < this->nrange; irange++ ) {
+
+/* Record the index at which the next gap starts. */
+ next_start = pr[ 1 ] + 1;
+
+/* The end of the "current range" is changed to be the end of the gap
+ between the previous range and the current range. */
+ pr[ 1 ] = pr[ 0 ] - 1;
+
+/* The start of the "current range" is changed to be the start of the gap
+ between the previous range and the current range. */
+ pr[ 0 ] = istart;
+
+/* Update the start of the next gap. */
+ istart = next_start;
+
+/* Increment the pointer to the next range. */
+ pr += 2;
+ }
+
+/* If the last range ended before the last nested index value, there
+ is a gap after it and so we will end up with one more range than we
+ had to begin with. Extend the ranges array and then add in the gap
+ following the final range. */
+ if( istart < max_nest ) {
+ irange = this->nrange++;
+ this->range = astGrow( this->range, this->nrange, 2*sizeof(*(this->range)) );
+ pr = this->range + 2*irange;
+ if( astOK ) {
+ pr[ 0 ] = istart;
+ pr[ 1 ] = max_nest - 1;
+ }
+ }
+ }
+ }
+}
+
+static void NestedToXy( int64_t nested, int order, int *ix, int *iy ){
+/*
+* Name:
+* NestedToXy
+
+* Purpose:
+* Get the HPX12 grid coords of the cell at given HEALPix nested index
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void NestedToXy( int64_t nested, int order, int *ix, int *iy )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function returns the HPX12 grid coords of the cell at given
+* HEALPix nested index. This function is the inverse of XyToNested.
+
+* Parameters:
+* nested
+* The nested index of the required HEALPix cell.
+* order
+* The HEALPix order.
+* ix
+* Pointer to an int returned holding the one-based grid X index of
+* the cell within the HPX12 projection.
+* iy
+* Pointer to an int returned holding the one-based grid Y index of
+* the cell within the HPX12 projection.
+
+*/
+
+/* Local Variables: */
+ int fi;
+ int fx;
+ int fy;
+ int nppf;
+ int64_t tx;
+ int64_t ty;
+ int64_t tj;
+
+/* The least significant "2*order" bits hold the x index in the even bits
+ and the y index in the odd bits (both relative to the south corner of
+ the facet). The next more significant four bits holds the facet index.
+ Get the zero-based facet index. */
+ fi = ( nested >> (2*order) );
+
+/* Facet indices must be in the range 0-11. */
+ if( fi < 12 ) {
+
+/* Get the lowest "2*order" bits. */
+ tj = nested & ( ( 1L << (2*order) ) - 1 );
+
+/* Reverse the process in XyToNested to extract the even and odd bits
+ from "tj" into "tx" and "ty" - the zero-based offsets from the bottom
+ left (i.e. south) corner of the facet to the requested cell. */
+ tx = (tj >> 1 ) & 0x5555555555555555;
+ ty = tj & 0x5555555555555555;
+
+ tx = (tx | (tx >> 1)) & 0x3333333333333333;
+ tx = (tx | (tx >> 2)) & 0x0F0F0F0F0F0F0F0F;
+ tx = (tx | (tx >> 4)) & 0x00FF00FF00FF00FF;
+ tx = (tx | (tx >> 8)) & 0x0000FFFF0000FFFF;
+ tx = (tx | (tx >> 16)) & 0x00000000FFFFFFFF;
+
+ ty = (ty | (ty >> 1)) & 0x3333333333333333;
+ ty = (ty | (ty >> 2)) & 0x0F0F0F0F0F0F0F0F;
+ ty = (ty | (ty >> 4)) & 0x00FF00FF00FF00FF;
+ ty = (ty | (ty >> 8)) & 0x0000FFFF0000FFFF;
+ ty = (ty | (ty >> 16)) & 0x00000000FFFFFFFF;
+
+/* Get the number of cells along one edge of a facet.*/
+ nppf = ( 1 << order );
+
+/* Change tx so that it is measured in the opposite direction, starting
+ at the bottom left corner of the facet. */
+ tx = nppf - 1 - tx;
+
+/* Get the offset in facet along X and Y from the bottom left corner of
+ the HPX12 projection to the botton left corner of the facet. */
+ if( fi < 4 ) {
+ fx = fi;
+ fy = fx + 1;
+ } else if( fi < 8 ) {
+ fx = fi - 4;
+ fy = fx;
+ } else {
+ fx = fi - 7;
+ fy = fx - 1;
+ }
+
+/* Convert these offsets from facets to cells and add them onto the
+ offsets within the facet found above. Increment to get the required
+ 1-based X and Y indices. */
+ *ix = tx + fx*nppf + 1;
+ *iy = ty + fy*nppf + 1;
+
+/* Return -1 if the facet index is out of bounds */
+ } else {
+ *ix = -1;
+ *iy = -1;
+ }
+
+}
+
+static double OrderToRes( int order ){
+/*
+* Name:
+* OrderToRes
+
+* Purpose:
+* Get the mean resolution of a specified HEALPix order.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* double OrderToRes( int order )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function returns the mean resolution, in arc-seconds, corresponding
+* to the supplied HEALPix order.
+
+* Parameters:
+* order
+* The HEALPix order.
+
+* Returned Value:
+* The mean resolution, in arc-seconds.
+*/
+
+/* Check the supplied value. */
+ if ( order < 0 || order > AST__MXORDHPX ) return 0;
+
+/* There are 12*4^(order) equal area pixels covering the whole sphere. So
+ divide 4.pi steradians by 12*4^(order) to get the area of one pixel.
+ Convert to square arc-seconds and then take the square root to get he
+ mean pixel size. */
+ return 211076.29/(double)( 1 << order );
+}
+
+static void PutCell( AstMoc *this, AstMapping **map, int ix, int iy, int order,
+ CellList *clist, int regtype, void *data, int cmode,
+ const char *method, int *status ){
+/*
+* Name:
+* PutCell
+
+* Purpose:
+* Add a HEALPix cell into a CellList if it is an interior cell.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void PutCell( AstMoc *this, AstMapping **map, int ix, int iy, int order,
+* CellList *clist, int regtype, void *data, int cmode,
+* const char *method, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function examines a single specified HEALPix cell at a
+* specified order to see if it overlaps a specified region of
+* interest on the sky. If the cell is contained entirely within the
+* region of interest, the complete cell is added to the supplied
+* CellList. If the cell falls entirely outside the region of interest
+* it is ignored. If it falls on the edge of the region of interest, the
+* cell is subdivided into four child cells (i.e. the HEALPix order is
+* increased by one), and this function is called recursively to check
+* each child cell.
+
+* Parameters:
+* this
+* Pointer to the Moc.
+* map
+* Pointer to an array of Mapping pointers. Each Mapping transforms
+* from grid coordinates within an all-sky HPX-like projection
+* to the coordinate system used for determining if a given position
+* is inside or outside the region of interest (as indicated by the
+* value of parameter "regtype"). The Mapping stored at index "i"
+* within the supplied array of pointers is for HEALPix order "i".
+* The supplied array should have "maxorder+1" elements. The
+* HPX-like projection used is indicated by the "iproj" field in the
+* "clist" structure, as is the maxorder value.
+* ix
+* The 1-based grid index of the cell to be added, on the first
+* (X) input axis of Mapping "map[order]".
+* iy
+* The 1-based grid index of the cell to be added, on the second
+* (Y) input axis of Mapping "map[order]".
+* order
+* The 0-based order to which "ix" and "iy" refer.
+* clist
+* Pointer to a structure holding a list of the grid coords, at
+* all orders, at the centre of the cells that are inside the region
+* of interest. The contents of this structure are updated on exit to
+* hold any parts of the supplied cell that are inside the region of
+* intertest.
+* regtype
+* Indicates how the region of interest is defined:
+* 0: By an AST Region object. The outputs of "map" should be in
+* the Frame represented by the Region. The "data" pointer should
+* be a pointer to the Region.
+* 1: By a 2D pixel array. The outputs of "map" should be in grid
+* coordinates within the pixel array. The "data" pointer should
+* be a pointer to a PixelMask structure.
+* data
+* Pointer to an object that defines the region of interest. See
+* "regtype".
+* method
+* Name of calling method - for error messages.
+* status
+* Inherited status value.
+
+* Notes:
+* - Speeding up this algorithm by passing down corner information
+* from the higher level sounds plausible, but in fact causes problems
+* for regions which intersect the discontinuities at RA=0/6/12/18/24
+* hours. At such points, the fact that a corner position is a valid
+* interior point at order N does not guarantee that it is a valid
+* interior point at order N+1, because the edges of the valid region
+* of the projection changes with the order.
+
+*/
+
+/* Local Variables: */
+ AstPointSet *ps3;
+ AstPointSet *ps2;
+ AstPointSet *ps1;
+ AstRegion *region;
+ double **ptr3;
+ double **ptr1;
+ double *px;
+ double *py;
+ int i;
+ int ii;
+ int allin;
+ int allout;
+ int inside[9];
+ int ixc;
+ int iyc;
+ int j;
+ int npoint;
+
+/* Check the global error status. */
+ if( !astOK ) return;
+
+/* If we have reached the maximum order, just test the centre of the
+ specified cell. */
+ if( order == clist->maxorder ) {
+ npoint = 1;
+ ps1 = astPointSet( npoint, 2, "", status );
+ ptr1 = astGetPoints( ps1 );
+ if( astOK ) {
+ ptr1[ 0 ][ 0 ] = ix;
+ ptr1[ 1 ][ 0 ] = iy;
+ }
+
+/* Otherwise, test a 3x3 grid of points covering the specified cell. */
+ } else {
+ npoint = 9;
+ ps1 = astPointSet( npoint, 2, "", status );
+ ptr1 = astGetPoints( ps1 );
+ if( astOK ) {
+ px = ptr1[ 0 ];
+ py = ptr1[ 1 ];
+ for( j = -1; j <= 1; j++ ) {
+ for( i = -1; i <= 1; i++ ) {
+ *(px++) = ix + HALF*i;
+ *(py++) = iy + HALF*j;
+ }
+ }
+ }
+ }
+
+/* Transform these positions into region of interest coordinates. */
+ ps2 = astTransform( map[order], ps1, 1, NULL );
+
+/* If all the positions are inside the region of interest, we assume the
+ entire cell is inside the region of interest. First do ROIs defined
+ by a Region... */
+ if( regtype == 0 ) {
+
+/* If the ROI is defined by an AST Region object, use the Region to
+ transform the points in ps2. The results will be bad if the point is
+ outside the Region. Construct an array of 9 flags indicating which
+ points on the 3x3 grid covering the cell are inside the ROI. The array
+ of flags is in raster order, bottom left to top right. */
+ region = (AstRegion *) data;
+ ps3 = astTransform( region, ps2, 1, NULL );
+ ptr3 = astGetPoints( ps3 );
+ if( astOK ) {
+ px = ptr3[ 0 ];
+
+/* If only a single point is being tested, test it. */
+ if( npoint == 1 ){
+ inside[ 0 ] = ( *px != AST__BAD );
+
+/* Otherwise a 3x3 grid of points is being tested. */
+ } else {
+ for( i = 0; i < npoint; i++ ) {
+ inside[ i ] = ( *(px++) != AST__BAD );
+ }
+ }
+ }
+ ps3 = astAnnul( ps3 );
+
+/* If the ROI is defined by a pixel array, see which of the positions
+ correspond to selected pixels in the array and fill the "inside"
+ array with appropriate values. */
+ } else {
+ TestPixels( (PixelMask *) data, &npoint, ps2, inside, status );
+ }
+
+/* Test to see if all positions are inside, or outside, the ROI. */
+ allin = 1;
+ allout = 1;
+ for( i = 0; i < npoint; i++ ) {
+ if( inside[ i ] ) {
+ allout = 0;
+ } else {
+ allin = 0;
+ }
+ }
+
+/* If the specified pixel appears to be inside the region of interest, add it
+ to the supplied CellList. */
+ if( allin ) {
+ if( ix == AST__BAD || iy == AST__BAD ) {
+ astError( AST__INTER, "%s(%s): Bad interior cell centre "
+ "(internal programming error).", status, method,
+ astGetClass( this ) );
+ } else {
+ if( clist->len[ order ] < INT_MAX - 2 ) {
+ ii = clist->len[ order ]++;
+ clist->ix[ order ] = astGrow( clist->ix[ order ], ii + 1,
+ sizeof(*(clist->ix[ order ])));
+ clist->iy[ order ] = astGrow( clist->iy[ order ], ii + 1,
+ sizeof(*(clist->iy[ order ])));
+ } else {
+ astError( AST__BGMOC, "%s(%s): The normalised MOC has too "
+ "many elements.", status, method, astGetClass( this ) );
+ }
+ if( astOK ) {
+ clist->ix[ order ][ ii ] = ix;
+ clist->iy[ order ][ ii ] = iy;
+ }
+ }
+
+/* Otherwise, if the cell appears to be partially inside the region of
+ interest, divide up the current cell into 4 cells at order "order+1", and
+ call this function recursively to add the bits of each one that are inside
+ the region of interest. Do not do this if we are at the mximum order. */
+ } else if( !allout && order < clist->maxorder ) {
+ iyc = 2*iy - 1;
+ for( j = 0; j < 2; j++,iyc++ ) {
+ ixc = 2*ix - 1;
+ for( i = 0; i < 2; i++,ixc++ ) {
+ PutCell( this, map, ixc, iyc, order + 1, clist, regtype, data,
+ cmode, method, status );
+ }
+ }
+ }
+
+/* Free resources. */
+ ps2 = astAnnul( ps2 );
+ ps1 = astAnnul( ps1 );
+}
+
+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 "circle.h"
+* void RegBaseBox( AstRegion *this, double *lbnd, double *ubnd,
+* int *status )
+
+* Class Membership:
+* Circle 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: */
+ AstMoc *this;
+ AstPointSet *ps;
+ double **ptr;
+ double *pdec;
+ double *pra;
+ double maxdec;
+ double maxra0;
+ double maxra12;
+ double mindec;
+ double minra0;
+ double minra12;
+ double ra0;
+ double ra12;
+ double wid0;
+ double wid12;
+ int ip;
+ int np;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the Moc structure */
+ this = (AstMoc *) this_region;
+
+/* The bounding box is cached in the Moc structure, in the form of RA and
+ Dec values. If the bounds have not yet been calculated, calculate them
+ now. */
+ if( this->lbnd[ 0 ] == AST__BAD ) {
+
+/* Get a mesh of points over the boundary of the MOC, in ICRS (the base
+ Frame). */
+ ps = astRegBaseMesh( this );
+ ptr = astGetPoints( ps );
+ np = astGetNpoint( ps );
+ if( astOK ) {
+
+/* Go though all the positions in the mesh, maintaining two bounding
+ boxes - one centred on RA=0 and one centred on RA=12. */
+ mindec = DBL_MAX;
+ maxdec = -DBL_MAX;
+ minra0 = DBL_MAX;
+ maxra0 = -DBL_MAX;
+ minra12 = DBL_MAX;
+ maxra12 = -DBL_MAX;
+
+ pra = ptr[ 0 ];
+ pdec = ptr[ 1 ];
+ for( ip = 0; ip < np; ip++,pdec++,pra++ ){
+ if( *pdec < mindec ) mindec = *pdec;
+ if( *pdec > maxdec ) maxdec = *pdec;
+
+ ra12 = *pra;
+ while( ra12 < 0.0 ) ra12 += 2*AST__DPI;
+ while( ra12 > 2*AST__DPI ) ra12 -= 2*AST__DPI;
+ if( ra12 < minra12 ) minra12 = ra12;
+ if( ra12 > maxra12 ) maxra12 = ra12;
+
+ ra0 = *pra;
+ while( ra0 < -AST__DPI ) ra0 += 2*AST__DPI;
+ while( ra0 > AST__DPI ) ra0 -= 2*AST__DPI;
+ if( ra0 < minra0 ) minra0 = ra0;
+ if( ra0 > maxra0 ) maxra0 = ra0;
+ }
+
+/* Get the width of the two bounding boxes, and choose the narrower. */
+ wid0 = maxra0 - minra0;
+ wid12 = maxra12 - minra12;
+ if( wid0 < wid12 ) {
+ this->lbnd[ 0 ] = minra0;
+ this->ubnd[ 0 ] = maxra0;
+ } else {
+ this->lbnd[ 0 ] = minra12;
+ this->ubnd[ 0 ] = maxra12;
+ }
+
+ this->lbnd[ 1 ] = mindec;
+ this->ubnd[ 1 ] = maxdec;
+ }
+
+/* Free resources */
+ ps = astAnnul( ps );
+ }
+
+/* Return the bounds. */
+ lbnd[ 0 ] = this->lbnd[ 0 ];
+ ubnd[ 0 ] = this->ubnd[ 0 ];
+ lbnd[ 1 ] = this->lbnd[ 1 ];
+ ubnd[ 1 ] = this->ubnd[ 1 ];
+}
+
+static AstPointSet *RegBaseMesh( AstRegion *this_region, int *status ){
+/*
+* Name:
+* RegBaseMesh
+
+* Purpose:
+* Return a PointSet containing points spread around the boundary of a
+* Region.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "region.h"
+* AstPointSet *RegBaseMesh( AstRegion *this )
+
+* Class Membership:
+* Region virtual function.
+
+* Description:
+* This function returns a PointSet containing a set of points on
+* the boundary of the Moc. The points refer to the base Frame of
+* the encapsulated FrameSet (which is always ICRS for a Moc). The
+* points in the returned PointSet are ordered so that Dec increased
+* from start to end. Points with equal Dec (allowing for rounding
+* error) are sorted into increasing RA. All RA values are in the
+* range [0,2PI[.
+
+* Parameters:
+* this
+* Pointer to the Region.
+
+* Returned Value:
+* Pointer to the PointSet.
+
+* Notes:
+* - A NULL pointer is returned if an error has already occurred, or if
+* this function should fail for any reason.
+* - The implementation for the Moc class ignores the MeshSize
+* attribute defined by the parent Region class. Instead, a mesh point is
+* placed at every normalised Moc cell corner that falls on the boundary.
+*/
+
+/* Local Variables: */
+ AstMoc *this;
+ AstPointSet *result;
+ Cell **neb;
+ Cell *cell0;
+ Cell *cell1;
+ Cell *cell2;
+ Cell *cell;
+ Cell *cell_foot[ AST__MXORDHPX + 1 ];
+ Cell *prev_cell;
+ Corner *corner;
+ Corner *corner_foot;
+ Corner *new_corner;
+ Corner *old_corner;
+ Corner *prev_corner;
+ Corner *start;
+ astDECLARE_GLOBALS
+ double **ptr;
+ double *pdec;
+ double *pra;
+ int *pni;
+ int core;
+ int dist;
+ int icell;
+ int icorner;
+ int ix;
+ int iy;
+ int maxorder;
+ int minorder;
+ int npoint;
+ int nused;
+ int order;
+ int64_t *pnk;
+ int64_t npix;
+
+/* Initialise */
+ result = NULL;
+
+/* Check the local error status. */
+ if ( !astOK ) return result;
+
+/* Get a pointer to the Moc structure. */
+ this = (AstMoc *) this_region;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(this);
+
+/* If the Moc structure contains a pointer to a PointSet holding
+ a previously created mesh, return it. */
+ if( this->basemesh ) {
+ result = astClone( this->basemesh );
+
+/* Otherwise, if the Moc is empty, or covers the whole sky, indicate that
+ it has no boundary by returned a PointSet containing a single point with
+ a value of AST__BAD on every axis. */
+ } else if( this->nrange == 0 || ( this->range[0] == 0 &&
+ this->range[1] == 12*( 1L << 2*astGetMaxOrder(this) ) - 1 )){
+
+ this->basemesh = astPointSet( 1, 2, "", status );
+ ptr = astGetPoints( this->basemesh );
+ this->meshdist = astMalloc( sizeof( *(this->meshdist ) ) );
+ if( ptr ) {
+ ptr[ 0 ][ 0 ] = AST__BAD;
+ ptr[ 1 ][ 0 ] = AST__BAD;
+ this->meshdist[ 0 ] = 0.0;
+ }
+ result = astClone( this->basemesh );
+
+/* Otherwise, create a new mesh. */
+ } else {
+
+/* Get the resolution of the Moc. Convert from arc-seconds to radians.
+ Use a tenbgth of the MOC resolution at the current order. */
+ Comp_Corner_Tol = 0.1*AST__DD2R*OrderToRes( astGetMaxOrder( this ) )/3600.0;
+
+/* Initialise pointers to the end of the chain of Cell structures at each
+ order. */
+ for( order = 0; order <= AST__MXORDHPX; order++ ) {
+ cell_foot[ order ] = NULL;
+ }
+
+/* Ensure the the normalised form of the MOC is available. */
+ GetNorm( this, "astRegBaseMesh", status );
+
+/* Set the pointer to the first nuniq value in the normalised moc. Use a
+ local variable appropriate to the type of the normalised moc. */
+ if( this->knorm ) {
+ pnk = this->knorm;
+ pni = NULL;
+ } else {
+ pni = this->inorm;
+ pnk = NULL;
+ }
+
+/* Go through all the nuniq values in the normalised MOC. */
+ minorder = -1;
+ maxorder = -1;
+ for( icell = 0; icell < this->moclength; icell++ ) {
+
+/* Decode the nuniq value to get the order and npix, using a fast log2
+ function. Increment the pointer to the next nuniq value. */
+ if( pnk ) {
+ order = log2_64( *pnk / 4 ) / 2;
+ npix = *pnk - ( 1L << (2 + 2*order) );
+ pnk++;
+ } else {
+ order = log2_32( *pni / 4 ) / 2;
+ npix = *pni - ( 1 << (2 + 2*order) );
+ pni++;
+ }
+
+/* Convert the nested index (npix) to grid coords in an HPX12 projection. */
+ NestedToXy( npix, order, &ix, &iy );
+
+/* Create a Cell structure to describe the current nuniq value and append it
+ to the end of the chain of Cell structures at the current order. */
+ (void) MakeCell( ix, iy, order, cell_foot, status );
+
+/* Keep track of the highest and lowest order for which we have cells. */
+ if( cell_foot[ order ] ) {
+ if( minorder == -1 ) minorder = order;
+ maxorder = order;
+ }
+ }
+
+/* Find the edges of the cells at increasing orders. Each pass round this
+ loop moves bundary cells from 'order' to the equivalent 4 child cells
+ at 'order+1', and then deletes all remaining cells at 'order'. So after
+ the final pass (which has order = maxorder-1), all cells will be at
+ order 'maxorder'. */
+ for( order = minorder; order < maxorder; order++ ) {
+
+/* Skip this order if there are no cells at it. */
+ if( cell_foot[ order ] && astOK ) {
+
+/* Create chains of Corner structures for all cells at the current order.
+ A Corner holds the (RA,Dec) of a point on the sky, plus pointers to
+ up to four Cell structures that has that point at one of its corners.
+ A Corner is an "interior position" if it corresponds to a corner of
+ exactly four cells (there are a few special cases where interior
+ points may only be used by 3 cells). Note, whether a corner is
+ interior does not depend on whether the adjacent cells are flagged
+ as interior, it just depends on how many cells touch the corner. */
+ MakeCorners( this, order, cell_foot[ order ],
+ &corner_foot, 0, status );
+
+/* Identify interior cells (cells for which all the corners are
+ interior positions). Cells which are children of interior cells
+ at the next lower order will already be flagged as interior. */
+ cell = cell_foot[ order ];
+ while( cell ) {
+ if( cell->bl->interior && cell->tl->interior &&
+ cell->tr->interior && cell->br->interior ) {
+ cell->interior = 1;
+ }
+ cell = cell->prev;
+ }
+
+/* Find interior cells that are adjacent to at least one boundary cell
+ (i.e. find interior cells that are not also "core" cells). Subdivide
+ these, plus all boundary cells, into four child cells at the next
+ order, and add these child cells to the chain of cells at the next
+ order. We need to retain a layer of interior cells that are on the
+ inside of the boundary layer to prevent the inside edge of the hole
+ of unused cells in the middle of the region being mistaken for a
+ boundary of the region. */
+ cell = cell_foot[ order ];
+ while( cell ) {
+
+/* Assume this cell is a core cell (i.e. surrounded by interior cells on
+ all sides). */
+ core = 1;
+
+/* If the bottom left corner touches a cell that is not interior, then
+ the current cell is not a core cell. */
+ neb = cell->bl->cells;
+ for( icell = 0; icell < cell->bl->ncell; icell++ ) {
+ if( !((*(neb++))->interior) ) {
+ core = 0;
+ break;
+ }
+ }
+
+/* Otherwise, if the top left corner touches a cell that is not interior,
+ then the current cell is not a core cell. */
+ if( core ) {
+ neb = cell->tl->cells;
+ for( icell = 0; icell < cell->tl->ncell; icell++ ) {
+ if( !((*(neb++))->interior) ) {
+ core = 0;
+ break;
+ }
+ }
+ }
+
+/* Otherwise, if the top right corner touches a cell that is not interior,
+ then the current cell is not a core cell. */
+ if( core ) {
+ neb = cell->tr->cells;
+ for( icell = 0; icell < cell->tr->ncell; icell++ ) {
+ if( !((*(neb++))->interior) ) {
+ core = 0;
+ break;
+ }
+ }
+ }
+
+/* Otherwise, if the bottom right corner touches a cell that is not interior,
+ then the current cell is not a core cell. */
+ if( core ) {
+ neb = cell->br->cells;
+ for( icell = 0; icell < cell->br->ncell; icell++ ) {
+ if( !((*(neb++))->interior) ) {
+ core = 0;
+ break;
+ }
+ }
+ }
+
+/* If the current cell is core, check it is flagged as interior. */
+ if( core ) {
+ if( !( cell->interior ) && astOK ) {
+ astError( AST__INTER, "astRegBaseMesh(%s): Core cell "
+ "not flagged as interior (internal programming "
+ "error).", status, astGetClass( this ) );
+ break;
+ }
+
+/* If the current cell is not core, append its children to the end of the
+ chain of cells at the next order. These child cells will be flagged as
+ interior if the parent cell is interior. */
+ } else {
+ AppendChildren( this, cell, order, cell_foot, status );
+ }
+
+/* Move on to the next cell. */
+ cell = cell->prev;
+ }
+
+/* Delete all cells at the current order */
+ cell = cell_foot[ order ];
+ while( cell ) {
+ prev_cell = cell->prev;
+ (void) astFree( cell );
+ cell = prev_cell;
+ }
+
+/* Delete all corners at the current order */
+ corner = corner_foot;
+ while( corner ) {
+ prev_corner = corner->prev;
+ (void) astFree( corner );
+ corner = prev_corner;
+ }
+
+/* Ensure the Cell foot pointer is nullified for this order. */
+ cell_foot[ order ] = NULL;
+ }
+ }
+
+/* All cells are now at the maximum order. Create the chain of Corners
+ for the cells at this order. Indicate that the chain should be sorted
+ (primary key is Dec, secondary key is RA). */
+ MakeCorners( this, maxorder, cell_foot[ maxorder ], &corner_foot,
+ 1, status );
+
+/* We now measure how far around the perimeter we need to go to reach
+ each non-interior corner, starting from an arbitrary starting point.
+ This information is needed by the astRegTrace method. The distance
+ to each non-interior corner (in units of corners) is stored in the
+ "dist" component of the Corner structure. These will have been
+ initialised above to INT_MAX, indicating "no distance yet found".
+ Initialise the distance from the start to zero and then loop
+ until all non-interior corners have a distance. */
+ dist = 0;
+ while( astOK ) {
+
+/* Find the first non-interior corner that has not yet been assigned a
+ perimeter distance. */
+ corner = corner_foot;
+ while( corner && ( corner->interior || corner->dist != INT_MAX ) ) {
+ corner = corner->prev;
+ }
+
+/* We are done if no such non-interior corner was found. */
+ if( !corner ) break;
+
+/* There could be several disjoint areas in the Moc, each having its own
+ permimeter. The corner we have found above marks the start of such a
+ perimeter. Loop until we arrive back at the starting corner. */
+ old_corner = NULL;
+ start = corner;
+ while( astOK ) {
+
+/* Store the distance (as a number of corners) along the perimeter from the
+ start to the current corner. If the corner already has a distance
+ (i.e. because it is the touching point between two otherwise disjoint
+ regions), leave the existing distance unchanged. */
+ if( corner->dist == INT_MAX ) {
+ corner->dist = dist++;
+
+/* The Moc may contain several disjoint regions. We mark the start of each such
+ region with a negative "dist" value. */
+ if( !old_corner ) corner->dist = -corner->dist;
+ }
+
+/* Indicate we have not yet chosen the next corner on the path. */
+ new_corner = NULL;
+
+/* Since the current corner is not interior it can be used by at most 3
+ cells. Save convenience pointers to them. These pointers are checked
+ in the order "cell0", "cell1", "cell2" to see if an onward route can
+ be found to a corner of the cell. If the corner is used by only one
+ cell, it becomes "cell0". */
+ if( corner->ncell == 1 ) {
+ cell0 = corner->cells[ 0 ];
+ cell1 = NULL;
+ cell2 = NULL;
+
+/* If the corner is used by two cells, the cell that contains the old
+ corner as well as the current corner becomes "cell1", and the other
+ cell becomes "cell0". This gives priority to onward routes that go to
+ the new cell ("cell0") rather than going back to the old cell. */
+ } else if( corner->ncell == 2 ) {
+ cell1 = corner->cells[ 0 ];
+ if( cell1->bl == old_corner ||
+ cell1->tl == old_corner ||
+ cell1->tr == old_corner ||
+ cell1->br == old_corner ) {
+ cell0 = corner->cells[ 1 ];
+ } else {
+ cell0 = cell1;
+ cell1 = corner->cells[ 0 ];
+ }
+ cell2 = NULL;
+
+/* If the corner is used by three cells, the cell that contains the old
+ corner as well as the current corner becomes "cell2" (lowest priority),
+ and the other two cells become "cell0" and "cell1". This gives priority
+ to onward routes that do not go back to the old cell. */
+ } else if( corner->ncell == 3 ) {
+ for( icell = 0; icell < 3; icell++ ) {
+ cell2 = corner->cells[ icell ];
+ if( cell2->bl == old_corner ||
+ cell2->tl == old_corner ||
+ cell2->tr == old_corner ||
+ cell2->br == old_corner ) break;
+ }
+ if( icell == 4 && old_corner ) {
+ astError( AST__INTER, "astRegBaseMesh(%s): Old corner "
+ "not found (internal programming error).",
+ status, astGetClass( this ) );
+ break;
+ }
+
+/* Of the other two cells, prefer routes that continue to the cell that
+ is diagonally opposite the cell containing the old corner. Assign the
+ remaining two cells to "cell0" and "cell1", then swap them if cell1
+ is diagonally opposite "cell2". */
+ cell0 = corner->cells[ ( icell + 1 ) % 3 ];
+ cell1 = corner->cells[ ( icell + 2 ) % 3 ];
+ if( ( cell2->tr == corner && cell1->bl == corner ) ||
+ ( cell2->br == corner && cell1->tl == corner ) ||
+ ( cell2->bl == corner && cell1->tr == corner ) ||
+ ( cell2->tl == corner && cell1->br == corner ) ) {
+ cell = cell0;
+ cell0 = cell1;
+ cell1 = cell;
+ }
+
+/* The corner should never be used by more than 3 cells. */
+ } else {
+ astError( AST__INTER, "astRegBaseMesh(%s): Boundary corner "
+ "used by %d cells (internal programming error).",
+ status, astGetClass( this ), corner->ncell );
+ break;
+ }
+
+/* Find the corner in cell0 and see if the neighbouring corner in
+ clockwise direction within cell0 is a boundary point that has not yet
+ been included in the path. If so, we use it as the next corner. */
+ if( corner == cell0->bl && !cell0->tl->interior && cell0->tl->dist == INT_MAX ) {
+ new_corner = cell0->tl;
+ } else if( corner == cell0->tl && !cell0->tr->interior && cell0->tr->dist == INT_MAX ) {
+ new_corner = cell0->tr;
+ } else if( corner == cell0->tr && !cell0->br->interior && cell0->br->dist == INT_MAX ) {
+ new_corner = cell0->br;
+ } else if( corner == cell0->br && !cell0->bl->interior && cell0->bl->dist == INT_MAX ) {
+ new_corner = cell0->bl;
+
+/* If this failed to produce a new corner, then do the same using cell1
+ (if it exists). */
+ } else if( cell1 ) {
+ if( corner == cell1->bl && !cell1->tl->interior && cell1->tl->dist == INT_MAX ) {
+ new_corner = cell1->tl;
+ } else if( corner == cell1->tl && !cell1->tr->interior && cell1->tr->dist == INT_MAX ) {
+ new_corner = cell1->tr;
+ } else if( corner == cell1->tr && !cell1->br->interior && cell1->br->dist == INT_MAX ) {
+ new_corner = cell1->br;
+ } else if( corner == cell1->br && !cell1->bl->interior && cell1->bl->dist == INT_MAX ) {
+ new_corner = cell1->bl;
+
+/* If this failed to produce a new corner, then do the same using cell3
+ (if it exists). */
+ } else if( cell2 ) {
+ if( corner == cell2->bl && !cell2->tl->interior && cell2->tl->dist == INT_MAX ) {
+ new_corner = cell2->tl;
+ } else if( corner == cell2->tl && !cell2->tr->interior && cell2->tr->dist == INT_MAX ) {
+ new_corner = cell2->tr;
+ } else if( corner == cell2->tr && !cell2->br->interior && cell2->br->dist == INT_MAX ) {
+ new_corner = cell2->br;
+ } else if( corner == cell2->br && !cell2->bl->interior && cell2->bl->dist == INT_MAX ) {
+ new_corner = cell2->bl;
+ }
+ }
+ }
+
+/* The above may fail to find a suitable new corner if two disjoint
+ regions touch at the new corner, since that corner will already
+ have a distance assigned to it when it comes to be checked as part of
+ tracing the second disjoint region. So try the whole thing again, but
+ this time allowing the new corner to have a pre-assigned distance (i.e.
+ to already have been used). This means we give priority to adjacent
+ corners that have not already been used, but allow used corners to be
+ re-used if necessary. */
+ if( !new_corner ) {
+ if( corner == cell0->bl && !cell0->tl->interior ) {
+ new_corner = cell0->tl;
+ } else if( corner == cell0->tl && !cell0->tr->interior ) {
+ new_corner = cell0->tr;
+ } else if( corner == cell0->tr && !cell0->br->interior ) {
+ new_corner = cell0->br;
+ } else if( corner == cell0->br && !cell0->bl->interior ) {
+ new_corner = cell0->bl;
+
+ } else if( cell1 ) {
+ if( corner == cell1->bl && !cell1->tl->interior ) {
+ new_corner = cell1->tl;
+ } else if( corner == cell1->tl && !cell1->tr->interior ) {
+ new_corner = cell1->tr;
+ } else if( corner == cell1->tr && !cell1->br->interior ) {
+ new_corner = cell1->br;
+ } else if( corner == cell1->br && !cell1->bl->interior ) {
+ new_corner = cell1->bl;
+
+ } else if( cell2 ) {
+ if( corner == cell2->bl && !cell2->tl->interior ) {
+ new_corner = cell2->tl;
+ } else if( corner == cell2->tl && !cell2->tr->interior ) {
+ new_corner = cell2->tr;
+ } else if( corner == cell2->tr && !cell2->br->interior ) {
+ new_corner = cell2->br;
+ } else if( corner == cell2->br && !cell2->bl->interior ) {
+ new_corner = cell2->bl;
+ }
+ }
+ }
+
+/* Count the number of consecutive corners that have already been used.
+ Abort if the last five corners were all re-used, since we have
+ probably got into a loop. */
+ if( new_corner && ++nused == 5 && astOK ) {
+ astError( AST__INTER, "astRegBaseMesh(%s): Re-drawing "
+ "previously drawn corners (internal "
+ "programming error).", status, astGetClass( this ) );
+ }
+ } else {
+ nused = 0;
+ }
+
+/* If we have arrived back at the first corner, break out of the loop. */
+ if( new_corner == start ) break;
+
+/* Sanity check. Check the next corner selected above is OK. */
+ if( ( !new_corner || new_corner->interior ) && astOK ) {
+ if( !new_corner ) {
+ astError( AST__INTER, "astRegBaseMesh(%s): Next perimeter "
+ "corner is undefined (internal programming error).",
+ status, astGetClass( this ) );
+ } else {
+ astError( AST__INTER, "astRegBaseMesh(%s): Next perimeter "
+ "corner is interior (internal programming error).",
+ status, astGetClass( this ) );
+ }
+ }
+
+/* Record the previous corner, and move on to the next corner. */
+ old_corner = corner;
+ corner = new_corner;
+ }
+ }
+
+/* Create the returned PointSet and put the (ra,dec) values into it
+ from each non-interior corner. First count the number of non-interior
+ corners. */
+ npoint = 0;
+ corner = corner_foot;
+ while( corner ) {
+ if( !(corner->interior) ) npoint++;
+ corner = corner->prev;
+ }
+
+/* Allocate an array to hold the indices within the returned PointSet in
+ order of increasing perimeter distance. */
+ this->meshdist = astMalloc( npoint*sizeof( *(this->meshdist ) ) );
+
+/* Create the PointSet, and get pointers to its data arrays. */
+ result = astPointSet( npoint, 2, " ", status );
+ ptr = astGetPoints( result );
+ if( astOK ) {
+ pra = ptr[ 0 ];
+ pdec = ptr[ 1 ];
+
+/* Store the RA and Dec values at all non-interior corners, then delete
+ the corners. Also invert the "distance" values stored in the Corner
+ structures to get an array that indexes the mesh in order of distance
+ around the perimeter. Negative values in this array indicate breaks
+ in the perimeter between separate disjoint regions. */
+ icorner = 0;
+ corner = corner_foot;
+ while( corner ) {
+ if( !corner->interior ) {
+ *(pra++) = corner->ra;
+ *(pdec++) = corner->dec;
+
+ if( corner->dist >= 0 ) {
+ (this->meshdist)[ corner->dist ] = icorner++;
+ } else {
+ (this->meshdist)[ -corner->dist ] = -(icorner++);
+ }
+
+ }
+ prev_corner = corner->prev;
+ (void) astFree( corner );
+ corner = prev_corner;
+ }
+
+/* Store it in the parent Region structure for future use. */
+ this->basemesh = astClone( result );
+ }
+
+/* Free the remaining cells. */
+ cell = cell_foot[ maxorder ];
+ while( cell ) {
+ prev_cell = cell->prev;
+ (void) astFree( cell );
+ cell = prev_cell;
+ }
+ }
+
+/* Annul the result if an error has occurred. */
+ if( !astOK ) result = astAnnul( result );
+
+/* Return a pointer to the output PointSet. */
+ 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 Moc.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* int RegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc,
+* int **mask, int *status ){
+
+* Class Membership:
+* Moc 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 Moc.
+*
+* Some tolerance is allowed, as specified by the resolution of the Moc,
+* and the supplied uncertainty Region "unc" which describes the
+* uncertainty of the supplied points.
+
+* Parameters:
+* this
+* Pointer to the Moc.
+* pset
+* Pointer to the PointSet. The points are assumed to refer to the
+* base Frame of the FrameSet encapsulated by "this" (always ICRS
+* for a Moc)
+* 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: */
+ AstMoc *this;
+ AstPointSet *mesh;
+ astDECLARE_GLOBALS
+ double **ptr_mesh;
+ double **ptr_pins;
+ double *pdec_mesh2;
+ double *pdec_mesh;
+ double *pra_mesh;
+ double *pra_mesh2;
+ double *safe;
+ double cosdec;
+ double dec_high;
+ double dec_low;
+ double dec_pin;
+ double dra;
+ double drad;
+ double l1;
+ double l2;
+ double lbnd_unc[ 2 ];
+ double ra_high;
+ double ra_low;
+ double ra_pin;
+ double ubnd_unc[ 2 ];
+ int *index;
+ int *pi;
+ int *pm;
+ int imesh2;
+ int imesh;
+ int ipin;
+ int len_mesh;
+ int len_pins;
+ int on;
+ int result;
+
+/* Initialise */
+ result = 0;
+ if( mask ) *mask = NULL;
+
+/* Check the inherited status. */
+ if( !astOK ) return result;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(this_region);
+
+/* Get a pointer to the Moc structure. */
+ this = (AstMoc *) this_region;
+
+/* Check the supplied PointSet has the 2 axis values per point. */
+ if( astGetNcoord( pset ) != 2 && astOK ) {
+ astError( AST__INTER, "astRegPins(%s): Illegal number of axis "
+ "values per point (%d) in the supplied PointSet - should be "
+ "2 (internal AST programming error).", status, astGetClass( this ),
+ astGetNcoord( pset ) );
+ }
+
+/* Get the number of axes in the uncertainty Region and check it is the
+ same as above. */
+ if( unc && astGetNaxes( unc ) != 2 && astOK ) {
+ astError( AST__INTER, "astRegPins(%s): Illegal number of axes "
+ "(%d) in the supplied uncertainty Region - should be 2 "
+ "(internal AST programming error).", status,
+ astGetClass( this ), astGetNaxes( unc ) );
+ }
+
+/* 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 Moc for it still to be considered to be on the boundary.
+ First get the resolution of the Moc, in radians. */
+ l1 = AST__DD2R*OrderToRes( astGetMaxOrder( this ) )/3600.0;
+
+/* 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 );
+ astGetRegionBounds( unc, lbnd_unc, ubnd_unc );
+
+/* Find the geodesic length of the diagonal of this bounding box. */
+ l2 = astDistance( unc, lbnd_unc, ubnd_unc );
+
+/* Use a zero sized box "unc" if no box was supplied. */
+ } else {
+ l2 = 0.0;
+ }
+
+/* The required border width is the total diagonal of the two bounding
+ boxes. */
+ drad = l1 + l2;
+
+/* Get a mesh of points on the boundary of the Moc. The mesh is sorted into
+ increasing Dec (values with equal Dec are sorted into increasing RA).
+ All RA values are in the range [0,2PI[. */
+ mesh = astRegBaseMesh( this );
+ ptr_mesh = astGetPoints( mesh );
+ len_mesh = astGetNpoint( mesh );
+
+/* Get pointers to the (ra,dec) values at all pis, and the number of
+ pins. */
+ ptr_pins = astGetPoints( pset );
+ len_pins = astGetNpoint( pset );
+
+/* If a mask is required, allocate it now, initialising it to all zeros. */
+ if( mask ) *mask = astCalloc( len_pins, sizeof(**mask) );
+
+/* Sort the supplied PointSet in the same way. Use an index to avoid
+ modifying the supplied PointSet. */
+ index = astMalloc( len_pins*sizeof( *index ) );
+ if( astOK ) {
+ Comp_Decra_Ptr1 = ptr_pins[ 0 ];
+ Comp_Decra_Ptr2 = ptr_pins[ 1 ];
+ pi = index;
+ for( ipin = 0; ipin < len_pins; ipin++ ) *(pi++) = ipin;
+ qsort( index, len_pins, sizeof(*index), Comp_decra );
+
+/* Initialise pointers to the first point in the mesh, and its index. */
+ pra_mesh = ptr_mesh[ 0 ];
+ pdec_mesh = ptr_mesh[ 1 ];
+ imesh = 0;
+
+/* For the moment, assume all pins are on the boundary. */
+ result = 1;
+
+/* Now loop through the sorted pins. */
+ pm = mask ? *mask : NULL;
+ pi = index;
+ for( ipin = 0; ipin < len_pins; ipin++,pi++,pm++ ) {
+ ra_pin = ptr_pins[ 0 ][ *pi ];
+ dec_pin = ptr_pins[ 1 ][ *pi ];
+
+/* Get the bounds of the box in which a mesh point must fall for the pin
+ to be on the boundary. */
+ cosdec= cos( dec_pin );
+ if( cosdec > 0.0 ) {
+ dra = drad/cos( dec_pin );
+ ra_low = ra_pin - dra;
+ ra_high = ra_pin + dra;
+ } else {
+ ra_low = -DBL_MAX;
+ ra_high = DBL_MAX;
+ }
+ dec_low = dec_pin - drad;
+ dec_high = dec_pin + drad;
+
+/* Move forward through the mesh until a mesh point is found that has a
+ Dec value larger than or equal to the lower Dec limit for the current
+ pin. */
+ for( ; imesh < len_mesh; imesh++,pra_mesh++,pdec_mesh++ ) {
+ if( *pdec_mesh >= dec_low ) break;
+ }
+
+/* If no such mesh point is found, the current pin and all subsequent
+ pins in the sorted list do not fall on the boundary. So we can break
+ out of the pin loop. */
+ if( imesh == len_mesh ) {
+ result = 0;
+ break;
+ }
+
+/* Move forward through the mesh until a mesh point is found that has a
+ RA value within the RA limits for the current pin, or the upper Dec
+ limit is exceeded. Modify each mesh RA value so that it is within
+ [-PI,+PI[ of the pin's RA before checking if it is within the limits.
+ If such a mesh point is found, the pin is on the boundary. */
+ pra_mesh2 = pra_mesh;
+ pdec_mesh2 = pdec_mesh;
+ imesh2 = imesh;
+ on = 0;
+ for( ; imesh2 < len_mesh; imesh2++,pra_mesh2++,pdec_mesh2++ ) {
+ if( *pdec_mesh2 > dec_high ) break;
+
+ dra = *pra_mesh2 - ra_pin;
+ while( dra >= AST__DPI ) dra -= 2*AST__DPI;
+ while( dra < -AST__DPI ) dra += 2*AST__DPI;
+ dra += ra_pin;
+
+ if( dra >= ra_low && dra <= ra_high ) {
+ on = 1;
+ break;
+ }
+ }
+
+/* If a suitable mesh point was found and a mask is being created, flag
+ that the pin is on the boundary and continue to check further pins. */
+ if( on ) {
+ if( mask ) *pm = 1;
+
+/* If no suitable mesh point was found, the current pin does not fall on
+ the boundary but later ones may do. We can break out of the pin loop
+ unless a mask is being created, in which case we need to continue to
+ check later pins. */
+ } else {
+ result = 0;
+ if( !mask ) break;
+ }
+ }
+ }
+
+/* Free resources */
+ mesh = astAnnul( mesh );
+ index = astFree( index );
+ safe = astFree( safe );
+
+/* If an error has occurred, return zero. */
+ if( !astOK ) {
+ result = 0;
+ if( mask ) *mask = astFree( *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 "region.h"
+* int astRegTrace( AstRegion *this, int n, double *dist, double **ptr );
+
+* Class Membership:
+* Moc member function (overrides the astRegTrace 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 astRegTrace method is implemented by the class
+* of Region supplied, and zero if not.
+
+*/
+
+/* Local Variables; */
+ AstMoc *this;
+ AstPointSet *mesh;
+ double **ptr_mesh;
+ double whi;
+ double wlo;
+ double x;
+ int i;
+ int imesh;
+ int imesh_lo;
+ int imesh_hi;
+ int jhi;
+ int jlo;
+ int len_mesh;
+
+/* Check inherited status. */
+ if( ! astOK ) return 0;
+
+/* Get a pointer to the Moc structure. */
+ this = (AstMoc *) this_region;
+
+/* Check we have some points to find. */
+ if( n > 0 ) {
+
+/* Get a mesh of points on the boundary of the Moc. The mesh is sorted into
+ increasing Dec (values with equal Dec are sorted into increasing RA).
+ All RA values are in the range [0,2PI[. */
+ mesh = astRegBaseMesh( this );
+ ptr_mesh = astGetPoints( mesh );
+ len_mesh = astGetNpoint( mesh );
+ if( astOK ) {
+
+/* Another array is created at the same time as the above mesh, which
+ holds indices into the mesh array. These index values are sorted
+ so that the corresponding mesh positions move monotonically around
+ the perimeter of the MOC. We use this array to find the required
+ positions. Do each required distance in turn. */
+ for( i = 0; i < n; i++ ) {
+
+/* Scale the supplied distance (in the range 0 -> 1) into the range 0
+ -> len_mesh-1. Do linear interpolation between adjacent points. A Moc
+ may contain several disjoint regions. If so, the first point in the
+ second and each subsequent region is indicated by a negative value in
+ the "meshdist" array. Return bad values for positions between the
+ start of each such regioon and the end of the previous. This tells the
+ curve plotting algorithm in the Plot class to introduce a break, rather
+ than connecting the two regions with a straight line. The
+ "this->meshdist" converts distance values into indices into the mesh. */
+ x = dist[ i ]*( len_mesh - 1 );
+ jlo = (int)( x );
+ jhi = jlo + 1;
+ whi = x - jlo;
+ wlo = 1.0 - whi;
+
+ if( jlo < 0 ) {
+ imesh = (this->meshdist)[ 0 ];
+ ptr[ 0 ][ i ] = ptr_mesh[ 0 ][ imesh ];
+ ptr[ 1 ][ i ] = ptr_mesh[ 1 ][ imesh ];
+
+ } else if( jhi >= len_mesh ) {
+ imesh = (this->meshdist)[ len_mesh - 1 ];
+ ptr[ 0 ][ i ] = ptr_mesh[ 0 ][ imesh ];
+ ptr[ 1 ][ i ] = ptr_mesh[ 1 ][ imesh ];
+
+ } else {
+ imesh_hi = (this->meshdist)[ jhi ];
+ if( imesh_hi < 0 ) {
+ ptr[ 0 ][ i ] = AST__BAD;
+ ptr[ 1 ][ i ] = AST__BAD;
+ } else {
+ imesh_lo = (this->meshdist)[ jlo ];
+ if( imesh_lo < 0 ) imesh_lo = -imesh_lo;
+
+ ptr[ 0 ][ i ] = wlo*ptr_mesh[ 0 ][ imesh_lo ] +
+ whi*ptr_mesh[ 0 ][ imesh_hi ];
+ ptr[ 1 ][ i ] = wlo*ptr_mesh[ 1 ][ imesh_lo ] +
+ whi*ptr_mesh[ 1 ][ imesh_hi ];
+ }
+ }
+ }
+ }
+
+/* Free resources. */
+ mesh = astAnnul( mesh );
+ }
+
+/* Return the result. */
+ return 1;
+}
+
+static int ResToOrder( double res ){
+/*
+* Name:
+* ResToOrder
+
+* Purpose:
+* Get the HEALPix order for a given resolution.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* int ResToOrder( double res )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function returns the HEALPix order that gives a resolution
+* closest to the supplied resolution.
+
+* Parameters:
+* res
+* The resolution, in arc-seconds.
+
+* Returned Value:
+* The HEALPix order.
+*/
+
+/* Check the supplied value. */
+ if ( res <= 0.0 ) return 0;
+
+/* The inverse of OrderToRes. */
+ return (int)( 0.5 + log( 211076.29/res )/0.6931472 );
+}
+
+
+static void SetAttrib( AstObject *this_object, const char *setting, int *status ) {
+/*
+* Name:
+* SetAttrib
+
+* Purpose:
+* Set an attribute value for a Moc.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void SetAttrib( AstObject *this, const char *setting, int *status )
+
+* Class Membership:
+* Moc member function (extends the astSetAttrib method inherited from
+* the Region class).
+
+* Description:
+* This function assigns an attribute value for a Moc, 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 Moc.
+* setting
+* Pointer to a null terminated string specifying the new attribute
+* value.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* void
+*/
+
+/* Local Vaiables: */
+ AstMoc *this; /* Pointer to the Moc structure */
+ double dval; /* Floating point attribute value */
+ int ival; /* Integer attribute value */
+ int len; /* Length of setting string */
+ int nc; /* Number of characters read by astSscanf */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the Moc structure. */
+ this = (AstMoc *) this_object;
+
+/* Obtain the length of the setting string. */
+ len = 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. */
+
+/* MaxOrder. */
+/* --------- */
+ if ( nc = 0,
+ ( 1 == astSscanf( setting, "maxorder= %d %n", &ival, &nc ) )
+ && ( nc >= len ) ) {
+ if( astTestMaxOrder( this ) ) {
+ astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.",
+ status, setting, astGetClass( this ) );
+ astError( AST__NOWRT, "The previously set \"MaxOrder\" value cannot "
+ "be changed." , status );
+ } else {
+ astSetMaxOrder( this, ival );
+ }
+
+/* MaxRes. */
+/* ------- */
+ } else if ( nc = 0,
+ ( 1 == astSscanf( setting, "maxres= %lg %n", &dval, &nc ) )
+ && ( nc >= len ) ) {
+ if( astTestMaxOrder( this ) ) {
+ astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.",
+ status, setting, astGetClass( this ) );
+ astError( AST__NOWRT, "The previously set \"MaxRes\" value cannot "
+ "be changed." , status );
+ } else {
+ ival = ResToOrder( dval );
+ astSetMaxOrder( this, ival );
+ }
+
+/* MinOrder. */
+/* --------- */
+ } else if ( nc = 0,
+ ( 1 == astSscanf( setting, "minorder= %d %n", &ival, &nc ) )
+ && ( nc >= len ) ) {
+ astSetMinOrder( this, ival );
+
+/* MinRes. */
+/* ------- */
+ } else if ( nc = 0,
+ ( 1 == astSscanf( setting, "minres= %lg %n", &dval, &nc ) )
+ && ( nc >= len ) ) {
+ ival = ResToOrder( dval );
+ astSetMinOrder( this, ival );
+
+/* 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( "moctype" ) ||
+ MATCH( "moclength" ) ||
+ MATCH( "mocarea" ) ){
+ 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 );
+ }
+
+/* Undefine macros local to this function. */
+#undef MATCH
+}
+
+static void SetMaxOrder( AstMoc *this, int value, int *status ){
+/*
+*
+* Name:
+* SetMaxOrder
+
+* Purpose:
+* Sety the value for the MaxOrder attribute
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void SetMaxOrder( AstMoc *this, int value, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* Sets the value of the MaxPlot attribute.
+
+* Parameters:
+* this
+* The Moc.
+* value
+* The new attribute value.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Store the supplied value. */
+ this->maxorder = astMIN(astMAX(value,0),AST__MXORDHPX);
+
+/* Clear the cached information stored in the Moc structure so that it is
+ re-calculated when next needed. */
+ ClearCache( this, status );
+}
+
+static void Sink1( void *data, size_t nc, const char *buf, int *status ){
+/*
+* Name:
+* Sink1
+
+* Purpose:
+* A sink function for use with astGetMocText
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void Sink1( void *data, size_t nc, const char *buf, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function accumulates the length of the strings written out by
+* astGetMocText, but does not actually write the strings out anywhere.
+
+* Parameters:
+* data
+* Pointer to an arbitrary structure used to communicate with the
+* code that is calling astGetMocText.
+* nc
+* The number of character to be written out from "buf".
+* buf
+* A buffer holding the characters to be written out.
+* status
+* Pointer to the inherited status variable.
+
+*/
+ if( data ) *((size_t *) data) += nc;
+}
+
+static void Sink2( void *data, size_t nc, const char *buf, int *status ){
+/*
+* Name:
+* Sink2
+
+* Purpose:
+* A sink function for use with astGetMocText
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void Sink2( void *data, size_t nc, const char *buf, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function appends the supplied strings to the string pointed to
+* by the supplied data structure, reporting an error if the string is
+* too small.
+
+* Parameters:
+* data
+* Pointer to an arbitrary structure used to communicate with the
+* code that is calling astGetMocText.
+* nc
+* The number of character to be written out from "buf".
+* buf
+* A buffer holding the characters to be written out.
+* status
+* Pointer to the inherited status variable.
+
+*/
+ if( !astOK ) return;
+
+ SinkData *data2 = (SinkData *) data;
+
+ if( nc > data2->mxsize ) {
+ astError( AST__SMBUF, "astGetMocString(Moc): The supplied string "
+ "buffer is too small.", status );
+ } else {
+ memcpy( data2->string, buf, nc );
+ data2->string += nc;
+ data2->mxsize -= nc;
+ }
+}
+
+static const char *Source1( void *data, size_t *nc, int *status ){
+/*
+* Name:
+* Source1
+
+* Purpose:
+* A source function for use with astAddMocText
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* const char *Source1( void *data, size_t *nc, int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function returns a pointer to the next set of characters to be
+* added into the Moc by astAddMocText, together with the number of
+* characters in the set.
+
+* Parameters:
+* data
+* Pointer to an arbitrary structure used to communicate with the
+* code that is calling astAddMocText.
+* nc
+* Returned holding the number of character to be added into the Moc.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the characters to be added into the Moc, or NULL if no
+* more characters remain to be added or an error has occurred.
+
+*/
+
+ SourceData *data2 = (SourceData *) data;
+ const char *result = NULL;
+ *nc = 0;
+
+ if( !astOK || ! data ) return result;
+
+ result = data2->string;
+ *nc = data2->mxsize;
+
+ data2->string = NULL;
+ data2->mxsize = 0;
+
+ return result;
+}
+
+static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* TestAttrib
+
+* Purpose:
+* Test if a specified attribute value is set for a Moc.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* int TestAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* Moc member function (over-rides the astTestAttrib protected
+* method inherited from the Region class).
+
+* Description:
+* This function returns a boolean result (0 or 1) to indicate whether
+* a value has been set for one of a Moc's attributes.
+
+* Parameters:
+* this
+* Pointer to the Moc.
+* 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: */
+ AstMoc *this; /* Pointer to the Moc structure */
+ int result; /* Result value to return */
+
+/* Initialise. */
+ result = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Obtain a pointer to the Moc structure. */
+ this = (AstMoc *) this_object;
+
+/* Check the attribute name and test the appropriate attribute. */
+
+/* MaxOrder. */
+/* --------- */
+ if ( !strcmp( attrib, "maxorder" ) ) {
+ result = astTestMaxOrder( this );
+
+/* MaxRes. */
+/* ------- */
+ } else if ( !strcmp( attrib, "maxres" ) ) {
+ result = astTestMaxOrder( this );
+
+/* MinOrder. */
+/* --------- */
+ } else if ( !strcmp( attrib, "minorder" ) ) {
+ result = astTestMinOrder( this );
+
+/* MinRes. */
+/* ------- */
+ } else if ( !strcmp( attrib, "minres" ) ) {
+ result = astTestMinOrder( 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, "moctype" ) ||
+ !strcmp( attrib, "moclength" ) ||
+ !strcmp( attrib, "mocarea" ) ) {
+ 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 TestCell( AstMoc *this, int order, int64_t npix, int parent,
+ int *status ) {
+/*
+*++
+* Name:
+c astTestCell
+f AST_TESTCELL
+
+* Purpose:
+* Tests if a single HEALPix cell is included in a Moc.
+
+* Type:
+* Public virtual function.
+
+* Synopsis:
+c #include "moc.h"
+c void astTestCell( AstMoc *this, int order, int64_t npix, int parent )
+f RESULT = AST_TESTCELL( THIS, ORDER, NPIX, PARENT, STATUS )
+
+* Class Membership:
+* Moc method.
+
+* Description:
+* This function returns
+c a non-zero value
+f .TRUE.
+* if the Moc includes the specified cell.
+
+* Parameters:
+c this
+f THIS = INTEGER (Given)
+* Pointer to the Moc to be modified.
+c order
+f ORDER = INTEGER (Given)
+* The HEALPix order of the cell to test.
+c Zero
+f .FALSE.
+* is returned if this is higher than the maximum order allowed in
+* the Moc (as given by its MaxOrder attribute).
+c npix
+f NPIX = INTEGER*8 (Given)
+* The "npix" value identifying the cell to test (see the MOC
+* recommendation for more details).
+c parent
+f PARENT = LOGICAL (Given)
+* Indicates the value to return if the tested cell is not included
+* at the specified order, but a parent cell (at a lower order) is
+* included.
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Returned Value:
+c astTestCell()
+c One if the specified cell is included in the Moc at the
+c specified order, of (if "parent" is non-zero) a parent cell is
+c included in the Moc. Zero otherwise.
+f AST_TESTCELL = LOGICAL
+f .TRUE. if the specified cell is included in the Moc at the
+f specified order, of (if PARENT is non-zero) a parent cell is
+f included in the Moc. .FALSE. otherwise.
+
+*--
+*/
+
+/* Local Variables: */
+ int irange;
+ int maxorder;
+ int shift;
+ int64_t *pr;
+ int64_t ihigh;
+ int64_t ilow;
+ int result;
+
+/* Initialise */
+ result = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return 0;
+
+/* Validate */
+ if( order < 0 || order > AST__MXORDHPX ) {
+ astError( AST__INVAR, "astTestCell(%s): Invalid value (%d) "
+ "supplied for parameter 'order' - must be no greater "
+ "than %d.", status, astGetClass( this ), order,
+ AST__MXORDHPX );
+
+ } else if( npix < 0 || npix > MaxNpix( order ) ) {
+ astError( AST__INVAR, "astTestCell(%s): Invalid value (%zu) "
+ "supplied for parameter 'npix' - must be greater "
+ "than 0 and less than %zu.", status, astGetClass( this ),
+ npix, MaxNpix( order ) + 1 );
+ }
+
+/* If the specified order is greater than the MaxOrder attribute, return
+ zero. */
+ maxorder = astGetMaxOrder( this );
+ if( order <= maxorder && astOK ) {
+
+/* Get the upper and lower bounds of the range of cells at MaxOrder
+ that corresponds to the specified cell. */
+ shift = 2*( maxorder - order );
+ ilow = ( npix << shift );
+ ihigh = ( (npix + 1 ) << shift ) - 1;
+
+/* See if this range of cells is included in the Moc. */
+ pr = this->range;
+ for( irange = 0; irange < this->nrange; irange++, pr += 2 ) {
+ if( pr[ 0 ] <= ilow && pr[ 1 ] >= ihigh ) {
+ result = 1;
+ break;
+ }
+ }
+
+/* If it is included, it may be that the entire parent cell is included.
+ We should return non-zero for this only if "parent" is non-zero. So
+ if parent is zero, check that the entire parent is NOT included in
+ the MOC. */
+ if( result && !parent ) {
+
+/* Get the npix value for the immediate parent cell at order (order-1). */
+ npix = ( npix >> 2 );
+
+/* Get the upper and lower bounds of the range of cells at MaxOrder
+ that corresponds to the immediate parent cell. */
+ shift += 2;
+ ilow = ( npix << shift );
+ ihigh = ( (npix + 1 ) << shift ) - 1;
+
+/* See if this range of cells is included in the Moc. If so return zero. */
+ pr = this->range;
+ for( irange = 0; irange < this->nrange; irange++, pr += 2 ) {
+ if( pr[ 0 ] <= ilow && pr[ 1 ] >= ihigh ) {
+ result = 0;
+ break;
+ }
+ }
+ }
+ }
+
+/* Return the result. */
+ return result;
+}
+
+
+
+#define MAKE_TESTOP(test) \
+\
+/* First handle cases where a bad value is defined. */ \
+ if( bad ) { \
+\
+/* Loop round all positions to be tested. */ \
+ for( ipos = 0; ipos < *npos; ipos++,px++,py++) { \
+ inside[ ipos ] = 0; \
+\
+/* Get the 1-based grid indices of the pixel containing the position. */ \
+ if( *px != AST__BAD && *py != AST__BAD ) { \
+ ix = (int)( *px + 0.5 ); \
+ iy = (int)( *py + 0.5 ); \
+\
+/* Check that the position is within the array. */ \
+ if( ix > 0 && ix <= nx && \
+ iy > 0 && iy <= ny ){ \
+\
+/* Get the vector index of the required array element. */ \
+ ii = ( ix - 1 ) + ( iy - 1 )*nx; \
+\
+/* If it passes the selection test, set the returned flag to zero. */ \
+ inside[ ipos ] = ( p[ii] != *bad && ( test) ); \
+ } \
+ } \
+ } \
+\
+/* Now handle cases where no bad value is defined. */ \
+ } else { \
+\
+/* Loop round all positions to be tested. */ \
+ for( ipos = 0; ipos < *npos; ipos++,px++,py++ ) { \
+ inside[ ipos ] = 0; \
+\
+/* Get the 1-based grid indices of the pixel containing the position. */ \
+ if( *px != AST__BAD && *py != AST__BAD ) { \
+ ix = (int)( *px + 0.5 ); \
+ iy = (int)( *py + 0.5 ); \
+\
+/* Check that the position is within the array. */ \
+ if( ix > 0 && ix <= nx && \
+ iy > 0 && iy <= ny ){ \
+\
+/* Get the vector index of the required array element. */ \
+ ii = ( ix - 1 ) + ( iy - 1 )*nx; \
+\
+/* If it passes the selection test, set the returned flag to zero. */ \
+ inside[ ipos ] = ( test ); \
+ } \
+ } \
+ } \
+ }
+
+#define MAKE_TESTTYPE(Xtype) \
+\
+ const Xtype *p = (Xtype *) pixelmask->data; \
+ Xtype *v = (Xtype *) pixelmask->value; \
+ Xtype *bad = (Xtype *) pixelmask->bad; \
+ \
+/* Do each type of test. */ \
+ if( pixelmask->oper == AST__LT ) { \
+ MAKE_TESTOP( p[ii] < *v ); \
+ } else if( pixelmask->oper == AST__LE ) { \
+ MAKE_TESTOP( p[ii] <= *v ); \
+ } else if( pixelmask->oper == AST__EQ ) { \
+ MAKE_TESTOP( p[ii] == *v ); \
+ } else if( pixelmask->oper == AST__NE ) { \
+ MAKE_TESTOP( p[ii] != *v ); \
+ } else if( pixelmask->oper == AST__GE ) { \
+ MAKE_TESTOP( p[ii] >= *v ); \
+ } else if( pixelmask->oper == AST__GT ) { \
+ MAKE_TESTOP( p[ii] > *v ); \
+ }
+
+
+static void TestPixels( PixelMask *pixelmask, int *npos, AstPointSet *ps,
+ int inside[ 9 ], int *status ){
+/*
+* Name:
+* TestPixels
+
+* Purpose:
+* Test if a set of positions are inside a pixel array.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* void TestPixels( PixelMask *pixelmask, int *npos, AstPointSet *ps,
+* int inside[ 9 ], int *status )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function returns flags indicating which of the supplied positions
+* correspond to selected pixels in the supplied pixel array.
+
+* Parameters:
+* pixelmask
+* Structure defining the pixel mask.
+* npos
+* Supplied holding the number of positions in the supplied PointSet.
+* Returned holding the number of usable values in the "inside"
+* array. The numer of positions, together with their order, is
+* defined by the "PutCell" function.
+* ps
+* PointSet holding the 2D positions to be tested, in grid coords.
+* inside
+* Pointer to an array of 9 ints that are returned non-zero if
+* the corresponding position is inside the region of interest.
+* They are stored in raster order from bottom left to top right.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ double **ptr;
+ double *px;
+ double *py;
+ int ipos;
+ int ix;
+ int iy;
+ int nx;
+ int ny;
+ size_t ii;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get pointers to the arrays of X and Y grid coordinate values. */
+ ptr = astGetPoints( ps );
+ if( astOK ) {
+ px = ptr[ 0 ];
+ py = ptr[ 1 ];
+
+/* Store convenience values */
+ nx = pixelmask->nx;
+ ny = pixelmask->ny;
+
+/* Do each data type in turn. This stores "*npos" flags, corresponding to
+ the "*npos" positions in "ps", at the start of the "inside" array. */
+ if( pixelmask->type == DOUBLE ){
+ MAKE_TESTTYPE(double)
+ } else if( pixelmask->type == LONG_INT ){
+ MAKE_TESTTYPE(long int)
+ } else if( pixelmask->type == UNSIGNED_LONG_INT ){
+ MAKE_TESTTYPE(unsigned long int)
+ } else if( pixelmask->type == INT ){
+ MAKE_TESTTYPE(int)
+ } else if( pixelmask->type == UNSIGNED_INT ){
+ MAKE_TESTTYPE(unsigned int)
+ } else if( pixelmask->type == SHORT_INT ){
+ MAKE_TESTTYPE(short int)
+ } else if( pixelmask->type == UNSIGNED_SHORT_INT ){
+ MAKE_TESTTYPE(unsigned short int)
+ } else if( pixelmask->type == SIGNED_CHAR ){
+ MAKE_TESTTYPE(signed char)
+ } else if( pixelmask->type == UNSIGNED_CHAR ){
+ MAKE_TESTTYPE(unsigned char)
+ } else if( pixelmask->type == FLOAT ){
+ MAKE_TESTTYPE(float)
+
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+ } else if( pixelmask->type == LONG_DOUBLE ){
+ MAKE_TESTTYPE(long double)
+#endif
+
+ } else {
+ astError( AST__INTER, "TestPixel(Moc): Unsupported data type %d "
+ "(internal programming error).", status, pixelmask->type );
+ }
+
+ }
+}
+
+#undef MAKE_TESTOP
+#undef MAKE_TESTTYPE
+
+static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in,
+ int forward, AstPointSet *out, int *status ) {
+/*
+* Name:
+* Transform
+
+* Purpose:
+* Apply a Moc to transform a set of points.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* AstPointSet *Transform( AstMapping *this, AstPointSet *in,
+* int forward, AstPointSet *out, int *status )
+
+* Class Membership:
+* Moc member function (over-rides the astTransform protected
+* method inherited from the Region class).
+
+* Description:
+* This function takes a Moc 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 Moc. Points inside
+* the Moc are copied unchanged from input to output.
+
+* Parameters:
+* this
+* Pointer to the Moc.
+* 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 Moc.
+* - 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 *map1; /* Sky->HPX12 grid coords Mapping */
+ AstMoc *this; /* Pointer to Moc */
+ AstPointSet *ps1; /* PointSet holding HPX12 grid coords */
+ AstPointSet *pset_tmp; /* Pointer to PointSet holding ICRS positions */
+ AstPointSet *result; /* Pointer to output PointSet */
+ double **ptr; /* Pointer to grid (x,y) coordinate data */
+ double **ptr_out; /* Pointer to output current Frame coordinate data */
+ double *px; /* Pointer to grid X values */
+ double *px_out; /* Pointer to output X values */
+ double *py; /* Pointer to grid Y values */
+ double *py_out; /* Pointer to output Y values */
+ int inside; /* Point inside Moc? */
+ int ipoint; /* Index of input point */
+ int irange; /* Index of current range */
+ int neg; /* Has the Region been negated? */
+ int npoint; /* No. of input points */
+ int order; /* HEALPix order used by the moc */
+ int64_t *pn; /* Pointer to range of nested index */
+ int64_t inest; /* Nested index of cell holding current position */
+
+/* Check the global error status. */
+ if ( !astOK ) return NULL;
+
+/* Obtain a pointer to the Moc structure. */
+ this = (AstMoc *) this_mapping;
+
+/* Apply the parent mapping using the stored pointer to the Transform member
+ function inherited from the parent Region class. This function validates
+ all parameters and generates an output PointSet if necessary,
+ containing a copy of the input PointSet. */
+ result = (*parent_transform)( this_mapping, in, forward, out, status );
+
+/* 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
+ Moc is defined - i.e. ICRS). 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( this, in, 0, NULL, NULL );
+
+/* Get the number of points to be transformed. */
+ npoint = astGetNpoint( pset_tmp );
+
+/* Get a pointer to the output axis values. */
+ ptr_out = astGetPoints( result );
+
+/* See if the Region has been negated. */
+ neg = astGetNegated( this );
+
+/* We will now extend the parent astTransform method by performing the
+ calculations needed to generate the output coordinate values. */
+
+/* Perform coordinate arithmetic. */
+/* ------------------------------ */
+ if ( astOK ) {
+
+/* Get the HEALPix order used by the Moc. */
+ order = astGetMaxOrder( this );
+
+/* Get a Mapping that goes from ICRS to grid coordinates in an HPX12
+ projection of the whole sky with the Moc's order. */
+ map1 = GetCachedMapping( this, order, "astTransform", status );
+
+/* Use this Mapping to convert all the ICRS positions to HPX12 grid
+ coords. Note the inverse and forward transformations of a Moc are
+ identical so we do not need to consider the value of the Invert
+ attribute. */
+ ps1 = astTransform( map1, pset_tmp, 1, NULL );
+
+/* Get pointers to the first X and Y grid coordinate values. */
+ ptr = astGetPoints( ps1 );
+ px = ptr[ 0 ];
+ py = ptr[ 1 ];
+
+/* Get pointers to the first output sky coordinate values. */
+ ptr_out = astGetPoints( result );
+ px_out = ptr_out[ 0 ];
+ py_out = ptr_out[ 1 ];
+
+/* Check all the positions. */
+ for( ipoint = 0; ipoint < npoint; ipoint++ ) {
+
+/* Convert from grid (x,y) to nested index. */
+ inest = XyToNested( order, (int)( *(px++) + 0.5 ),
+ (int)( *(py++) + 0.5 ) );
+
+/* Test if this nested index is contained in the Moc. Each pair of
+ adjacent values in the "this->range" array are the upper and lower
+ bounds of a range of nested index contained in the Moc. The ranges are
+ stored in ascending order. Loop until a range is found that ends
+ after or at the value of "inest". */
+ inside = 0;
+ pn = this->range;
+ for( irange = 0; irange < this->nrange; irange++ ) {
+ if( pn[ 1 ] >= inest ) {
+
+/* The current range ends at or after "inest". The current position is
+ inside the Moc if the current range starts at or before "inest". */
+ if( pn[ 0 ] <= inest ) inside = 1;
+
+/* No need to check any more ranges. */
+ break;
+ }
+ pn += 2;
+ }
+
+/* Negate the inside flag if the Region has been negated. */
+ if( neg ) inside = !inside;
+
+/* Set the output values bad if the current position is no inside the Moc. */
+ if( !inside ) {
+ *px_out = AST__BAD;
+ *py_out = AST__BAD;
+ }
+
+/* Move on to the next output position. */
+ px_out++;
+ py_out++;
+ }
+
+/* Free resources */
+ ps1 = astAnnul( ps1 );
+ }
+ pset_tmp = astAnnul( pset_tmp );
+
+/* Annul the result if an error has occurred. */
+ if( !astOK ) result = astAnnul( result );
+
+/* Return a pointer to the output PointSet. */
+ return result;
+}
+
+static int64_t XyToNested( int order, int ix, int iy ){
+/*
+* Name:
+* XyToNested
+
+* Purpose:
+* Get the HEALPix nested index of the cell at given HPX12 grid coords.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* int64_t XyToNested( int order, int ix, int iy )
+
+* Class Membership:
+* Moc member function
+
+* Description:
+* This function returns the HEALPix nested index of a cell with given
+* grid coords in the HPX12 projection.
+
+* Parameters:
+* order
+* The HEALPix order.
+* ix
+* The one-based grid X index of the cell within the HPX12
+* projection.
+* iy
+* The one-based grid Y index of the cell within the HPX12
+* projection.
+
+* Notes:
+* The (ix,iy) values give the grid coords within the all-sky projection
+* and so can be very large. For this reason, the AstMoc class
+* restricts the maximum order to 27, compared to the value of 29
+* included in the MOC recommendation. Using a value of 27 means that the
+* largest value of ix and iy will fit into a signed 4 byte integer.
+
+* Returned Value:
+* The HEALPix nested index, or int64_MAX if the supplied grid
+* indices do not correspond to a HEALPix cell.
+*/
+
+/* Local Variables: */
+ int fi;
+ int fx;
+ int fy;
+ int nppf;
+ int64_t tx;
+ int64_t ty;
+ int64_t result;
+
+/* Initialise the returned index to a "bad" value. */
+ result = INT64_MAX;
+
+/* Get the number of cells along one edge of a facet.*/
+ nppf = ( 1 << order );
+
+/* Get the zero based facet (x,y) indices - offsets from the bottom left
+ facet in Calabretta & Roukema 2005 Fig 3. Convert ix and iy from
+ one-based to zero-based before doing the calculations. */
+ fx = (--ix)/nppf;
+ fy = (--iy)/nppf;
+
+/* Get the (x,y) offsets of the cell into the facet, measured from the
+ bottom left corner of the facet as shown in Calabretta & Roukema 2005
+ Fig 3. Do this now before moving the top right facet to the bottom
+ left. */
+ tx = ix - nppf*fx;
+ ty = iy - nppf*fy;
+
+/* The top right facet is the same as the bottom left facet. */
+ if( fx == 4 && fy == 4 ) {
+ fx = 0;
+ fy = 0;
+ }
+
+/* Check that the facet indices are legal. */
+ if( fx >= 0 && fx < 5 && fy >= 0 && fy < 5 &&
+ fx >= fy - 1 && fx <= fy + 1 ) {
+
+/* Get the facet index in the range 0 - 11. The facet numbering scheme is
+ shown in Fig 5 of the HEALPix paper (Gorski et al 2005). For instance
+ (fx,fy)=(0,0) (bottom left facet in Calabretta & Roukema 2005 Fig 3) has
+ fi=4, and (fx,fy)=(2,2) (centre facet) has fi=6. Facet 4 is duplicated,
+ occupying at both the bottom left and the top right corners. */
+ if( fx != 4 || fy != 4 ) {
+ fi = ( fx + fy )/2 + ( fx - fy + 1 )*4;
+ } else {
+ fi = 4;
+ }
+
+/* Change tx so that it is measured in the opposite direction, starting
+ at the bottom right corner of the facet. */
+ tx = nppf - 1 - tx;
+
+/* Get the scalar index of the tile within the facet. Interleave the bits
+ of tx and ty (tx gives the even bits) and add this index onto
+ the index of the first tile in the facet, to get the index of the
+ required tile. Interleaving is performed using the "Binary
+ Magic Numbers" method with code based on the public domain
+ example (collection (C) 1997-2005 Sean Eron Anderson) available at:
+ http://graphics.stanford.edu/~seander/bithacks.html#InterleaveBMN */
+ tx = (tx | (tx << 16)) & 0x0000FFFF0000FFFF;
+ tx = (tx | (tx << 8)) & 0x00FF00FF00FF00FF;
+ tx = (tx | (tx << 4)) & 0x0F0F0F0F0F0F0F0F;
+ tx = (tx | (tx << 2)) & 0x3333333333333333;
+ tx = (tx | (tx << 1)) & 0x5555555555555555;
+
+ ty = (ty | (ty << 16)) & 0x0000FFFF0000FFFF;
+ ty = (ty | (ty << 8)) & 0x00FF00FF00FF00FF;
+ ty = (ty | (ty << 4)) & 0x0F0F0F0F0F0F0F0F;
+ ty = (ty | (ty << 2)) & 0x3333333333333333;
+ ty = (ty | (ty << 1)) & 0x5555555555555555;
+
+/* Combine them all together. to form the final index. */
+ result = ( (int64_t) fi << ( 2*order ) ) | ty | (tx << 1);
+ }
+
+/* Return the result */
+ 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:
+* MaxOrder
+
+* Purpose:
+* The highest HEALPix order used in the MOC.
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Integer.
+
+* Description:
+* This attribute gives the best resolution of the MOC expressed as a
+* HEALPix order in the range zero to 27 (this class does not support
+* orders greater than 27). It's value can only be set once (for
+* instance as an option when the Moc constructor is invoked). An
+* error will be reported if a subsequent attempt to set or clear the
+* attribute is made. If no value is supplied for MaxOrder before the
+* first area of sky is added to the empty Moc, then a default value
+* will be selected and set, depending on the method used to add the
+* first area to the Moc:
+
+c - astAddCell: the order of the specified cell.
+f - AST_ADDCELL: the order of the specified cell.
+*
+c - astAddRegion: if the added Region is a Moc, then the Moc's MaxOrder
+f - AST_ADDREGION: if the added Region is a Moc, then the Moc's MaxOrder
+* value is used, Otherwise the value used corresponds to
+* the resolution closest to 0.1% of the linear size of
+* the Region being added (determined using method
+c astGetRegionDisc).
+f AST_GETREGIONDISC).
+*
+c - astAddPixelMask<X>: the smallest order that results in the cells in
+f - AST_ADDPIXELMASK<X>: the smallest order that results in the cells in
+* the Moc being no larger than the size of the pixels in the centre of
+* the pixel mask.
+*
+c - astAddMocData: the largest order present in the supplied normalised
+f - AST_ADDMOCDATA: the largest order present in the supplied normalised
+* MOC data array.
+*
+c - astAddMocString: the largest order present in the supplied MOC.
+f - AST_ADDMOCString: the largest order present in the supplied MOC.
+*
+* A default value of -1 will be returned for the MaxOrder attribute
+* prior to its value being set.
+*
+* The MaxRes attribute is equivalent to MaxOrder but expresses the
+* resolution as a number of arc-seconds rather than as a HEALPix order.
+*
+* Increasing the HEALPix order by one roughly halves the resolution of the
+* Moc. For instance, a value of 18 corresponds to a resolution of about
+* 0.8 arc-second, and 19 corresponds to about 0.4 arc-seconds.
+
+* Applicability:
+* Moc
+* All Mocs have this attribute.
+
+*att--
+*/
+astMAKE_GET(Moc,MaxOrder,int,0,( ( this->maxorder != -INT_MAX ) ?
+ this->maxorder : -1 ))
+astMAKE_TEST(Moc,MaxOrder,( this->maxorder != -INT_MAX ))
+
+
+/*
+*att++
+* Name:
+* MinOrder
+
+* Purpose:
+* The lowest HEALPix order used in the MOC.
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Integer.
+
+* Description:
+* This attribute controls the size of the largest hole or island that
+* could be missed when adding Regions or pixel masks into a Moc using
+c methods astAddRegion or astAddPixelMask.
+f methods AST_ADDREGION or AST__ADDPIXELMASK.
+* It gives the resolution of the initial grid used to identify areas
+* that are inside or outside the Region or pixel mask, expressed as a
+* HEALPix order in the range zero to 27 (this class does not support
+* orders greater than 27). Unselected areas (i.e. bounded "holes" or
+* or "islands"in the selection) that are smaller than one cell of this
+* initial grid may be missed (i.e. such holes may be "filled in" and
+* islands omitted in the resulting Moc).
+*
+* The default value is (MaxOrder-4), with a lower limit of zero. For
+* instance, if MaxOrder is 16 (a resolution of 3.2 arc-seconds), then
+* MinOrder will be 12, meaning that bounded holes within selected areas
+* may be filled in if the hole is smaller than 51 arc-seconds. Increase
+* the value of this attribute to ensures that only holes smaller than
+* this value can be missed. Note, doing so will increase the time spent
+* creating the Moc.
+*
+* To ensure no pixels are missed, set MinOrder to some very large
+* value (larger than 27). If MinOrder is set greater than MaxOrder, the
+* value of MaxOrder will be used whenever MinOrder is required.
+*
+* The MinRes attribute is equivalent to MinOrder but expresses the
+* resolution as a number of arc-seconds rather than as a HEALPix order.
+* Any change made to MinOrder will cause a corresponding change in the
+* MinRes attribute.
+
+* Applicability:
+* Moc
+* All Mocs have this attribute.
+
+*att--
+*/
+astMAKE_CLEAR(Moc,MinOrder,minorder,-INT_MAX)
+astMAKE_GET(Moc,MinOrder,int,0,( ( this->minorder != -INT_MAX ) ?
+ this->minorder:astMAX(0,astGetMaxOrder(this)-5)))
+astMAKE_SET(Moc,MinOrder,int,minorder, astMIN(astMAX(value,0),AST__MXORDHPX))
+astMAKE_TEST(Moc,MinOrder,( this->minorder != -INT_MAX ))
+
+
+/*
+*att++
+* Name:
+* MaxRes
+
+* Purpose:
+* The best resolution of the MOC.
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Floating point, read-only.
+
+* Description:
+* This attribute is an alternative to the MaxOrder attribute and gives
+* the best resolution of the MOC expressed as a number of arc-seconds.
+* It can be set only when the Moc is constructed - an error is
+* reported if any subsequent attempt is made to set or clear the value
+* of MaxRes. See attribute MaxOrder for more details.
+*
+* A default value of zero will be returned for the MaxRes attribute
+* prior to its value (or the value of MaxOrder) being set.
+
+* Applicability:
+* Moc
+* All Mocs have this attribute.
+
+*att--
+*/
+
+/*
+*att++
+* Name:
+* MinRes
+
+* Purpose:
+* The worst resolution of the MOC.
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Floating point.
+
+* Description:
+* This attribute gives the poorest resolution of the MOC expressed as
+* a number of arc-seconds. When a new value is set for MinRes, the
+* MinOrder attribute will be set to the order that gives a resolution
+* closest to the requested resolution. When the current value of
+* MinRes is requested, the resolution corresponding to the current
+* value of MinOrder will be returned. When MinRes is tested or
+* cleared, the MinOrder attribute will be tested or cleared.
+
+* Applicability:
+* Moc
+* All Mocs have this attribute.
+
+*att--
+*/
+
+
+/*
+*att++
+* Name:
+* MocType
+
+* Purpose:
+* The data type used to describe a Moc in FITS
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Integer, read-only.
+
+* Description:
+* This read-only attribute gives the data type to be used when
+* writing out the Moc to a FITS binary table. The attribute takes the
+* value 4 or 8. The binary table should contain a single column of
+* signed integer values in which each integer has 4 or 8 bytes, as
+* indicated by the value of this attribute.
+
+* Applicability:
+* Moc
+* All Mocs have this attribute.
+
+*att--
+*/
+
+/*
+*att++
+* Name:
+* MocArea
+
+* Purpose:
+* The area covered by the Moc, in square arc-minutes.
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Floating point, read-only.
+
+* Description:
+* This read-only attribute gives the area covered by the Moc, in
+* square arc-minutes.
+
+* Applicability:
+* Moc
+* All Mocs have this attribute.
+
+*att--
+*/
+
+/*
+*att++
+* Name:
+* MocLength
+
+* Purpose:
+* The table length used to describe a Moc in FITS
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Integer, read-only.
+
+* Description:
+* This read-only attribute gives the length of the number of rows
+* needed to describe the Moc in a FITS binary table. This is the
+* number of cells in the normalised Moc.
+
+* Applicability:
+* Moc
+* All Mocs have this attribute.
+
+*att--
+*/
+
+
+
+
+/* Copy constructor. */
+/* ----------------- */
+static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
+/*
+* Name:
+* Copy
+
+* Purpose:
+* Copy constructor for Moc objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* void Copy( const AstObject *objin, AstObject *objout, int *status )
+
+* Description:
+* This function implements the copy constructor for Moc 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: */
+ AstMoc *out;
+ AstMoc *in;
+ int order;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain pointers to the input and output Mocs. */
+ in = (AstMoc *) objin;
+ out = (AstMoc *) objout;
+
+/* Ensure safe values. */
+ out->range = NULL;
+ out->knorm = NULL;
+ out->inorm = NULL;
+ out->basemesh = NULL;
+ out->meshdist = NULL;
+ out->unc = NULL;
+
+/* Copy dynamic arrays */
+ out->range = astStore( NULL, in->range, astSizeOf( in->range ) );
+ if( in->knorm ) out->knorm = astStore( NULL, in->knorm,
+ astSizeOf( in->knorm ));
+ if( in->inorm ) out->inorm = astStore( NULL, in->inorm,
+ astSizeOf( in->inorm ));
+ if( in->meshdist ) out->meshdist = astStore( NULL, in->meshdist,
+ astSizeOf( in->meshdist ));
+ if( in->unc ) out->unc = astCopy( in->unc );
+ if( in->basemesh ) out->basemesh = astCopy( in->basemesh );
+
+/* Ensure the output has no cached Mappings. These will be re-generated
+ when required. */
+ for( order = 0; order <= AST__MXORDHPX; order++ ) {
+ out->cached_maps[ order ] = NULL;
+ }
+}
+
+
+/* Destructor. */
+/* ----------- */
+static void Delete( AstObject *obj, int *status ) {
+/*
+* Name:
+* Delete
+
+* Purpose:
+* Destructor for Moc objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* void Delete( AstObject *obj, int *status )
+
+* Description:
+* This function implements the destructor for Moc 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: */
+ AstMoc *this;
+ int order;
+
+/* Obtain a pointer to the Moc structure. */
+ this = (AstMoc *) obj;
+
+/* Free all resources. */
+ this->range = astFree( this->range );
+ this->knorm = astFree( this->knorm );
+ this->inorm = astFree( this->inorm );
+ this->meshdist = astFree( this->meshdist );
+
+ for( order = 0; order <= AST__MXORDHPX; order++ ) {
+ if( this->cached_maps[ order ] ) this->cached_maps[ order ] = astAnnul( this->cached_maps[ order ] );
+ }
+
+ if( this->unc ) this->unc = astAnnul( this->unc );
+ if( this->basemesh ) this->basemesh = astAnnul( this->basemesh );
+}
+
+/* Dump function. */
+/* -------------- */
+static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
+/*
+* Name:
+* Dump
+
+* Purpose:
+* Dump function for Moc 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 Moc class to an output Channel.
+
+* Parameters:
+* this
+* Pointer to the Moc 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: */
+ AstMoc *this; /* Pointer to the Moc structure */
+ char name[50]; /* Name for output item */
+ int irange; /* Index of current range */
+ int ival; /* Integer attribute value */
+ int set; /* Attribute value set? */
+ int64_t *pr; /* Pointer to next range's bound value */
+
+/* A union used to convert an signed 8 byte integer into two 4 byte
+ integers. */
+ union {
+ int a[2];
+ int64_t b;
+ } un;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the Moc structure. */
+ this = (AstMoc *) this_object;
+
+/* Write out values representing the instance variables for the
+ Moc 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. */
+
+/* MaxOrder. */
+/* --------- */
+/* Write out the MaxOrder attribute. */
+ set = TestMaxOrder( this, status );
+ ival = set ? GetMaxOrder( this, status ) : astGetMaxOrder( this );
+ astWriteInt( channel, "MaxOrder", set, 0, ival, "Maximum HEALPix order" );
+
+/* MinOrder. */
+/* --------- */
+/* Write out the MinOrder attribute. */
+ set = TestMinOrder( this, status );
+ ival = set ? GetMinOrder( this, status ) : astGetMinOrder( this );
+ astWriteInt( channel, "MinOrder", set, 0, ival, "Minimum HEALPix order" );
+
+/* Number of ranges. */
+/* ----------------- */
+ astWriteInt( channel, "NRange", 1, 0, this->nrange, "Number of ranges" );
+
+/* The ranges of nested indices in the MOC. */
+/* ---------------------------------------- */
+/* The Channel class does not have support for 64 bit ints, so each 64 bit
+ int is written out as two 4 byte ints. Each is only written out if it
+ is non-zero. Only write out the upper bound if it is not the same as
+ the lower bound. */
+ pr = this->range;
+ for( irange = 0; irange < this->nrange; irange++, pr += 2 ) {
+ un.b = pr[0];
+ if( un.a[0] ) {
+ sprintf( name, "Lba%d", irange );
+ astWriteInt( channel, name, 1, 0, un.a[0], "" );
+ }
+ if( un.a[1] ) {
+ sprintf( name, "Lbb%d", irange );
+ astWriteInt( channel, name, 1, 0, un.a[1], "" );
+ }
+
+ if( pr[1] != pr[0] ) {
+ un.b = pr[1];
+ if( un.a[0] ) {
+ sprintf( name, "Uba%d", irange );
+ astWriteInt( channel, name, 1, 0, un.a[0], "" );
+ }
+ if( un.a[1] ) {
+ sprintf( name, "Ubb%d", irange );
+ astWriteInt( channel, name, 1, 0, un.a[1], "" );
+ }
+ }
+ }
+
+}
+
+/* Standard class functions. */
+/* ========================= */
+/* Implement the astIsAMoc and astCheckMoc functions using the macros
+ defined for this purpose in the "object.h" header file. */
+astMAKE_ISA(Moc,Region)
+astMAKE_CHECK(Moc)
+
+AstMoc *astMoc_( const char *options, int *status, ...) {
+/*
+*++
+* Name:
+c astMoc
+f AST_MOC
+
+* Purpose:
+* Create a Moc.
+
+* Type:
+* Public function.
+
+* Synopsis:
+c #include "moc.h"
+c AstMoc *astMoc( const char *options, ... )
+f RESULT = AST_MOC( OPTIONS, STATUS )
+
+* Class Membership:
+* Moc constructor.
+
+* Description:
+* This function creates a new Moc object and optionally initialises
+* its attributes.
+*
+* The Moc class uses the IVOA MOC (Multi-Order Coverage) recommendation
+* to describe a region on the sky. The region is made up of an
+* arbitrary collection of cells from the HEALPix sky tessellation,
+* and thus may represent any area on the sky, subject to the
+* constraint that the edges of the area correspond to edges of the
+* HEALPix cells. See the MOC recommendation for further information
+* (http://www.ivoa.net/documents/MOC/).
+*
+* As a description of a region on the sky, the Moc class can be seen
+* as an alternative to the Region class. Note, Mocs and Regions
+* are not interchangable (that is, a Moc is not a subclass of Region
+* and therefore Region methods cannot be applied to Mocs). The Moc
+* class is intended to describe an arbitrary collection of cells on
+* the sky, whereas the Region classes describe exact geometric shapes.
+* The Moc class has a method that allow a Region to be converted into
+* an approximating Moc, but Mocs cannot be converted into Regions.
+*
+* The MOC recommendation requires that a MOC always describes a sky
+* area using the ICRS coordinate system. However, the Moc class
+* allows its attributes to be changed so that it represents any
+* celestial coordinate system that can be mapped to ICRS. Note,
+* changing the System attribute will not change the area on the
+* sky covered by the Moc - it will just change the way that area is
+* described. For instance, if a Moc is created that covers a particular
+* galaxy in ICRS, and the System attriubute is then changed to Galactic,
+* the Moc will still cover the same galaxy, but it will now be described
+* in Galactic coordinates rather than ICRS. When a Moc is written out
+* through a FitsChan, FITS headers describing the Moc will be stored
+* in the FitsChan. The binary data for the single column of the
+* coresponding FITS binary table can be retrieved from the Moc using
+c method astGetMocData.
+f method AST_GETMOCDATA.
+*
+* In practice, to use this class an empty Moc object (i.e. a Moc
+* describing a null area of the sky) should first be created using the
+c astMoc
+f AST_MOC
+* constructor. Areas of the sky should then be added into the empty
+* Moc using one or more of the class methods.
+
+* Parameters:
+c maxorder
+f MAXORDER = INTEGER (Given)
+*
+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 Moc. 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 Moc. 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 parameters 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 astMoc()
+f AST_MOC = INTEGER
+* A pointer to the new Moc.
+
+* 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 */
+ AstMoc *new; /* Pointer to new Moc */
+ va_list args; /* Variable parameter list */
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* Initialise the Moc, allocating memory and initialising the
+ virtual function table as well if necessary. */
+ new = astInitMoc( NULL, sizeof( AstMoc ), !class_init,
+ &class_vtab, "Moc" );
+
+/* If successful, note that the virtual function table has been
+ initialised. */
+ if ( astOK ) {
+ class_init = 1;
+
+/* Obtain the variable parameter list and pass it along with the options string
+ to the astVSet method to initialise the new Moc'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 Moc. */
+ return new;
+}
+
+AstMoc *astMocId_( const char *options, ... ) {
+/*
+* Name:
+* astMocId_
+
+* Purpose:
+* Create a Moc.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "moc.h"
+* AstMoc *astMocId_( const char *options, ... )
+
+* Class Membership:
+* Moc constructor.
+
+* Description:
+* This function implements the external (public) interface to the
+* astMoc constructor function. It returns an ID value (instead
+* of a true C pointer) to external users, and must be provided
+* because astMoc_ has a variable parameter list which cannot be
+* encapsulated in a macro (where this conversion would otherwise
+* occur).
+*
+* The variable parameter list also prevents this function from
+* invoking astMoc_ 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 astMoc_.
+
+* Returned Value:
+* The ID value associated with the new Moc.
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstMoc *new; /* Pointer to new Moc */
+ va_list args; /* Variable parameter 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 Moc, allocating memory and initialising the
+ virtual function table as well if necessary. */
+ new = astInitMoc( NULL, sizeof( AstMoc ), !class_init,
+ &class_vtab, "Moc" );
+
+/* If successful, note that the virtual function table has been
+ initialised. */
+ if ( astOK ) {
+ class_init = 1;
+
+/* Obtain the variable parameter list and pass it along with the options string
+ to the astVSet method to initialise the new Moc'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 Moc. */
+ return astMakeId( new );
+}
+
+AstMoc *astInitMoc_( void *mem, size_t size, int init, AstMocVtab *vtab,
+ const char *name, int *status ) {
+/*
+*+
+* Name:
+* astInitMoc
+
+* Purpose:
+* Initialise a Moc.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "moc.h"
+* AstMoc *astInitMoc( void *mem, size_t size, int init, AstMocVtab *vtab,
+* const char *name )
+
+* Class Membership:
+* Moc initialiser.
+
+* Description:
+* This function is provided for use by class implementations to initialise
+* a new Moc object. It allocates memory (if necessary) to accommodate
+* the Moc plus any additional data associated with the derived class.
+* It then initialises a Moc 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 Moc at the start of the memory passed via the
+* "vtab" parameter.
+
+* Parameters:
+* mem
+* A pointer to the memory in which the Moc is to be initialised.
+* This must be of sufficient size to accommodate the Moc data
+* (sizeof(Moc)) 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 Moc (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 Moc
+* structure, so a valid value must be supplied even if not required for
+* allocating memory.
+* init
+* A logical flag indicating if the Moc'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 Moc.
+* 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).
+
+* Returned Value:
+* A pointer to the new Moc.
+
+* 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: */
+ AstMoc *new;
+ AstSkyFrame *sf;
+ int order;
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* If necessary, initialise the virtual function table. */
+ if ( init ) astInitMocVtab( vtab, name );
+
+/* Initialise. */
+ new = NULL;
+
+/* Create an ICRS SkyFrame to be the Frame in which the Region is
+ defined. */
+ sf = astSkyFrame( "System=ICRS", status );
+
+/* Initialise a Region structure (the parent class) as the first component
+ within the Moc structure, allocating memory if necessary. */
+ new = (AstMoc *) astInitRegion( mem, size, 0, (AstRegionVtab *) vtab,
+ name, sf, NULL, NULL );
+ if ( astOK ) {
+
+/* Initialise the Moc data. */
+/* ------------------------------ */
+ new->unc = NULL;
+ new->basemesh = NULL;
+ new->knorm = NULL;
+ new->inorm = NULL;
+ new->moclength = 0;
+ new->mocarea = AST__BAD;
+ new->nrange = 0;
+ new->meshdist = NULL;
+ new->maxorder = -INT_MAX;
+ new->minorder = -INT_MAX;
+ new->lbnd[ 0 ] = AST__BAD;
+ new->lbnd[ 1 ] = AST__BAD;
+ new->ubnd[ 0 ] = AST__BAD;
+ new->ubnd[ 1 ] = AST__BAD;
+
+/* An array of cached Mappings, one for each HEALPix order. Each
+ Mapping goes from ICRS to 1-based grid indices in an HPX12
+ projection at the corresponding order. These Mappings are
+ created as they are needed. */
+ for( order = 0; order <= AST__MXORDHPX; order++ ) {
+ new->cached_maps[ order ] = NULL;
+ }
+
+/* If an error occurred, clean up by deleting the new Moc. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Free the SkyFrame */
+ sf = astAnnul( sf );
+
+/* Return a pointer to the new Moc. */
+ return new;
+}
+
+AstMoc *astLoadMoc_( void *mem, size_t size, AstMocVtab *vtab,
+ const char *name, AstChannel *channel, int *status ) {
+/*
+*+
+* Name:
+* astLoadMoc
+
+* Purpose:
+* Load a Moc.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "moc.h"
+* AstMoc *astLoadMoc( void *mem, size_t size, AstMocVtab *vtab,
+* const char *name, AstChannel *channel )
+
+* Class Membership:
+* Moc loader.
+
+* Description:
+* This function is provided to load a new Moc 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
+* Moc 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 Moc at the start of the memory
+* passed via the "vtab" parameter.
+
+* Parameters:
+* mem
+* A pointer to the memory into which the Moc is to be
+* loaded. This must be of sufficient size to accommodate the
+* Moc data (sizeof(Moc)) 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 Moc (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 Moc 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(AstMoc) is used instead.
+* vtab
+* Pointer to the start of the virtual function table to be
+* associated with the new Moc. If this is NULL, a pointer
+* to the (static) virtual function table for the Moc 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 "Moc" is used instead.
+
+* Returned Value:
+* A pointer to the new Moc.
+
+* 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: */
+ AstMoc *new; /* Pointer to the new Moc */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ char buff[50]; /* Buffer for item name */
+ int irange; /* The index of the current range */
+ int order; /* HEALPix order */
+ int64_t *pr; /* Pointer to next range */
+ int64_t llast; /* Lower bounds of last range */
+ int64_t ulast; /* Upper bounds of last range */
+ int64_t max_nest; /* No of cells in whole sky */
+
+/* A union used to convert an signed 8 byte integer into two 4 byte
+ integers. */
+ union {
+ int a[2];
+ int64_t b;
+ } un;
+
+/* 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 Moc. In this case the
+ Moc belongs to this class, so supply appropriate values to be
+ passed to the parent class loader (and its parent, etc.). */
+ if ( !vtab ) {
+ size = sizeof( AstMoc );
+ vtab = &class_vtab;
+ name = "Moc";
+
+/* If required, initialise the virtual function table for this class. */
+ if ( !class_init ) {
+ astInitMocVtab( 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 Moc. */
+ 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, "Moc" );
+
+/* 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. */
+ new->maxorder = astReadInt( channel, "maxorder", -INT_MAX );
+ if ( TestMaxOrder( new, status ) ) SetMaxOrder( new, new->maxorder, status );
+
+ new->minorder = astReadInt( channel, "minorder", -INT_MAX );
+ if ( TestMinOrder( new, status ) ) SetMinOrder( new, new->minorder, status );
+
+/* Get the maximum number of nested indices at the order in use. */
+ max_nest = 12*( 1L << (2*new->maxorder) );
+
+/* Initialise other class properties. */
+ new->nrange = astReadInt( channel, "nrange", 0 );
+ new->range = astMalloc( 2*new->nrange*sizeof(*(new->range)) );
+ if( astOK ) {
+ llast = 0;
+ ulast = 0;
+
+ pr = new->range;
+ for( irange = 0; irange < new->nrange; irange++,pr += 2 ) {
+ (void) sprintf( buff, "lba%d", irange );
+ un.a[0] = astReadInt( channel, buff, 0 );
+ (void) sprintf( buff, "lbb%d", irange );
+ un.a[1] = astReadInt( channel, buff, 0 );
+ pr[ 0 ] = un.b;
+
+ (void) sprintf( buff, "uba%d", irange );
+ un.a[0] = astReadInt( channel, buff, 0 );
+ (void) sprintf( buff, "ubb%d", irange );
+ un.a[1] = astReadInt( channel, buff, 0 );
+ pr[ 1 ] = ( un.b > 0 ) ? un.b : pr[ 0 ];
+
+ if( pr[ 1 ] >= max_nest ) {
+ astError( AST__LDERR, "astLoadMoc(Moc): Ill-formed Moc.",
+ status );
+ astError( AST__LDERR, "Upper bound (%zu) is too high for "
+ "order %d.", status, pr[ 1 ], new->maxorder );
+ break;
+
+ } else if( pr[ 0 ] > pr[ 1 ] ) {
+ astError( AST__LDERR, "astLoadMoc(Moc): Ill-formed Moc.",
+ status );
+ astError( AST__LDERR, "Upper bound (%zu) lower than lower "
+ "bound (%zu).", status, pr[ 1 ], pr[ 0 ] );
+ break;
+
+ } else if( pr[ 0 ] <= ulast && irange != 0 ) {
+ astError( AST__LDERR, "astLoadMoc(Moc): Ill-formed Moc.",
+ status );
+ astError( AST__LDERR, "Range [%zu:%zu] overlaps range "
+ "[%zu:%zu].", status, pr[ 0 ], pr[ 1 ], llast,
+ ulast );
+ break;
+ }
+
+ llast = pr[ 0 ];
+ ulast = pr[ 1 ];
+ }
+ }
+
+/* Initialise other values */
+ new->unc = NULL;
+ new->basemesh = NULL;
+ new->inorm = NULL;
+ new->knorm = NULL;
+ new->moclength = 0;
+ new->mocarea = AST__BAD;
+ new->meshdist = NULL;
+ new->lbnd[ 0 ] = AST__BAD;
+ new->lbnd[ 1 ] = AST__BAD;
+ new->ubnd[ 0 ] = AST__BAD;
+ new->ubnd[ 1 ] = AST__BAD;
+
+/* An array of cached Mappings, one for each HEALPix order. Each
+ Mapping goes from ICRS to 1-based grid indices in an HPX12
+ projection at the corresponding order. These Mappings are
+ created as they are needed. */
+ for( order = 0; order <= AST__MXORDHPX; order++ ) {
+ new->cached_maps[ order ] = NULL;
+ }
+
+/* If an error occurred, clean up by deleting the new Moc. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return the new Moc 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 astAddRegion_( AstMoc *this, int cmode, AstRegion *region, int *status ){
+ if ( !astOK ) return;
+ (**astMEMBER(this,Moc,AddRegion))( this, cmode, region, status );
+}
+
+int astGetMocType_( AstMoc *this, int *status ){
+ if ( !astOK ) return 4;
+ return (**astMEMBER(this,Moc,GetMocType))( this, status );
+}
+
+int astGetMocLength_( AstMoc *this, int *status ){
+ if ( !astOK ) return 0;
+ return (**astMEMBER(this,Moc,GetMocLength))( this, status );
+}
+
+double astGetMocArea_( AstMoc *this, int *status ) {
+ if ( !astOK ) return 0.0;
+ return (**astMEMBER(this,Moc,GetMocArea))( this, status );
+}
+
+AstFitsChan *astGetMocHeader_( AstMoc *this, int *status ) {
+ if ( !astOK ) return NULL;
+ return (**astMEMBER(this,Moc,GetMocHeader))( this, status );
+}
+
+void astGetMocData_( AstMoc *this, size_t mxsize, void *array, int *status ) {
+ if ( !astOK ) return;
+ (**astMEMBER(this,Moc,GetMocData))( this, mxsize, array, status );
+}
+
+void astGetMocString_( AstMoc *this, int json, size_t mxsize, char *string,
+ size_t *size, int *status ){
+ if ( !astOK ) return;
+ (**astMEMBER(this,Moc,GetMocString))( this, json, mxsize, string, size,
+ status );
+}
+
+void astSetMaxOrder_( AstMoc *this, int value, int *status ){
+ if ( !astOK ) return;
+ (**astMEMBER(this,Moc,SetMaxOrder))( this, value, status );
+}
+
+void astClearMaxOrder_( AstMoc *this, int *status ){
+ if ( !astOK ) return;
+ (**astMEMBER(this,Moc,ClearMaxOrder))( this, status );
+}
+
+void astAddMocData_( AstMoc *this, int cmode, int negate, int maxorder,
+ int len, int nbyte, const void *data, int *status ) {
+ if ( !astOK ) return;
+ (**astMEMBER(this,Moc,AddMocData))( this, cmode, negate, maxorder, len,
+ nbyte, data, status );
+}
+
+void astAddMocString_( AstMoc *this, int cmode, int negate, int maxorder,
+ size_t len, const char *string, int *json, int *status ) {
+ if ( !astOK ) return;
+ (**astMEMBER(this,Moc,AddMocString))( this, cmode, negate, maxorder, len,
+ string, json, status );
+}
+
+void astAddCell_( AstMoc *this, int cmode, int order, int64_t npix, int *status ) {
+ if ( !astOK ) return;
+ (**astMEMBER(this,Moc,AddCell))( this, cmode, order, npix, status );
+}
+
+int astTestCell_( AstMoc *this, int order, int64_t npix, int parents, int *status ) {
+ if ( !astOK ) return 0;
+ return (**astMEMBER(this,Moc,TestCell))( this, order, npix, parents, status );
+}
+
+void astGetCell_( AstMoc *this, int icell, int *order, int64_t *npix,
+ int *status ) {
+ if ( !astOK ) return;
+ return (**astMEMBER(this,Moc,GetCell))( this, icell, order, npix, status );
+}
+
+
+
+#define MAKE_ADDPIXELMASK_(X,Xtype) \
+void astAddPixelMask##X##_( AstMoc *this, int cmode, AstFrameSet *wcs, Xtype value, \
+ int oper, int flags, Xtype badval, const Xtype array[], \
+ const int dims[2], int *status ) { \
+ if ( !astOK ) return; \
+ (**astMEMBER(this,Moc,AddPixelMask##X))( this, cmode, wcs, value, oper, \
+ flags, badval, array, dims, status ); \
+}
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+MAKE_ADDPIXELMASK_(LD,long double)
+#endif
+MAKE_ADDPIXELMASK_(D,double)
+MAKE_ADDPIXELMASK_(F,float)
+MAKE_ADDPIXELMASK_(L,long int)
+MAKE_ADDPIXELMASK_(UL,unsigned long int)
+MAKE_ADDPIXELMASK_(I,int)
+MAKE_ADDPIXELMASK_(UI,unsigned int)
+MAKE_ADDPIXELMASK_(S,short int)
+MAKE_ADDPIXELMASK_(US,unsigned short int)
+MAKE_ADDPIXELMASK_(B,signed char)
+MAKE_ADDPIXELMASK_(UB,unsigned char)
+#undef MAKE_ADDPIXELMASK_
+
+
+
+
+
+/* For debugging of astRegBaseMesh and astRegTrace......
+
+static void dump_corner( Corner *this, int order ) {
+ static FILE *fd = NULL;
+ int i;
+ double alpha, beta;
+
+ if( !fd ) {
+ fd = fopen( "corners.asc", "w" );
+ fprintf( fd, "# this ra dec alpha beta order int dist prev cell0 cell1 cell2 cell3\n");
+
+ }
+
+ alpha = cos( this->dec )*cos( this->ra );
+ beta = cos( this->dec )*sin( this->ra );
+
+ fprintf( fd, "%p %g %g %g %g %d %d %d %p ", this, this->ra, this->dec,
+ alpha, beta, order, this->interior, this->dist, this->prev );
+ for( i = 0; i < this->ncell; i++ ) fprintf(fd, "%p ", this->cells[i] );
+ for( ; i < 4; i++ ) fprintf( fd, "null " );
+ fprintf(fd, "\n");
+
+}
+
+static void dump_cell( AstMoc *this, Cell *cell, int order ) {
+ int status_value;
+ int *old_status;
+ int *status;
+ double x, y, ra, dec, alpha, beta;
+ static FILE *fd = NULL;
+
+ status_value = 0;
+ status = &status_value;
+ old_status = astWatch( status );
+
+ if( !fd ) {
+ fd = fopen( "cells.asc", "w" );
+ fprintf( fd, "# this x18 y18 ix iy ra dec alpha beta order int prev bl tl tr br\n");
+
+ }
+
+ AstMapping *map = GetCachedMapping( this, order, "dump_cell",
+ status );
+
+ x = cell->ix;
+ y = cell->iy;
+ astTran2( map, 1, &x, &y, 0, &ra, &dec );
+ alpha = cos( dec )*cos( ra );
+ beta = cos( dec )*sin( ra );
+
+
+ double x18 = ( 1L << (18 - order) )*( cell->ix - 0.5 ) + 0.5;
+ double y18 = ( 1L << (18 - order) )*( cell->iy - 0.5 ) + 0.5;
+
+ fprintf( fd, "%p %g %g %d %d %g %g %g %g %d %d %p ", cell, x18, y18, cell->ix, cell->iy,
+ ra, dec, alpha, beta, order, cell->interior, cell->prev );
+
+ if( cell->bl ) {
+ fprintf(fd, "%p ", cell->bl );
+ } else {
+ fprintf(fd, "null " );
+ }
+
+ if( cell->tl ) {
+ fprintf(fd, "%p ", cell->tl );
+ } else {
+ fprintf(fd, "null " );
+ }
+
+ if( cell->tr ) {
+ fprintf(fd, "%p ", cell->tr );
+ } else {
+ fprintf(fd, "null " );
+ }
+
+ if( cell->br ) {
+ fprintf(fd, "%p ", cell->br );
+ } else {
+ fprintf(fd, "null " );
+ }
+
+ fprintf(fd, "\n");
+
+ astWatch( old_status );
+
+}
+
+
+static void dump_moc( AstMoc *this, const char *fname, int *status ) {
+ double *px, *py, *pa, *pb;
+ FILE *fd;
+ int64_t npix, *pr;
+ int npoint, maxorder, irange, ix, iy;
+ AstMapping *map;
+ AstPointSet *ps, *ps2;
+ double **ptr, **ptr2, alpha, beta;
+
+ if( !astOK ) return;
+
+ fd = fopen( fname, "w" );
+ fprintf( fd, "# ix iy ra dec alpha beta\n");
+
+ maxorder = astGetMaxOrder( this );
+ map = GetCachedMapping( this, maxorder, "dump_moc", status );
+
+ pr = this->range;
+ for( irange = 0; irange < this->nrange; irange++, pr += 2 ){
+
+ npoint = pr[ 1 ] - pr[ 0 ] + 1;
+ ps = astPointSet( npoint, 2, " ", status );
+ ptr = astGetPoints( ps );
+ if( astOK ) {
+ px = ptr[ 0 ];
+ py = ptr[ 1 ];
+ for( npix = pr[ 0 ]; npix <= pr[1 ]; npix++ ) {
+ NestedToXy( npix, maxorder, &ix, &iy );
+ *(px++) = ix;
+ *(py++) = iy;
+ }
+ }
+
+ ps2 = astTransform( map, ps, 0, NULL );
+ ptr2 = astGetPoints( ps2 );
+ if( astOK ) {
+ px = ptr[ 0 ];
+ py = ptr[ 1 ];
+ pa = ptr2[ 0 ];
+ pb = ptr2[ 1 ];
+ for( npix = pr[ 0 ]; npix <= pr[1 ]; npix++ ) {
+ alpha = cos(*pb)*cos(*pa);
+ beta = cos(*pb)*sin(*pa);
+ fprintf( fd, "%g %g %g %g %g %g\n", *(px++), *(py++), *(pa++),
+ *(pb++), alpha, beta );
+ }
+ }
+
+ ps = astAnnul( ps );
+ ps2 = astAnnul( ps2 );
+
+ }
+
+ fclose( fd );
+}
+
+*/
+
+
+