1 /*
2 *class++
3 *  Name:
4 *     SphMap
5 
6 *  Purpose:
7 *     Map 3-d Cartesian to 2-d spherical coordinates
8 
9 *  Constructor Function:
10 c     astSphMap
11 f     AST_SPHMAP
12 
13 *  Description:
14 *     A SphMap is a Mapping which transforms points from a
15 *     3-dimensional Cartesian coordinate system into a 2-dimensional
16 *     spherical coordinate system (longitude and latitude on a unit
17 *     sphere centred at the origin). It works by regarding the input
18 *     coordinates as position vectors and finding their intersection
19 *     with the sphere surface. The inverse transformation always
20 *     produces points which are a unit distance from the origin
21 *     (i.e. unit vectors).
22 
23 *  Inheritance:
24 *     The SphMap class inherits from the Mapping class.
25 
26 *  Attributes:
27 *     In addition to those attributes common to all Mappings, every
28 *     SphMap also has the following attributes:
29 *
30 *     - UnitRadius: SphMap input vectors lie on a unit sphere?
31 *     - PolarLong: The longitude value to assign to either pole
32 
33 *  Functions:
34 c     The SphMap class does not define any new functions beyond those
35 f     The SphMap class does not define any new routines beyond those
36 *     which are applicable to all Mappings.
37 
38 *  Copyright:
39 *     Copyright (C) 1997-2006 Council for the Central Laboratory of the
40 *     Research Councils
41 
42 *  Licence:
43 *     This program is free software: you can redistribute it and/or
44 *     modify it under the terms of the GNU Lesser General Public
45 *     License as published by the Free Software Foundation, either
46 *     version 3 of the License, or (at your option) any later
47 *     version.
48 *
49 *     This program is distributed in the hope that it will be useful,
50 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
51 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
52 *     GNU Lesser General Public License for more details.
53 *
54 *     You should have received a copy of the GNU Lesser General
55 *     License along with this program.  If not, see
56 *     <http://www.gnu.org/licenses/>.
57 
58 *  Authors:
59 *     DSB: David Berry (Starlink)
60 *     RFWS: R.F. Warren-Smith (Starlink)
61 
62 *  History:
63 *     24-OCT-1996 (DSB):
64 *        Original version.
65 *     5-MAR-1997 (RFWS):
66 *        Tidied public prologues.
67 *     24-MAR-1998 (RFWS):
68 *        Override the astMapMerge method.
69 *     4-SEP-1998 (DSB):
70 *        Added UnitRadius attribute.
71 *     8-JAN-2003 (DSB):
72 *        Changed private InitVtab method to protected astInitSphMapVtab
73 *        method.
74 *     11-JUN-2003 (DSB):
75 *        Added PolarLong attribute.
76 *     10-MAY-2006 (DSB):
77 *        Override astEqual.
78 *     5-NOV-2013 (DSB):
79 *        Modify MapMerge so that it can spot and simplify an
80 *        (inverted SphMap,MatrixMap,SphMap) sequence in which the
81 *        MatrixMap just magnifies or reflects the radius vector.
82 *     25-MAR-2014 (DSB):
83 *        Correct 5-NOV-2013 MapMerge change.
84 *class--
85 */
86 
87 /* Module Macros. */
88 /* ============== */
89 /* Set the name of the class we are implementing. This indicates to
90    the header files that define class interfaces that they should make
91    "protected" symbols available. */
92 #define astCLASS SphMap
93 
94 /* Include files. */
95 /* ============== */
96 /* Interface definitions. */
97 /* ---------------------- */
98 
99 #include "globals.h"             /* Thread-safe global data access */
100 #include "error.h"               /* Error reporting facilities */
101 #include "memory.h"              /* Memory management facilities */
102 #include "globals.h"             /* Thread-safe global data access */
103 #include "object.h"              /* Base Object class */
104 #include "pointset.h"            /* Sets of points/coordinates */
105 #include "mapping.h"             /* Coordinate mappings (parent class) */
106 #include "channel.h"             /* I/O channels */
107 #include "unitmap.h"             /* Unit (identity) Mappings */
108 #include "sphmap.h"              /* Interface definition for this class */
109 #include "pal.h"                 /* SLA transformations */
110 #include "wcsmap.h"              /* For the AST__DPIBY2 (etc) constants */
111 #include "matrixmap.h"           /* Matrix mappings */
112 #include "winmap.h"              /* Shift and scale mappings */
113 #include "zoommap.h"             /* Scale mappings */
114 
115 /* Error code definitions. */
116 /* ----------------------- */
117 #include "ast_err.h"             /* AST error codes */
118 
119 /* C header files. */
120 /* --------------- */
121 #include <float.h>
122 #include <stdarg.h>
123 #include <stddef.h>
124 #include <stdio.h>
125 #include <string.h>
126 
127 /* Module Variables. */
128 /* ================= */
129 
130 /* Address of this static variable is used as a unique identifier for
131    member of this class. */
132 static int class_check;
133 
134 /* Pointers to parent class methods which are extended by this class. */
135 static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
136 static const char *(* parent_getattrib)( AstObject *, const char *, int * );
137 static int (* parent_testattrib)( AstObject *, const char *, int * );
138 static void (* parent_clearattrib)( AstObject *, const char *, int * );
139 static void (* parent_setattrib)( AstObject *, const char *, int * );
140 
141 /* Define macros for accessing each item of thread specific global data. */
142 #ifdef THREAD_SAFE
143 
144 /* Define how to initialise thread-specific globals. */
145 #define GLOBAL_inits \
146    globals->Class_Init = 0; \
147    globals->GetAttrib_Buff[ 0 ] = 0;
148 
149 /* Create the function that initialises global data for this module. */
150 astMAKE_INITGLOBALS(SphMap)
151 
152 /* Define macros for accessing each item of thread specific global data. */
153 #define class_init astGLOBAL(SphMap,Class_Init)
154 #define class_vtab astGLOBAL(SphMap,Class_Vtab)
155 #define getattrib_buff astGLOBAL(SphMap,GetAttrib_Buff)
156 
157 
158 
159 /* If thread safety is not needed, declare and initialise globals at static
160    variables. */
161 #else
162 
163 static char getattrib_buff[ 101 ];
164 
165 
166 /* Define the class virtual function table and its initialisation flag
167    as static variables. */
168 static AstSphMapVtab class_vtab;   /* Virtual function table */
169 static int class_init = 0;       /* Virtual function table initialised? */
170 
171 #endif
172 
173 /* External Interface Function Prototypes. */
174 /* ======================================= */
175 /* The following functions have public prototypes only (i.e. no
176    protected prototypes), so we must provide local prototypes for use
177    within this module. */
178 AstSphMap *astSphMapId_( const char *, ...);
179 
180 /* Prototypes for Private Member Functions. */
181 /* ======================================== */
182 static int GetUnitRadius( AstSphMap *, int * );
183 static int TestUnitRadius( AstSphMap *, int * );
184 static void ClearUnitRadius( AstSphMap *, int * );
185 static void SetUnitRadius( AstSphMap *, int, int * );
186 
187 static double GetPolarLong( AstSphMap *, int * );
188 static int TestPolarLong( AstSphMap *, int * );
189 static void ClearPolarLong( AstSphMap *, int * );
190 static void SetPolarLong( AstSphMap *, double, int * );
191 
192 static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
193 static const char *GetAttrib( AstObject *, const char *, int * );
194 static int Equal( AstObject *, AstObject *, int * );
195 static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * );
196 static int TestAttrib( AstObject *, const char *, int * );
197 static void ClearAttrib( AstObject *, const char *, int * );
198 static void Copy( const AstObject *, AstObject *, int * );
199 static void Delete( AstObject *, int * );
200 static void Dump( AstObject *, AstChannel *, int * );
201 static void SetAttrib( AstObject *, const char *, int * );
202 
203 /* Member functions. */
204 /* ================= */
ClearAttrib(AstObject * this_object,const char * attrib,int * status)205 static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) {
206 /*
207 *  Name:
208 *     ClearAttrib
209 
210 *  Purpose:
211 *     Clear an attribute value for a SphMap.
212 
213 *  Type:
214 *     Private function.
215 
216 *  Synopsis:
217 *     #include "sphmap.h"
218 *     void ClearAttrib( AstObject *this, const char *attrib, int *status, int *status )
219 
220 *  Class Membership:
221 *     SphMap member function (over-rides the astClearAttrib protected
222 *     method inherited from the Mapping class).
223 
224 *  Description:
225 *     This function clears the value of a specified attribute for a
226 *     SphMap, so that the default value will subsequently be used.
227 
228 *  Parameters:
229 *     this
230 *        Pointer to the SphMap.
231 *     attrib
232 *        Pointer to a null-terminated string specifying the attribute
233 *        name.  This should be in lower case with no surrounding white
234 *        space.
235 *     status
236 *        Pointer to the inherited status variable.
237 *     status
238 *        Pointer to the inherited status variable.
239 */
240 
241 /* Local Variables: */
242    AstSphMap *this;             /* Pointer to the SphMap structure */
243 
244 /* Check the global error status. */
245    if ( !astOK ) return;
246 
247 /* Obtain a pointer to the SphMap structure. */
248    this = (AstSphMap *) this_object;
249 
250 /* UnitRadius */
251 /* ---------- */
252    if ( !strcmp( attrib, "unitradius" ) ) {
253       astClearUnitRadius( this );
254 
255 /* PolarLong */
256 /* --------- */
257    } else if ( !strcmp( attrib, "polarlong" ) ) {
258       astClearPolarLong( this );
259 
260 /* If the attribute is still not recognised, pass it on to the parent
261    method for further interpretation. */
262    } else {
263       (*parent_clearattrib)( this_object, attrib, status );
264    }
265 }
266 
Equal(AstObject * this_object,AstObject * that_object,int * status)267 static int Equal( AstObject *this_object, AstObject *that_object, int *status ) {
268 /*
269 *  Name:
270 *     Equal
271 
272 *  Purpose:
273 *     Test if two SphMaps are equivalent.
274 
275 *  Type:
276 *     Private function.
277 
278 *  Synopsis:
279 *     #include "sphmap.h"
280 *     int Equal( AstObject *this, AstObject *that, int *status, int *status )
281 
282 *  Class Membership:
283 *     SphMap member function (over-rides the astEqual protected
284 *     method inherited from the astMapping class).
285 
286 *  Description:
287 *     This function returns a boolean result (0 or 1) to indicate whether
288 *     two SphMaps are equivalent.
289 
290 *  Parameters:
291 *     this
292 *        Pointer to the first Object (a SphMap).
293 *     that
294 *        Pointer to the second Object.
295 *     status
296 *        Pointer to the inherited status variable.
297 *     status
298 *        Pointer to the inherited status variable.
299 
300 *  Returned Value:
301 *     One if the SphMaps are equivalent, zero otherwise.
302 
303 *  Notes:
304 *     - A value of zero will be returned if this function is invoked
305 *     with the global status set, or if it should fail for any reason.
306 */
307 
308 /* Local Variables: */
309    AstSphMap *that;
310    AstSphMap *this;
311    int nin;
312    int nout;
313    int result;
314 
315 /* Initialise. */
316    result = 0;
317 
318 /* Check the global error status. */
319    if ( !astOK ) return result;
320 
321 /* Obtain pointers to the two SphMap structures. */
322    this = (AstSphMap *) this_object;
323    that = (AstSphMap *) that_object;
324 
325 /* Check the second object is a SphMap. We know the first is a
326    SphMap since we have arrived at this implementation of the virtual
327    function. */
328    if( astIsASphMap( that ) ) {
329 
330 /* Get the number of inputs and outputs and check they are the same for both. */
331       nin = astGetNin( this );
332       nout = astGetNout( this );
333       if( astGetNin( that ) == nin && astGetNout( that ) == nout ) {
334 
335 /* If the Invert flags for the two SphMaps differ, it may still be possible
336    for them to be equivalent. First compare the SphMaps if their Invert
337    flags are the same. In this case all the attributes of the two SphMaps
338    must be identical. */
339          if( astGetInvert( this ) == astGetInvert( that ) ) {
340 
341             if( astEQUAL( this->polarlong, that->polarlong ) &&
342                           this->unitradius == that->unitradius ){
343                result = 1;
344             }
345 
346 /* If the Invert flags for the two SphMaps differ, the attributes of the two
347    SphMaps must be inversely related to each other. */
348          } else {
349 
350 /* In the specific case of a SphMap, Invert flags must be equal. */
351             result = 0;
352 
353          }
354       }
355    }
356 
357 /* If an error occurred, clear the result value. */
358    if ( !astOK ) result = 0;
359 
360 /* Return the result, */
361    return result;
362 }
363 
GetAttrib(AstObject * this_object,const char * attrib,int * status)364 static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) {
365 /*
366 *  Name:
367 *     GetAttrib
368 
369 *  Purpose:
370 *     Get the value of a specified attribute for a SphMap.
371 
372 *  Type:
373 *     Private function.
374 
375 *  Synopsis:
376 *     #include "sphmap.h"
377 *     const char *GetAttrib( AstObject *this, const char *attrib, int *status, int *status )
378 
379 *  Class Membership:
380 *     SphMap member function (over-rides the protected astGetAttrib
381 *     method inherited from the Mapping class).
382 
383 *  Description:
384 *     This function returns a pointer to the value of a specified
385 *     attribute for a SphMap, formatted as a character string.
386 
387 *  Parameters:
388 *     this
389 *        Pointer to the SphMap.
390 *     attrib
391 *        Pointer to a null-terminated string containing the name of
392 *        the attribute whose value is required. This name should be in
393 *        lower case, with all white space removed.
394 *     status
395 *        Pointer to the inherited status variable.
396 *     status
397 *        Pointer to the inherited status variable.
398 
399 *  Returned Value:
400 *     - Pointer to a null-terminated string containing the attribute
401 *     value.
402 
403 *  Notes:
404 *     - The returned string pointer may point at memory allocated
405 *     within the SphMap, or at static memory. The contents of the
406 *     string may be over-written or the pointer may become invalid
407 *     following a further invocation of the same function or any
408 *     modification of the SphMap. A copy of the string should
409 *     therefore be made if necessary.
410 *     - A NULL pointer will be returned if this function is invoked
411 *     with the global error status set, or if it should fail for any
412 *     reason.
413 */
414 
415 /* Local Variables: */
416    astDECLARE_GLOBALS           /* Pointer to thread-specific global data */
417    AstSphMap *this;              /* Pointer to the SphMap structure */
418    const char *result;           /* Pointer value to return */
419    double dval;                  /* Double precision attribute value */
420    int ival;                     /* Int attribute value */
421 
422 /* Initialise. */
423    result = NULL;
424 
425 /* Check the global error status. */
426    if ( !astOK ) return result;
427 
428 /* Get a pointer to the thread specific global data structure. */
429    astGET_GLOBALS(this_object);
430 
431 /* Obtain a pointer to the SphMap structure. */
432    this = (AstSphMap *) this_object;
433 
434 /* UnitRadius. */
435 /* ----------- */
436    if ( !strcmp( attrib, "unitradius" ) ) {
437       ival = astGetUnitRadius( this );
438       if ( astOK ) {
439          (void) sprintf( getattrib_buff, "%d", ival );
440          result = getattrib_buff;
441       }
442 
443 /* PolarLong */
444 /* --------- */
445    } else if ( !strcmp( attrib, "polarlong" ) ) {
446       dval = astGetPolarLong( this );
447       if ( astOK ) {
448          (void) sprintf( getattrib_buff, "%.*g", DBL_DIG, dval );
449          result = getattrib_buff;
450       }
451 
452 /* If the attribute name was not recognised, pass it on to the parent
453    method for further interpretation. */
454    } else {
455       result = (*parent_getattrib)( this_object, attrib, status );
456    }
457 
458 /* Return the result. */
459    return result;
460 }
461 
astInitSphMapVtab_(AstSphMapVtab * vtab,const char * name,int * status)462 void astInitSphMapVtab_(  AstSphMapVtab *vtab, const char *name, int *status ) {
463 /*
464 *+
465 *  Name:
466 *     astInitSphMapVtab
467 
468 *  Purpose:
469 *     Initialise a virtual function table for a SphMap.
470 
471 *  Type:
472 *     Protected function.
473 
474 *  Synopsis:
475 *     #include "sphmap.h"
476 *     void astInitSphMapVtab( AstSphMapVtab *vtab, const char *name )
477 
478 *  Class Membership:
479 *     SphMap vtab initialiser.
480 
481 *  Description:
482 *     This function initialises the component of a virtual function
483 *     table which is used by the SphMap class.
484 
485 *  Parameters:
486 *     vtab
487 *        Pointer to the virtual function table. The components used by
488 *        all ancestral classes will be initialised if they have not already
489 *        been initialised.
490 *     name
491 *        Pointer to a constant null-terminated character string which contains
492 *        the name of the class to which the virtual function table belongs (it
493 *        is this pointer value that will subsequently be returned by the Object
494 *        astClass function).
495 *-
496 */
497 
498 /* Local Variables: */
499    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
500    AstObjectVtab *object;        /* Pointer to Object component of Vtab */
501    AstMappingVtab *mapping;      /* Pointer to Mapping component of Vtab */
502 
503 /* Check the local error status. */
504    if ( !astOK ) return;
505 
506 /* Get a pointer to the thread specific global data structure. */
507    astGET_GLOBALS(NULL);
508 
509 /* Initialize the component of the virtual function table used by the
510    parent class. */
511    astInitMappingVtab( (AstMappingVtab *) vtab, name );
512 
513 /* Store a unique "magic" value in the virtual function table. This
514    will be used (by astIsASphMap) to determine if an object belongs
515    to this class.  We can conveniently use the address of the (static)
516    class_check variable to generate this unique value. */
517    vtab->id.check = &class_check;
518    vtab->id.parent = &(((AstMappingVtab *) vtab)->id);
519 
520 /* Initialise member function pointers. */
521 /* ------------------------------------ */
522 /* Store pointers to the member functions (implemented here) that provide
523    virtual methods for this class. */
524    vtab->ClearUnitRadius = ClearUnitRadius;
525    vtab->SetUnitRadius = SetUnitRadius;
526    vtab->GetUnitRadius = GetUnitRadius;
527    vtab->TestUnitRadius = TestUnitRadius;
528 
529    vtab->ClearPolarLong = ClearPolarLong;
530    vtab->SetPolarLong = SetPolarLong;
531    vtab->GetPolarLong = GetPolarLong;
532    vtab->TestPolarLong = TestPolarLong;
533 
534 /* Save the inherited pointers to methods that will be extended, and
535    replace them with pointers to the new member functions. */
536    object = (AstObjectVtab *) vtab;
537    mapping = (AstMappingVtab *) vtab;
538 
539    parent_clearattrib = object->ClearAttrib;
540    object->ClearAttrib = ClearAttrib;
541    parent_getattrib = object->GetAttrib;
542    object->GetAttrib = GetAttrib;
543    parent_setattrib = object->SetAttrib;
544    object->SetAttrib = SetAttrib;
545    parent_testattrib = object->TestAttrib;
546    object->TestAttrib = TestAttrib;
547 
548    parent_transform = mapping->Transform;
549    mapping->Transform = Transform;
550 
551 /* Store replacement pointers for methods which will be over-ridden by
552    new member functions implemented here. */
553    object->Equal = Equal;
554    mapping->MapMerge = MapMerge;
555 
556 /* Declare the class dump, copy and delete functions.*/
557    astSetDump( vtab, Dump, "SphMap", "Cartesian to Spherical mapping" );
558    astSetCopy( (AstObjectVtab *) vtab, Copy );
559    astSetDelete( (AstObjectVtab *) vtab, Delete );
560 
561 /* If we have just initialised the vtab for the current class, indicate
562    that the vtab is now initialised, and store a pointer to the class
563    identifier in the base "object" level of the vtab. */
564    if( vtab == &class_vtab ) {
565       class_init = 1;
566       astSetVtabClassIdentifier( vtab, &(vtab->id) );
567    }
568 }
569 
MapMerge(AstMapping * this,int where,int series,int * nmap,AstMapping *** map_list,int ** invert_list,int * status)570 static int MapMerge( AstMapping *this, int where, int series, int *nmap,
571                      AstMapping ***map_list, int **invert_list, int *status ) {
572 /*
573 *  Name:
574 *     MapMerge
575 
576 *  Purpose:
577 *     Simplify a sequence of Mappings containing a SphMap.
578 
579 *  Type:
580 *     Private function.
581 
582 *  Synopsis:
583 *     #include "sphmap.h"
584 *     int MapMerge( AstMapping *this, int where, int series, int *nmap,
585 *                   AstMapping ***map_list, int **invert_list, int *status, int *status )
586 
587 *  Class Membership:
588 *     SphMap method (over-rides the protected astMapMerge method
589 *     inherited from the Mapping class).
590 
591 *  Description:
592 *     This function attempts to simplify a sequence of Mappings by
593 *     merging a nominated SphMap in the sequence with its neighbours,
594 *     so as to shorten the sequence if possible.
595 *
596 *     In many cases, simplification will not be possible and the
597 *     function will return -1 to indicate this, without further
598 *     action.
599 *
600 *     In most cases of interest, however, this function will either
601 *     attempt to replace the nominated SphMap with one which it
602 *     considers simpler, or to merge it with the Mappings which
603 *     immediately precede it or follow it in the sequence (both will
604 *     normally be considered). This is sufficient to ensure the
605 *     eventual simplification of most Mapping sequences by repeated
606 *     application of this function.
607 *
608 *     In some cases, the function may attempt more elaborate
609 *     simplification, involving any number of other Mappings in the
610 *     sequence. It is not restricted in the type or scope of
611 *     simplification it may perform, but will normally only attempt
612 *     elaborate simplification in cases where a more straightforward
613 *     approach is not adequate.
614 
615 *  Parameters:
616 *     this
617 *        Pointer to the nominated SphMap which is to be merged with
618 *        its neighbours. This should be a cloned copy of the SphMap
619 *        pointer contained in the array element "(*map_list)[where]"
620 *        (see below). This pointer will not be annulled, and the
621 *        SphMap it identifies will not be modified by this function.
622 *     where
623 *        Index in the "*map_list" array (below) at which the pointer
624 *        to the nominated SphMap resides.
625 *     series
626 *        A non-zero value indicates that the sequence of Mappings to
627 *        be simplified will be applied in series (i.e. one after the
628 *        other), whereas a zero value indicates that they will be
629 *        applied in parallel (i.e. on successive sub-sets of the
630 *        input/output coordinates).
631 *     nmap
632 *        Address of an int which counts the number of Mappings in the
633 *        sequence. On entry this should be set to the initial number
634 *        of Mappings. On exit it will be updated to record the number
635 *        of Mappings remaining after simplification.
636 *     map_list
637 *        Address of a pointer to a dynamically allocated array of
638 *        Mapping pointers (produced, for example, by the astMapList
639 *        method) which identifies the sequence of Mappings. On entry,
640 *        the initial sequence of Mappings to be simplified should be
641 *        supplied.
642 *
643 *        On exit, the contents of this array will be modified to
644 *        reflect any simplification carried out. Any form of
645 *        simplification may be performed. This may involve any of: (a)
646 *        removing Mappings by annulling any of the pointers supplied,
647 *        (b) replacing them with pointers to new Mappings, (c)
648 *        inserting additional Mappings and (d) changing their order.
649 *
650 *        The intention is to reduce the number of Mappings in the
651 *        sequence, if possible, and any reduction will be reflected in
652 *        the value of "*nmap" returned. However, simplifications which
653 *        do not reduce the length of the sequence (but improve its
654 *        execution time, for example) may also be performed, and the
655 *        sequence might conceivably increase in length (but normally
656 *        only in order to split up a Mapping into pieces that can be
657 *        more easily merged with their neighbours on subsequent
658 *        invocations of this function).
659 *
660 *        If Mappings are removed from the sequence, any gaps that
661 *        remain will be closed up, by moving subsequent Mapping
662 *        pointers along in the array, so that vacated elements occur
663 *        at the end. If the sequence increases in length, the array
664 *        will be extended (and its pointer updated) if necessary to
665 *        accommodate any new elements.
666 *
667 *        Note that any (or all) of the Mapping pointers supplied in
668 *        this array may be annulled by this function, but the Mappings
669 *        to which they refer are not modified in any way (although
670 *        they may, of course, be deleted if the annulled pointer is
671 *        the final one).
672 *     invert_list
673 *        Address of a pointer to a dynamically allocated array which,
674 *        on entry, should contain values to be assigned to the Invert
675 *        attributes of the Mappings identified in the "*map_list"
676 *        array before they are applied (this array might have been
677 *        produced, for example, by the astMapList method). These
678 *        values will be used by this function instead of the actual
679 *        Invert attributes of the Mappings supplied, which are
680 *        ignored.
681 *
682 *        On exit, the contents of this array will be updated to
683 *        correspond with the possibly modified contents of the
684 *        "*map_list" array.  If the Mapping sequence increases in
685 *        length, the "*invert_list" array will be extended (and its
686 *        pointer updated) if necessary to accommodate any new
687 *        elements.
688 *     status
689 *        Pointer to the inherited status variable.
690 *     status
691 *        Pointer to the inherited status variable.
692 
693 *  Returned Value:
694 *     If simplification was possible, the function returns the index
695 *     in the "map_list" array of the first element which was
696 *     modified. Otherwise, it returns -1 (and makes no changes to the
697 *     arrays supplied).
698 
699 *  Notes:
700 *     - A value of -1 will be returned if this function is invoked
701 *     with the global error status set, or if it should fail for any
702 *     reason.
703 */
704 
705 /* Local Variables: */
706    AstMapping *new;              /* Pointer to replacement Mapping */
707    AstMatrixMap *mm;             /* Pointer to MatrixMap */
708    AstWinMap *wm;                /* The new WinMap */
709    const char *class;            /* Pointer to Mapping class string */
710    double absval;                /* Absolute value fo each diagonal element */
711    double diag[ 3 ];             /* The diagonal matrix elements */
712    double polarlong;             /* Value of PolarLong attribute */
713    int imap1;                    /* Index of first SphMap */
714    int imap2;                    /* Index of second SphMap */
715    int imap;                     /* Loop counter for Mappings */
716    int result;                   /* Result value to return */
717    int simpler;                  /* Mappings simplified? */
718 
719 /* Initialise the returned result. */
720    result = -1;
721 
722 /* Check the global error status. */
723    if ( !astOK ) return result;
724 
725 /* Further initialisation. */
726    new = NULL;
727    simpler = 0;
728 
729 /* We will only handle the case of SphMaps in series and will consider
730    merging the nominated SphMap with the Mapping which follows
731    it. Check that there is such a Mapping. */
732    if ( series && ( ( where + 1 ) < *nmap ) ) {
733 
734 /* Obtain the indices of the two potential SphMaps to be merged. */
735       imap1 = where;
736       imap2 = where + 1;
737 
738 /* Obtain the Class string of the second Mapping and determine if it
739    is a SphMap. */
740       class = astGetClass( ( *map_list )[ imap2 ] );
741       if ( astOK && !strcmp( class, "SphMap" ) ) {
742 
743 /* Check if the first SphMap is applied in the inverse direction and
744    the second in the forward direction. This combination can be
745    simplified if the PolarLongitude attributes are equal.. */
746          if( ( *invert_list )[ imap1 ] && !( *invert_list )[ imap2 ] ) {
747             simpler = astEQUAL( astGetPolarLong( ( *map_list )[ imap1 ] ),
748                                 astGetPolarLong( ( *map_list )[ imap2 ] ) );
749 
750 /* If the first SphMap is applied in the forward direction and the second in
751    the inverse direction, the combination can only be simplified if the
752    input vectors to the first SphMap all have unit length (as indicated by
753    the UnitRadius attribute). */
754          } else if( !( *invert_list )[ imap1 ] && ( *invert_list )[ imap2 ] ) {
755             simpler = astGetUnitRadius( ( *map_list )[ imap1 ] );
756          }
757       }
758 
759 /* If the two SphMaps can be simplified, create a UnitMap to replace
760    them. */
761       if ( simpler ) {
762          new = (AstMapping *) astUnitMap( 2, "", status );
763 
764 /* Annul the pointers to the SphMaps. */
765          if ( astOK ) {
766             ( *map_list )[ imap1 ] = astAnnul( ( *map_list )[ imap1 ] );
767             ( *map_list )[ imap2 ] = astAnnul( ( *map_list )[ imap2 ] );
768 
769 /* Insert the pointer to the replacement Mapping and initialise its
770    invert flag. */
771             ( *map_list )[ imap1 ] = new;
772             ( *invert_list )[ imap1 ] = 0;
773 
774 /* Loop to close the resulting gap by moving subsequent elements down
775    in the arrays. */
776             for ( imap = imap2 + 1; imap < *nmap; imap++ ) {
777                ( *map_list )[ imap - 1 ] = ( *map_list )[ imap ];
778                ( *invert_list )[ imap - 1 ] = ( *invert_list )[ imap ];
779             }
780 
781 /* Clear the vacated elements at the end. */
782             ( *map_list )[ *nmap - 1 ] = NULL;
783             ( *invert_list )[ *nmap - 1 ] = 0;
784 
785 /* Decrement the Mapping count and return the index of the first
786    modified element. */
787             ( *nmap )--;
788             result = imap1;
789          }
790       }
791    }
792 
793 /* Another possible simplification is if the nominated Mapping is an inverted
794    SphMap followed in series by a ZoomMap or diagonal MatrixMap that has
795    diagonal elements of equal magnitude, which is then followed by a
796    non-inverted SphMap. This is equivalent to a 3D rotation of a pair of
797    (longitude,latitude) angles. The MatrixMap/ZoomMap may magnify the
798    radius vector, but this will not alter the angles. Any difference in
799    signs amongst the diagonal elements will cause a reflection or reversal
800    of the corresponbding angles, which can be represented by a WinMap. We
801    do not need to consider the other possibility (that the nominated
802    SphMap is the *last* Mapping in such a sequence of three), since we
803    will already have discovered such a sequence on an earlier invocation
804    of this function. */
805    if( series && !simpler && ( *invert_list )[ where ] &&
806        where + 2 < *nmap  ) {
807 
808 /* Check the third Mapping is a non-inverted SphMap. */
809       class = astGetClass( ( *map_list )[ where + 2 ] );
810       if( astOK && !strcmp( class, "SphMap" ) &&
811           !( *invert_list )[ where + 2 ] ) {
812 
813 /* Check the second Mapping is a ZoomMap, or a diagonal MatrixMap that
814    has diagonal elements of equal magnitude. Since the Mapping is
815    sandwiched between the two SphMaps, we know it must have 3 inputs and
816    3 outputs. Record the corresponding diagonal values. The state of the
817    Invert flag does not matter since it will only affect the degree to
818    which the radius vector is magnified - it will not change the signs of
819    any diagonal elements. */
820          class = astGetClass( ( *map_list )[ where + 1 ] );
821          if( astOK && !strcmp( class, "ZoomMap" ) ) {
822             diag[ 0 ] = astGetZoom( ( *map_list )[ where + 1 ] );
823             if( diag[ 0 ] != 0.0 ) {
824                diag[ 1 ] = diag[ 0 ];
825                diag[ 2 ] = diag[ 0 ];
826             } else {
827                class = NULL;
828             }
829 
830          } else if( astOK && !strcmp( class, "MatrixMap" ) ) {
831             mm = (AstMatrixMap *)  ( *map_list )[ where + 1 ];
832             if( mm->form == 1 && mm->f_matrix ) {
833                diag[ 0 ] = mm->f_matrix[ 0 ];
834                if( diag[ 0 ] != 0.0 ) {
835                   diag[ 1 ] = mm->f_matrix[ 1 ];
836                   diag[ 2 ] = mm->f_matrix[ 2 ];
837 
838                   absval = fabs( diag[ 0 ] );
839                   if( !astEQUAL( fabs( diag[ 1 ] ), absval ) ||
840                       !astEQUAL( fabs( diag[ 2 ] ), absval ) ) {
841                      class = NULL;
842                   }
843 
844                } else {
845                   class = NULL;
846                }
847 
848             } else {
849                class = NULL;
850             }
851 
852          } else {
853             class = NULL;
854          }
855 
856       } else {
857          class = NULL;
858       }
859 
860 /* We can only make changes if above conditions were met. */
861       if( class ) {
862 
863 /* Create a WinMap that modifies the (longitude,latitude) values, initially
864    with undefined corners. */
865          wm = astWinMap( 2, NULL, NULL, NULL, NULL, "", status );
866 
867 /* Store appropriate scales and offsets in the WinMap. These just depend on
868    the signs of the matrix diagonal elements since we know the magnitudes of
869    these elements are all equal. */
870          if( diag[ 0 ] < 0.0 ) {
871             if( diag[ 1 ] < 0.0 ) {
872                wm->a[ 0 ] = AST__DPI;
873                wm->b[ 0 ] = 1.0;
874             } else {
875                wm->a[ 0 ] = AST__DPI;
876                wm->b[ 0 ] = -1.0;
877             }
878 
879          } else {
880             if( diag[ 1 ] < 0.0 ) {
881                wm->a[ 0 ] = 0.0;
882                wm->b[ 0 ] = -1.0;
883             } else {
884                wm->a[ 0 ] = 0.0;
885                wm->b[ 0 ] = 1.0;
886             }
887          }
888 
889          if( diag[ 2 ] < 0.0 ) {
890             wm->a[ 1 ] = 0.0;
891             wm->b[ 1 ] = -1.0;
892          } else {
893             wm->a[ 1 ] = 0.0;
894             wm->b[ 1 ] = 1.0;
895          }
896 
897 /* We are aiming to replace the supplied (SphMap,MatrixMap,SphMap)
898    combination with (WinMap,SphMap,SphMap), leaving us with an inverted
899    and non-inverted SphMap side by side. This is on the understanding
900    that a subsequent call to this function will combine these two
901    adjacent SphMaps into a UnitMap. But this will only happen if the
902    adjacent SphMaps have equal values for their PolarLong attributes. The
903    change of (SphMap,MatrixMap) to (WinMap,SphMap) will change the value
904    of the PolarLong attribute in the first SphMap, so we need to work out
905    this changed value and check that it is the same as the PolarLong
906    value of the second SphMap. If they are different, there is no point
907    making any changes since the two SphMaps cannot be merged into a
908    UnitMap. So get the PolarLong value from the supplied first SphMap. */
909          polarlong = astGetPolarLong( ( *map_list )[ where ] );
910 
911 /* Modified the PolarLong value to take account of the change from
912    (SphMap,MatrixMap) to (WinMap,SphMap). */
913          polarlong =  wm->a[ 0 ] + wm->b[ 0 ]*polarlong;
914 
915 /* Check this is the same as the PolarLong value in the second SphMap. */
916          if( astEQUAL( polarlong, astGetPolarLong( ( *map_list )[ where + 2 ] ) ) ) {
917 
918 /* All is good, so we can now change the supplied Mappings list. First
919    change the PolarLong value in the first SphMap. */
920             astSetPolarLong( ( *map_list )[ where ], polarlong );
921 
922 /* Annul The MatrixMap or ZoomMap. */
923             (void) astAnnul( ( *map_list )[ where + 1 ] );
924 
925 /* Move the first SphMap to the slot left vacant by the annulled
926    MatrixMap or ZoomMap. */
927             ( *map_list )[ where + 1 ] = ( *map_list )[ where ];
928             ( *invert_list )[ where + 1 ] = ( *invert_list )[ where ];
929 
930 /* Store the new WinMap in the place of the SphMap. */
931             ( *map_list )[ where ] = astClone( wm );
932             ( *invert_list )[ where ] = 0;
933 
934 /* Return the index of the first modified element. */
935             result = where;
936          }
937 
938 /* Free resources. */
939          wm = astAnnul( wm );
940       }
941    }
942 
943 /* If an error occurred, clear the returned result. */
944    if ( !astOK ) result = -1;
945 
946 /* Return the result. */
947    return result;
948 }
949 
SetAttrib(AstObject * this_object,const char * setting,int * status)950 static void SetAttrib( AstObject *this_object, const char *setting, int *status ) {
951 /*
952 *  Name:
953 *     SetAttrib
954 
955 *  Purpose:
956 *     Set an attribute value for a SphMap.
957 
958 *  Type:
959 *     Private function.
960 
961 *  Synopsis:
962 *     #include "sphmap.h"
963 *     void SetAttrib( AstObject *this, const char *setting )
964 
965 *  Class Membership:
966 *     SphMap member function (over-rides the astSetAttrib protected
967 *     method inherited from the Mapping class).
968 
969 *  Description:
970 *     This function assigns an attribute value for a SphMap, the
971 *     attribute and its value being specified by means of a string of
972 *     the form:
973 *
974 *        "attribute= value "
975 *
976 *     Here, "attribute" specifies the attribute name and should be in
977 *     lower case with no white space present. The value to the right
978 *     of the "=" should be a suitable textual representation of the
979 *     value to be assigned and this will be interpreted according to
980 *     the attribute's data type.  White space surrounding the value is
981 *     only significant for string attributes.
982 
983 *  Parameters:
984 *     this
985 *        Pointer to the SphMap.
986 *     setting
987 *        Pointer to a null-terminated string specifying the new attribute
988 *        value.
989 */
990 
991 /* Local Variables: */
992    AstSphMap *this;              /* Pointer to the SphMap structure */
993    double dval;                  /* Double precision attribute value */
994    int len;                      /* Length of setting string */
995    int ival;                     /* Int attribute value */
996    int nc;                       /* Number of characters read by astSscanf */
997 
998 /* Check the global error status. */
999    if ( !astOK ) return;
1000 
1001 /* Obtain a pointer to the SphMap structure. */
1002    this = (AstSphMap *) this_object;
1003 
1004 /* Obtain the length of the setting string. */
1005    len = (int) strlen( setting );
1006 
1007 /* UnitRadius */
1008 /* ---------- */
1009    if ( nc = 0,
1010         ( 1 == astSscanf( setting, "unitradius= %d %n", &ival, &nc ) )
1011         && ( nc >= len ) ) {
1012       astSetUnitRadius( this, ival );
1013 
1014 /* PolarLong */
1015 /* --------- */
1016    } else if ( nc = 0,
1017         ( 1 == astSscanf( setting, "polarlong= %lf %n", &dval, &nc ) )
1018         && ( nc >= len ) ) {
1019       astSetPolarLong( this, dval );
1020 
1021 /* If the attribute is still not recognised, pass it on to the parent
1022    method for further interpretation. */
1023    } else {
1024       (*parent_setattrib)( this_object, setting, status );
1025    }
1026 }
1027 
TestAttrib(AstObject * this_object,const char * attrib,int * status)1028 static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) {
1029 /*
1030 *  Name:
1031 *     TestAttrib
1032 
1033 *  Purpose:
1034 *     Test if a specified attribute value is set for a SphMap.
1035 
1036 *  Type:
1037 *     Private function.
1038 
1039 *  Synopsis:
1040 *     #include "sphmap.h"
1041 *     int TestAttrib( AstObject *this, const char *attrib, int *status, int *status )
1042 
1043 *  Class Membership:
1044 *     SphMap member function (over-rides the astTestAttrib protected
1045 *     method inherited from the Mapping class).
1046 
1047 *  Description:
1048 *     This function returns a boolean result (0 or 1) to indicate whether
1049 *     a value has been set for one of a SphMap's attributes.
1050 
1051 *  Parameters:
1052 *     this
1053 *        Pointer to the SphMap.
1054 *     attrib
1055 *        Pointer to a null-terminated string specifying the attribute
1056 *        name.  This should be in lower case with no surrounding white
1057 *        space.
1058 *     status
1059 *        Pointer to the inherited status variable.
1060 *     status
1061 *        Pointer to the inherited status variable.
1062 
1063 *  Returned Value:
1064 *     One if a value has been set, otherwise zero.
1065 
1066 *  Notes:
1067 *     - A value of zero will be returned if this function is invoked
1068 *     with the global status set, or if it should fail for any reason.
1069 */
1070 
1071 /* Local Variables: */
1072    AstSphMap *this;             /* Pointer to the SphMap structure */
1073    int result;                   /* Result value to return */
1074 
1075 /* Initialise. */
1076    result = 0;
1077 
1078 /* Check the global error status. */
1079    if ( !astOK ) return result;
1080 
1081 /* Obtain a pointer to the SphMap structure. */
1082    this = (AstSphMap *) this_object;
1083 
1084 /* UnitRadius */
1085 /* ---------- */
1086    if ( !strcmp( attrib, "unitradius" ) ) {
1087       result = astTestUnitRadius( this );
1088 
1089 /* PolarLong */
1090 /* --------- */
1091    } else if ( !strcmp( attrib, "polarlong" ) ) {
1092       result = astTestPolarLong( this );
1093 
1094 /* If the attribute is still not recognised, pass it on to the parent
1095    method for further interpretation. */
1096    } else {
1097       result = (*parent_testattrib)( this_object, attrib, status );
1098    }
1099 
1100 /* Return the result, */
1101    return result;
1102 }
1103 
Transform(AstMapping * this,AstPointSet * in,int forward,AstPointSet * out,int * status)1104 static AstPointSet *Transform( AstMapping *this, AstPointSet *in,
1105                                int forward, AstPointSet *out, int *status ) {
1106 /*
1107 *  Name:
1108 *     Transform
1109 
1110 *  Purpose:
1111 *     Apply a SphMap to transform a set of points.
1112 
1113 *  Type:
1114 *     Private function.
1115 
1116 *  Synopsis:
1117 *     #include "sphmap.h"
1118 *     AstPointSet *Transform( AstMapping *this, AstPointSet *in,
1119 *                             int forward, AstPointSet *out, int *status, int *status )
1120 
1121 *  Class Membership:
1122 *     SphMap member function (over-rides the astTransform protected
1123 *     method inherited from the Mapping class).
1124 
1125 *  Description:
1126 *     This function takes a SphMap and a set of points encapsulated in a
1127 *     PointSet and transforms the points from Cartesian coordinates to
1128 *     spherical coordinates.
1129 
1130 *  Parameters:
1131 *     this
1132 *        Pointer to the SphMap.
1133 *     in
1134 *        Pointer to the PointSet holding the input coordinate data.
1135 *     forward
1136 *        A non-zero value indicates that the forward coordinate transformation
1137 *        should be applied, while a zero value requests the inverse
1138 *        transformation.
1139 *     out
1140 *        Pointer to a PointSet which will hold the transformed (output)
1141 *        coordinate values. A NULL value may also be given, in which case a
1142 *        new PointSet will be created by this function.
1143 *     status
1144 *        Pointer to the inherited status variable.
1145 *     status
1146 *        Pointer to the inherited status variable.
1147 
1148 *  Returned Value:
1149 *     Pointer to the output (possibly new) PointSet.
1150 
1151 *  Notes:
1152 *     -  A null pointer will be returned if this function is invoked with the
1153 *     global error status set, or if it should fail for any reason.
1154 *     -  The number of coordinate values per point in the input PointSet must
1155 *     match the number of coordinates for the SphMap being applied.
1156 *     -  If an output PointSet is supplied, it must have space for sufficient
1157 *     number of points and coordinate values per point to accommodate the
1158 *     result. Any excess space will be ignored.
1159 */
1160 
1161 /* Local Variables: */
1162    AstPointSet *result;          /* Pointer to output PointSet */
1163    AstSphMap *map;               /* Pointer to SphMap to be applied */
1164    double **ptr_in;              /* Pointer to input coordinate data */
1165    double **ptr_out;             /* Pointer to output coordinate data */
1166    int npoint;                   /* Number of points */
1167    int point;                    /* Loop counter for points */
1168    double *p0;                   /* Pointer to x axis value */
1169    double *p1;                   /* Pointer to y axis value */
1170    double *p2;                   /* Pointer to z axis value */
1171    double *q0;                   /* Pointer to longitude value */
1172    double *q1;                   /* Pointer to latitude value */
1173    double mxerr;                 /* Largest value which is effectively zero */
1174    double polarlong;             /* Longitude at either pole */
1175    double v[3];                  /* Vector for a single point */
1176 
1177 /* Check the global error status. */
1178    if ( !astOK ) return NULL;
1179 
1180 /* Obtain a pointer to the SphMap. */
1181    map = (AstSphMap *) this;
1182 
1183 /* Apply the parent mapping using the stored pointer to the Transform member
1184    function inherited from the parent Mapping class. This function validates
1185    all arguments and generates an output PointSet if necessary, but does not
1186    actually transform any coordinate values. */
1187    result = (*parent_transform)( this, in, forward, out, status );
1188 
1189 /* We will now extend the parent astTransform method by performing the
1190    calculations needed to generate the output coordinate values. */
1191 
1192 /* Determine the numbers of points and coordinates per point from the input
1193    PointSet and obtain pointers for accessing the input and output coordinate
1194    values. */
1195    npoint = astGetNpoint( in );
1196    ptr_in = astGetPoints( in );
1197    ptr_out = astGetPoints( result );
1198 
1199 /* Determine whether to apply the forward or inverse mapping, according to the
1200    direction specified and whether the mapping has been inverted. */
1201    if ( astGetInvert( map ) ) forward = !forward;
1202 
1203 /* Perform coordinate arithmetic. */
1204 /* ------------------------------ */
1205    if( astOK ){
1206 
1207 /* First deal with forward mappings from Cartesian to Spherical. */
1208       if( forward ){
1209 
1210 /* Get the longitude to return at either pole. */
1211          polarlong = astGetPolarLong( this );
1212 
1213 /* Store pointers to the input Cartesian axes. */
1214          p0 = ptr_in[ 0 ];
1215          p1 = ptr_in[ 1 ];
1216          p2 = ptr_in[ 2 ];
1217 
1218 /* Store pointers to the output Spherical axes. */
1219          q0 = ptr_out[ 0 ];
1220          q1 = ptr_out[ 1 ];
1221 
1222 /* Apply the mapping to every point. */
1223          for( point = 0; point < npoint; point++ ){
1224             if( *p0 != AST__BAD && *p1 != AST__BAD && *p2 != AST__BAD ){
1225                v[0] = *p0;
1226                v[1] = *p1;
1227                v[2] = *p2;
1228 
1229 /* At either pole, return the longitude equal to PolarLong attribute. */
1230                mxerr = fabs( 1000.0*v[ 2 ] )*DBL_EPSILON;
1231                if( fabs( v[ 0 ] ) < mxerr && fabs( v[ 1 ] ) < mxerr ) {
1232                   if( v[ 2 ] < 0.0 ) {
1233                      *(q0++) = polarlong;
1234                      *(q1++) = -AST__DPIBY2;
1235                   } else if( v[ 2 ] > 0.0 ) {
1236                      *(q0++) = polarlong;
1237                      *(q1++) = AST__DPIBY2;
1238                   } else {
1239                      *(q0++) = AST__BAD;
1240                      *(q1++) = AST__BAD;
1241                   }
1242 
1243 /* Otherwise use a SLALIB function to do the conversion (SLALIB always
1244    returns zero at either pole which is why we make the above check). */
1245                } else {
1246                   palDcc2s( v, q0++, q1++ );
1247                }
1248 
1249             } else {
1250                *(q0++) = AST__BAD;
1251                *(q1++) = AST__BAD;
1252             }
1253             p0++;
1254             p1++;
1255             p2++;
1256          }
1257 
1258 /* Now deal with inverse mappings from Spherical to Cartesian. */
1259       } else {
1260 
1261 /* Store pointers to the input Spherical axes. */
1262          q0 = ptr_in[ 0 ];
1263          q1 = ptr_in[ 1 ];
1264 
1265 /* Store pointers to the output Cartesian axes. */
1266          p0 = ptr_out[ 0 ];
1267          p1 = ptr_out[ 1 ];
1268          p2 = ptr_out[ 2 ];
1269 
1270 /* Apply the mapping to every point. */
1271          for( point = 0; point < npoint; point++ ){
1272             if( *q0 != AST__BAD && *q1 != AST__BAD ){
1273                palDcs2c( *q0, *q1, v );
1274                *(p0++) = v[ 0 ];
1275                *(p1++) = v[ 1 ];
1276                *(p2++) = v[ 2 ];
1277             } else {
1278                *(p0++) = AST__BAD;
1279                *(p1++) = AST__BAD;
1280                *(p2++) = AST__BAD;
1281 
1282             }
1283             q0++;
1284             q1++;
1285          }
1286 
1287       }
1288 
1289    }
1290 
1291 /* Return a pointer to the output PointSet. */
1292    return result;
1293 }
1294 
1295 /* Functions which access class attributes. */
1296 /* ---------------------------------------- */
1297 /* Implement member functions to access the attributes associated with
1298    this class using the macros defined for this purpose in the
1299    "object.h" file. For a description of each attribute, see the class
1300    interface (in the associated .h file). */
1301 
1302 /* UnitRadius */
1303 /* ---------- */
1304 /*
1305 *att++
1306 *  Name:
1307 *     UnitRadius
1308 
1309 *  Purpose:
1310 *     SphMap input vectors lie on a unit sphere?
1311 
1312 *  Type:
1313 *     Public attribute.
1314 
1315 *  Synopsis:
1316 *     Integer (boolean).
1317 
1318 *  Description:
1319 *     This is a boolean attribute which indicates whether the
1320 *     3-dimensional vectors which are supplied as input to a SphMap
1321 *     are known to always have unit length, so that they lie on a unit
1322 *     sphere centred on the origin.
1323 *
1324 c     If this condition is true (indicated by setting UnitRadius
1325 c     non-zero), it implies that a CmpMap which is composed of a
1326 c     SphMap applied in the forward direction followed by a similar
1327 c     SphMap applied in the inverse direction may be simplified
1328 c     (e.g. by astSimplify) to become a UnitMap. This is because the
1329 c     input and output vectors will both have unit length and will
1330 c     therefore have the same coordinate values.
1331 f     If this condition is true (indicated by setting UnitRadius
1332 f     non-zero), it implies that a CmpMap which is composed of a
1333 f     SphMap applied in the forward direction followed by a similar
1334 f     SphMap applied in the inverse direction may be simplified
1335 f     (e.g. by AST_SIMPLIFY) to become a UnitMap. This is because the
1336 f     input and output vectors will both have unit length and will
1337 f     therefore have the same coordinate values.
1338 *
1339 *     If UnitRadius is zero (the default), then although the output
1340 *     vector produced by the CmpMap (above) will still have unit
1341 *     length, the input vector may not have. This will, in general,
1342 *     change the coordinate values, so it prevents the pair of SphMaps
1343 *     being simplified.
1344 
1345 *  Notes:
1346 *     - This attribute is intended mainly for use when SphMaps are
1347 *     involved in a sequence of Mappings which project (e.g.) a
1348 *     dataset on to the celestial sphere. By regarding the celestial
1349 *     sphere as a unit sphere (and setting UnitRadius to be non-zero)
1350 *     it becomes possible to cancel the SphMaps present, along with
1351 *     associated sky projections, when two datasets are aligned using
1352 *     celestial coordinates. This often considerably improves
1353 *     performance.
1354 *     - Such a situations often arises when interpreting FITS data and
1355 *     is handled automatically by the FitsChan class.
1356 *     - The value of the UnitRadius attribute is used only to control
1357 *     the simplification of Mappings and has no effect on the value of
1358 *     the coordinates transformed by a SphMap. The lengths of the
1359 *     input 3-dimensional Cartesian vectors supplied are always
1360 *     ignored, even if UnitRadius is non-zero.
1361 
1362 *  Applicability:
1363 *     SphMap
1364 *        All SphMaps have this attribute.
1365 *att--
1366 */
1367 astMAKE_CLEAR(SphMap,UnitRadius,unitradius,-1)
1368 astMAKE_GET(SphMap,UnitRadius,int,0,(this->unitradius == -1 ? 0 : this->unitradius))
1369 astMAKE_SET(SphMap,UnitRadius,int,unitradius,( value ? 1 : 0 ))
1370 astMAKE_TEST(SphMap,UnitRadius,( this->unitradius != -1 ))
1371 
1372 /* PolarLong */
1373 /* --------- */
1374 /*
1375 *att++
1376 *  Name:
1377 *     PolarLong
1378 
1379 *  Purpose:
1380 *     The longitude value to assign to either pole
1381 
1382 *  Type:
1383 *     Public attribute.
1384 
1385 *  Synopsis:
1386 *     Double precision.
1387 
1388 *  Description:
1389 *     This attribute holds the longitude value, in radians, to be
1390 *     returned when a Cartesian position corresponding to either the north
1391 *     or south pole is transformed into spherical coordinates. The
1392 *     default value is zero.
1393 
1394 *  Applicability:
1395 *     SphMap
1396 *        All SphMaps have this attribute.
1397 *att--
1398 */
astMAKE_CLEAR(SphMap,PolarLong,polarlong,AST__BAD)1399 astMAKE_CLEAR(SphMap,PolarLong,polarlong,AST__BAD)
1400 astMAKE_GET(SphMap,PolarLong,double,0.0,(this->polarlong == AST__BAD ? 0.0 : this->polarlong))
1401 astMAKE_SET(SphMap,PolarLong,double,polarlong,value)
1402 astMAKE_TEST(SphMap,PolarLong,( this->polarlong != AST__BAD ))
1403 
1404 /* Copy constructor. */
1405 /* ----------------- */
1406 static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
1407 /*
1408 *  Name:
1409 *     Copy
1410 
1411 *  Purpose:
1412 *     Copy constructor for SphMap objects.
1413 
1414 *  Type:
1415 *     Private function.
1416 
1417 *  Synopsis:
1418 *     void Copy( const AstObject *objin, AstObject *objout, int *status, int *status, int *status )
1419 
1420 *  Description:
1421 *     This function implements the copy constructor for SphMap objects.
1422 
1423 *  Parameters:
1424 *     objin
1425 *        Pointer to the SphMap to be copied.
1426 *     objout
1427 *        Pointer to the SphMap being constructed.
1428 *     status
1429 *        Pointer to the inherited status variable.
1430 *     status
1431 *        Pointer to the inherited status variable.
1432 *     status
1433 *        Pointer to the inherited status variable.
1434 
1435 */
1436 
1437 }
1438 
1439 /* Destructor. */
1440 /* ----------- */
Delete(AstObject * obj,int * status)1441 static void Delete( AstObject *obj, int *status ) {
1442 /*
1443 *  Name:
1444 *     Delete
1445 
1446 *  Purpose:
1447 *     Destructor for SphMap objects.
1448 
1449 *  Type:
1450 *     Private function.
1451 
1452 *  Synopsis:
1453 *     void Delete( AstObject *obj, int *status, int *status )
1454 
1455 *  Description:
1456 *     This function implements the destructor for SphMap objects.
1457 
1458 *  Parameters:
1459 *     obj
1460 *        Pointer to the SphMap to be deleted.
1461 *     status
1462 *        Pointer to the inherited status variable.
1463 *     status
1464 *        Pointer to the inherited status variable.
1465 
1466 *  Notes:
1467 *     - This destructor does nothing and exists only to maintain a
1468 *     one-to-one correspondence between destructors and copy
1469 *     constructors.
1470 */
1471 
1472 
1473 }
1474 
1475 /* Dump function. */
1476 /* -------------- */
Dump(AstObject * this_object,AstChannel * channel,int * status)1477 static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
1478 /*
1479 *  Name:
1480 *     Dump
1481 
1482 *  Purpose:
1483 *     Dump function for SphMap objects.
1484 
1485 *  Type:
1486 *     Private function.
1487 
1488 *  Synopsis:
1489 *     void Dump( AstObject *this, AstChannel *channel, int *status, int *status, int *status, int *status )
1490 
1491 *  Description:
1492 *     This function implements the Dump function which writes out data
1493 *     for the SphMap class to an output Channel.
1494 
1495 *  Parameters:
1496 *     this
1497 *        Pointer to the SphMap whose data are being written.
1498 *     channel
1499 *        Pointer to the Channel to which the data are being written.
1500 *     status
1501 *        Pointer to the inherited status variable.
1502 *     status
1503 *        Pointer to the inherited status variable.
1504 *     status
1505 *        Pointer to the inherited status variable.
1506 *     status
1507 *        Pointer to the inherited status variable.
1508 */
1509 
1510 /* Local Variables: */
1511    AstSphMap *this;              /* Pointer to the SphMap structure */
1512    double dval;                  /* Double precision attribute value */
1513    int ival;                     /* Integer value */
1514    int set;                      /* Attribute value set? */
1515 
1516 /* Check the global error status. */
1517    if ( !astOK ) return;
1518 
1519 /* Obtain a pointer to the SphMap structure. */
1520    this = (AstSphMap *) this_object;
1521 
1522 /* Write out values representing the instance variables for the
1523    SphMap class.  Accompany these with appropriate comment strings,
1524    possibly depending on the values being written.*/
1525 
1526 /* In the case of attributes, we first use the appropriate (private)
1527    Test...  member function to see if they are set. If so, we then use
1528    the (private) Get... function to obtain the value to be written
1529    out.
1530 
1531    For attributes which are not set, we use the astGet... method to
1532    obtain the value instead. This will supply a default value
1533    (possibly provided by a derived class which over-rides this method)
1534    which is more useful to a human reader as it corresponds to the
1535    actual default attribute value.  Since "set" will be zero, these
1536    values are for information only and will not be read back. */
1537 
1538 /* UnitRadius. */
1539 /* ------- */
1540    set = TestUnitRadius( this, status );
1541    ival = set ? GetUnitRadius( this, status ) : astGetUnitRadius( this );
1542    if( ival ) {
1543       astWriteInt( channel, "UntRd", set, 0, ival, "All input vectors have unit length" );
1544    } else {
1545       astWriteInt( channel, "UntRd", set, 0, ival, "Input vectors do not all have unit length" );
1546    }
1547 
1548 /* PolarLong. */
1549 /* ---------- */
1550    set = TestPolarLong( this, status );
1551    dval = set ? GetPolarLong( this, status ) : astGetPolarLong( this );
1552    astWriteDouble( channel, "PlrLg", set, 1, dval, "Polar longitude (rad.s)" );
1553 
1554 }
1555 
1556 /* Standard class functions. */
1557 /* ========================= */
1558 /* Implement the astIsASphMap and astCheckSphMap functions using the macros
1559    defined for this purpose in the "object.h" header file. */
astMAKE_ISA(SphMap,Mapping)1560 astMAKE_ISA(SphMap,Mapping)
1561 astMAKE_CHECK(SphMap)
1562 
1563 AstSphMap *astSphMap_( const char *options, int *status, ...) {
1564 /*
1565 *++
1566 *  Name:
1567 c     astSphMap
1568 f     AST_SPHMAP
1569 
1570 *  Purpose:
1571 *     Create a SphMap.
1572 
1573 *  Type:
1574 *     Public function.
1575 
1576 *  Synopsis:
1577 c     #include "sphmap.h"
1578 c     AstSphMap *astSphMap( const char *options, ... )
1579 f     RESULT = AST_SPHMAP( OPTIONS, STATUS )
1580 
1581 *  Class Membership:
1582 *     SphMap constructor.
1583 
1584 *  Description:
1585 *     This function creates a new SphMap and optionally initialises
1586 *     its attributes.
1587 *
1588 *     A SphMap is a Mapping which transforms points from a
1589 *     3-dimensional Cartesian coordinate system into a 2-dimensional
1590 *     spherical coordinate system (longitude and latitude on a unit
1591 *     sphere centred at the origin). It works by regarding the input
1592 *     coordinates as position vectors and finding their intersection
1593 *     with the sphere surface. The inverse transformation always
1594 *     produces points which are a unit distance from the origin
1595 *     (i.e. unit vectors).
1596 
1597 *  Parameters:
1598 c     options
1599 f     OPTIONS = CHARACTER * ( * ) (Given)
1600 c        Pointer to a null-terminated string containing an optional
1601 c        comma-separated list of attribute assignments to be used for
1602 c        initialising the new SphMap. The syntax used is identical to
1603 c        that for the astSet function and may include "printf" format
1604 c        specifiers identified by "%" symbols in the normal way.
1605 f        A character string containing an optional comma-separated
1606 f        list of attribute assignments to be used for initialising the
1607 f        new SphMap. The syntax used is identical to that for the
1608 f        AST_SET routine.
1609 c     ...
1610 c        If the "options" string contains "%" format specifiers, then
1611 c        an optional list of additional arguments may follow it in
1612 c        order to supply values to be substituted for these
1613 c        specifiers. The rules for supplying these are identical to
1614 c        those for the astSet function (and for the C "printf"
1615 c        function).
1616 f     STATUS = INTEGER (Given and Returned)
1617 f        The global status.
1618 
1619 *  Returned Value:
1620 c     astSphMap()
1621 f     AST_SPHMAP = INTEGER
1622 *        A pointer to the new SphMap.
1623 
1624 *  Notes:
1625 *     - The spherical coordinates are longitude (positive
1626 *     anti-clockwise looking from the positive latitude pole) and
1627 *     latitude. The Cartesian coordinates are right-handed, with the x
1628 *     axis (axis 1) at zero longitude and latitude, and the z axis
1629 *     (axis 3) at the positive latitude pole.
1630 *     - At either pole, the longitude is set to the value of the
1631 *     PolarLong attribute.
1632 *     - If the Cartesian coordinates are all zero, then the longitude
1633 *     and latitude are set to the value AST__BAD.
1634 *     - A null Object pointer (AST__NULL) will be returned if this
1635 c     function is invoked with the AST error status set, or if it
1636 f     function is invoked with STATUS set to an error value, or if it
1637 *     should fail for any reason.
1638 
1639 *  Status Handling:
1640 *     The protected interface to this function includes an extra
1641 *     parameter at the end of the parameter list descirbed above. This
1642 *     parameter is a pointer to the integer inherited status
1643 *     variable: "int *status".
1644 
1645 
1646 *  Status Handling:
1647 *     The protected interface to this function includes an extra
1648 *     parameter at the end of the parameter list descirbed above. This
1649 *     parameter is a pointer to the integer inherited status
1650 *     variable: "int *status".
1651 
1652 
1653 *  Status Handling:
1654 *     The protected interface to this function includes an extra
1655 *     parameter at the end of the parameter list descirbed above. This
1656 *     parameter is a pointer to the integer inherited status
1657 *     variable: "int *status".
1658 
1659 *--
1660 */
1661 
1662 /* Local Variables: */
1663    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
1664    AstSphMap *new;              /* Pointer to new SphMap */
1665    va_list args;                /* Variable argument list */
1666 
1667 /* Get a pointer to the thread specific global data structure. */
1668    astGET_GLOBALS(NULL);
1669 
1670 /* Check the global status. */
1671    if ( !astOK ) return NULL;
1672 
1673 /* Initialise the SphMap, allocating memory and initialising the
1674    virtual function table as well if necessary. */
1675    new = astInitSphMap( NULL, sizeof( AstSphMap ), !class_init, &class_vtab,
1676                         "SphMap" );
1677 
1678 /* If successful, note that the virtual function table has been
1679    initialised. */
1680    if ( astOK ) {
1681       class_init = 1;
1682 
1683 /* Obtain the variable argument list and pass it along with the options string
1684    to the astVSet method to initialise the new SphMap's attributes. */
1685       va_start( args, status );
1686       astVSet( new, options, NULL, args );
1687       va_end( args );
1688 
1689 /* If an error occurred, clean up by deleting the new object. */
1690       if ( !astOK ) new = astDelete( new );
1691    }
1692 
1693 /* Return a pointer to the new SphMap. */
1694    return new;
1695 }
1696 
astSphMapId_(const char * options,...)1697 AstSphMap *astSphMapId_( const char *options, ...) {
1698 /*
1699 *  Name:
1700 *     astSphMapId_
1701 
1702 *  Purpose:
1703 *     Create a SphMap.
1704 
1705 *  Type:
1706 *     Private function.
1707 
1708 *  Synopsis:
1709 *     #include "sphmap.h"
1710 *     AstSphMap *astSphMapId_( const char *options, ... )
1711 
1712 *  Class Membership:
1713 *     SphMap constructor.
1714 
1715 *  Description:
1716 *     This function implements the external (public) interface to the
1717 *     astSphMap constructor function. It returns an ID value (instead
1718 *     of a true C pointer) to external users, and must be provided
1719 *     because astSphMap_ has a variable argument list which cannot be
1720 *     encapsulated in a macro (where this conversion would otherwise
1721 *     occur).
1722 *
1723 *     The variable argument list also prevents this function from
1724 *     invoking astSphMap_ directly, so it must be a re-implementation
1725 *     of it in all respects, except for the final conversion of the
1726 *     result to an ID value.
1727 
1728 *  Parameters:
1729 *     As for astSphMap_.
1730 
1731 *  Returned Value:
1732 *     The ID value associated with the new SphMap.
1733 */
1734 
1735 /* Local Variables: */
1736    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
1737    AstSphMap *new;              /* Pointer to new SphMap */
1738    va_list args;                /* Variable argument list */
1739    int *status;                 /* Pointer to inherited status value */
1740 
1741 /* Get a pointer to the inherited status value. */
1742    status = astGetStatusPtr;
1743 
1744 /* Get a pointer to the thread specific global data structure. */
1745    astGET_GLOBALS(NULL);
1746 
1747 /* Check the global status. */
1748    if ( !astOK ) return NULL;
1749 
1750 /* Initialise the SphMap, allocating memory and initialising the
1751    virtual function table as well if necessary. */
1752    new = astInitSphMap( NULL, sizeof( AstSphMap ), !class_init, &class_vtab,
1753                         "SphMap" );
1754 
1755 /* If successful, note that the virtual function table has been
1756    initialised. */
1757    if ( astOK ) {
1758       class_init = 1;
1759 
1760 /* Obtain the variable argument list and pass it along with the options string
1761    to the astVSet method to initialise the new SphMap's attributes. */
1762       va_start( args, options );
1763       astVSet( new, options, NULL, args );
1764       va_end( args );
1765 
1766 /* If an error occurred, clean up by deleting the new object. */
1767       if ( !astOK ) new = astDelete( new );
1768    }
1769 
1770 /* Return an ID value for the new SphMap. */
1771    return astMakeId( new );
1772 }
1773 
astInitSphMap_(void * mem,size_t size,int init,AstSphMapVtab * vtab,const char * name,int * status)1774 AstSphMap *astInitSphMap_( void *mem, size_t size, int init,
1775                            AstSphMapVtab *vtab, const char *name, int *status ) {
1776 /*
1777 *+
1778 *  Name:
1779 *     astInitSphMap
1780 
1781 *  Purpose:
1782 *     Initialise a SphMap.
1783 
1784 *  Type:
1785 *     Protected function.
1786 
1787 *  Synopsis:
1788 *     #include "sphmap.h"
1789 *     AstSphMap *astInitSphMap( void *mem, size_t size, int init,
1790 *                               AstSphMapVtab *vtab, const char *name )
1791 
1792 *  Class Membership:
1793 *     SphMap initialiser.
1794 
1795 *  Description:
1796 *     This function is provided for use by class implementations to initialise
1797 *     a new SphMap object. It allocates memory (if necessary) to accommodate
1798 *     the SphMap plus any additional data associated with the derived class.
1799 *     It then initialises a SphMap structure at the start of this memory. If
1800 *     the "init" flag is set, it also initialises the contents of a virtual
1801 *     function table for a SphMap at the start of the memory passed via the
1802 *     "vtab" parameter.
1803 
1804 *  Parameters:
1805 *     mem
1806 *        A pointer to the memory in which the SphMap is to be initialised.
1807 *        This must be of sufficient size to accommodate the SphMap data
1808 *        (sizeof(SphMap)) plus any data used by the derived class. If a value
1809 *        of NULL is given, this function will allocate the memory itself using
1810 *        the "size" parameter to determine its size.
1811 *     size
1812 *        The amount of memory used by the SphMap (plus derived class data).
1813 *        This will be used to allocate memory if a value of NULL is given for
1814 *        the "mem" parameter. This value is also stored in the SphMap
1815 *        structure, so a valid value must be supplied even if not required for
1816 *        allocating memory.
1817 *     init
1818 *        A logical flag indicating if the SphMap's virtual function table is
1819 *        to be initialised. If this value is non-zero, the virtual function
1820 *        table will be initialised by this function.
1821 *     vtab
1822 *        Pointer to the start of the virtual function table to be associated
1823 *        with the new SphMap.
1824 *     name
1825 *        Pointer to a constant null-terminated character string which contains
1826 *        the name of the class to which the new object belongs (it is this
1827 *        pointer value that will subsequently be returned by the astGetClass
1828 *        method).
1829 
1830 *  Returned Value:
1831 *     A pointer to the new SphMap.
1832 
1833 *  Notes:
1834 *     -  A null pointer will be returned if this function is invoked with the
1835 *     global error status set, or if it should fail for any reason.
1836 *-
1837 */
1838 
1839 /* Local Variables: */
1840    AstSphMap *new;              /* Pointer to new SphMap */
1841 
1842 /* Check the global status. */
1843    if ( !astOK ) return NULL;
1844 
1845 /* If necessary, initialise the virtual function table. */
1846    if ( init ) astInitSphMapVtab( vtab, name );
1847 
1848 /* Initialise. */
1849    new = NULL;
1850 
1851 /* Initialise a Mapping structure (the parent class) as the first component
1852    within the SphMap structure, allocating memory if necessary. Specify that
1853    the Mapping should be defined in both the forward and inverse directions. */
1854    new = (AstSphMap *) astInitMapping( mem, size, 0,
1855                                        (AstMappingVtab *) vtab, name,
1856                                        3, 2, 1, 1 );
1857 
1858    if ( astOK ) {
1859 
1860 /* Initialise the SphMap data. */
1861 /* --------------------------- */
1862 /* Are all input vectors of unit length? Store a value of -1 to indicate that
1863    no value has yet been set. This will cause a default value of 0 (no, i.e.
1864    input vectors are not all of unit length) to be used. */
1865       new->unitradius = -1;
1866       new->polarlong = AST__BAD;
1867 
1868    }
1869 
1870 /* Return a pointer to the new SphMap. */
1871    return new;
1872 }
1873 
astLoadSphMap_(void * mem,size_t size,AstSphMapVtab * vtab,const char * name,AstChannel * channel,int * status)1874 AstSphMap *astLoadSphMap_( void *mem, size_t size,
1875                            AstSphMapVtab *vtab, const char *name,
1876                            AstChannel *channel, int *status ) {
1877 /*
1878 *+
1879 *  Name:
1880 *     astLoadSphMap
1881 
1882 *  Purpose:
1883 *     Load a SphMap.
1884 
1885 *  Type:
1886 *     Protected function.
1887 
1888 *  Synopsis:
1889 *     #include "sphmap.h"
1890 *     AstSphMap *astLoadSphMap( void *mem, size_t size,
1891 *                               AstSphMapVtab *vtab, const char *name,
1892 *                               AstChannel *channel )
1893 
1894 *  Class Membership:
1895 *     SphMap loader.
1896 
1897 *  Description:
1898 *     This function is provided to load a new SphMap using data read
1899 *     from a Channel. It first loads the data used by the parent class
1900 *     (which allocates memory if necessary) and then initialises a
1901 *     SphMap structure in this memory, using data read from the input
1902 *     Channel.
1903 *
1904 *     If the "init" flag is set, it also initialises the contents of a
1905 *     virtual function table for a SphMap at the start of the memory
1906 *     passed via the "vtab" parameter.
1907 
1908 
1909 *  Parameters:
1910 *     mem
1911 *        A pointer to the memory into which the SphMap is to be
1912 *        loaded.  This must be of sufficient size to accommodate the
1913 *        SphMap data (sizeof(SphMap)) plus any data used by derived
1914 *        classes. If a value of NULL is given, this function will
1915 *        allocate the memory itself using the "size" parameter to
1916 *        determine its size.
1917 *     size
1918 *        The amount of memory used by the SphMap (plus derived class
1919 *        data).  This will be used to allocate memory if a value of
1920 *        NULL is given for the "mem" parameter. This value is also
1921 *        stored in the SphMap structure, so a valid value must be
1922 *        supplied even if not required for allocating memory.
1923 *
1924 *        If the "vtab" parameter is NULL, the "size" value is ignored
1925 *        and sizeof(AstSphMap) is used instead.
1926 *     vtab
1927 *        Pointer to the start of the virtual function table to be
1928 *        associated with the new SphMap. If this is NULL, a pointer
1929 *        to the (static) virtual function table for the SphMap class
1930 *        is used instead.
1931 *     name
1932 *        Pointer to a constant null-terminated character string which
1933 *        contains the name of the class to which the new object
1934 *        belongs (it is this pointer value that will subsequently be
1935 *        returned by the astGetClass method).
1936 *
1937 *        If the "vtab" parameter is NULL, the "name" value is ignored
1938 *        and a pointer to the string "SphMap" is used instead.
1939 
1940 *  Returned Value:
1941 *     A pointer to the new SphMap.
1942 
1943 *  Notes:
1944 *     - A null pointer will be returned if this function is invoked
1945 *     with the global error status set, or if it should fail for any
1946 *     reason.
1947 *-
1948 */
1949 
1950 #define KEY_LEN 50               /* Maximum length of a keyword */
1951 
1952 /* Local Variables: */
1953    astDECLARE_GLOBALS            /* Pointer to thread-specific global data */
1954    AstSphMap *new;               /* Pointer to the new SphMap */
1955 
1956 /* Initialise. */
1957    new = NULL;
1958 
1959 /* Check the global error status. */
1960    if( !astOK ) return new;
1961 
1962 /* Get a pointer to the thread specific global data structure. */
1963    astGET_GLOBALS(channel);
1964 
1965 /* If a NULL virtual function table has been supplied, then this is
1966    the first loader to be invoked for this SphMap. In this case the
1967    SphMap belongs to this class, so supply appropriate values to be
1968    passed to the parent class loader (and its parent, etc.). */
1969    if ( !vtab ) {
1970       size = sizeof( AstSphMap );
1971       vtab = &class_vtab;
1972       name = "SphMap";
1973 
1974 /* If required, initialise the virtual function table for this class. */
1975       if ( !class_init ) {
1976          astInitSphMapVtab( vtab, name );
1977          class_init = 1;
1978       }
1979    }
1980 
1981 /* Invoke the parent class loader to load data for all the ancestral
1982    classes of the current one, returning a pointer to the resulting
1983    partly-built SphMap. */
1984    new = astLoadMapping( mem, size, (AstMappingVtab *) vtab, name,
1985                          channel );
1986 
1987    if ( astOK ) {
1988 
1989 /* Read input data. */
1990 /* ================ */
1991 /* Request the input Channel to read all the input data appropriate to
1992    this class into the internal "values list". */
1993       astReadClassData( channel, "SphMap" );
1994 
1995 /* Now read each individual data item from this list and use it to
1996    initialise the appropriate instance variable(s) for this class. */
1997 
1998 /* In the case of attributes, we first read the "raw" input value,
1999    supplying the "unset" value as the default. If a "set" value is
2000    obtained, we then use the appropriate (private) Set... member
2001    function to validate and set the value properly. */
2002 
2003 /* UnitRadius. */
2004 /* ----------- */
2005       new->unitradius = astReadInt( channel, "untrd", -1 );
2006       if ( TestUnitRadius( new, status ) ) SetUnitRadius( new, new->unitradius, status );
2007 
2008 /* PolarLong. */
2009 /* ---------- */
2010       new->polarlong = astReadDouble( channel, "plrlg", AST__BAD );
2011       if ( TestPolarLong( new, status ) ) SetPolarLong( new, new->polarlong, status );
2012 
2013    }
2014 
2015 /* If an error occurred, clean up by deleting the new SphMap. */
2016    if ( !astOK ) new = astDelete( new );
2017 
2018 /* Return the new SphMap pointer. */
2019    return new;
2020 }
2021 
2022 /* Virtual function interfaces. */
2023 /* ============================ */
2024 /* These provide the external interface to the virtual functions defined by
2025    this class. Each simply checks the global error status and then locates and
2026    executes the appropriate member function, using the function pointer stored
2027    in the object's virtual function table (this pointer is located using the
2028    astMEMBER macro defined in "object.h").
2029 
2030    Note that the member function may not be the one defined here, as it may
2031    have been over-ridden by a derived class. However, it should still have the
2032    same interface. */
2033 
2034 
2035 
2036 
2037 
2038