-
Notifications
You must be signed in to change notification settings - Fork 0
/
acast.tex
2172 lines (1875 loc) · 110 KB
/
acast.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
%% This is file `elsarticle-template-2-harv.tex',
%%
%% Copyright 2009 Elsevier Ltd
%%
%% This file is part of the 'Elsarticle Bundle'.
%% ---------------------------------------------
%%
%% It may be distributed under the conditions of the LaTeX Project Public
%% License, either version 1.2 of this license or (at your option) any
%% later version. The latest version of this license is in
%% http://www.latex-project.org/lppl.txt
%% and version 1.2 or later is part of all distributions of LaTeX
%% version 1999/12/01 or later.
%%
%% The list of all files belonging to the 'Elsarticle Bundle' is
%% given in the file `manifest.txt'.
%%
%% Template article for Elsevier's document class `elsarticle'
%% with harvard style bibliographic references
%%
%% $Id: elsarticle-template-2-harv.tex 155 2009-10-08 05:35:05Z rishi $
%% $URL: http://lenova.river-valley.com/svn/elsbst/trunk/elsarticle-template-2-harv.tex $
%%
%%\documentclass[preprint,authoryear,12pt]{elsarticle}
%% Use the option review to obtain double line spacing
%% \documentclass[authoryear,preprint,review,12pt]{elsarticle}
%% Use the options 1p,twocolumn; 3p; 3p,twocolumn; 5p; or 5p,twocolumn
%% for a journal layout:
%% Astronomy & Computing uses 5p
%% \documentclass[final,authoryear,5p,times]{elsarticle}
\documentclass[final,authoryear,5p,times,twocolumn]{elsarticle}
%% if you use PostScript figures in your article
%% use the graphics package for simple commands
%% \usepackage{graphics}
%% or use the graphicx package for more complicated commands
\usepackage{graphicx}
%% or use the epsfig package if you prefer to use the old commands
%% \usepackage{epsfig}
%% The amssymb package provides various useful mathematical symbols
\usepackage{amssymb}
%% The amsthm package provides extended theorem environments
%% \usepackage{amsthm}
\usepackage[pdftex,pdfpagemode={UseOutlines},bookmarks,bookmarksopen,colorlinks,linkcolor={blue},citecolor={green},urlcolor={red}]{hyperref}
\usepackage{hypernat}
\usepackage{listings}
\usepackage{color}
\definecolor{mygreen}{rgb}{0,0.5,0}
\lstset{
language=Python,
basicstyle=\scriptsize\ttfamily,
showstringspaces=false,
stringstyle=\color{black},
keywordstyle=\bfseries\color{black},
morecomment=[l]{/},
commentstyle=\color{black},
}
%% The lineno packages adds line numbers. Start line numbering with
%% \begin{linenumbers}, end it with \end{linenumbers}. Or switch it on
%% for the whole article with \linenumbers after \end{frontmatter}.
%% \usepackage{lineno}
%% natbib.sty is loaded by default. However, natbib options can be
%% provided with \biboptions{...} command. Following options are
%% valid:
%% round - round parentheses are used (default)
%% square - square brackets are used [option]
%% curly - curly braces are used {option}
%% angle - angle brackets are used <option>
%% semicolon - multiple citations separated by semi-colon (default)
%% colon - same as semicolon, an earlier confusion
%% comma - separated by comma
%% authoryear - selects author-year citations (default)
%% numbers- selects numerical citations
%% super - numerical citations as superscripts
%% sort - sorts multiple citations according to order in ref. list
%% sort&compress - like sort, but also compresses numerical citations
%% compress - compresses without sorting
%% longnamesfirst - makes first citation full author list
%%
%% \biboptions{longnamesfirst,comma}
% \biboptions{}
\journal{Astronomy \& Computing}
%% Make single quotes look right in verbatim mode
\usepackage{upquote}
\begin{document}
\begin{frontmatter}
%% Title, authors and addresses
%% use the tnoteref command within \title for footnotes;
%% use the tnotetext command for the associated footnote;
%% use the fnref command within \author or \address for footnotes;
%% use the fntext command for the associated footnote;
%% use the corref command within \author for corresponding author footnotes;
%% use the cortext command for the associated footnote;
%% use the ead command for the email address,
%% and the form \ead[url] for the home page:
%%
%% \title{Title\tnoteref{label1}}
%% \tnotetext[label1]{}
%% \author{Name\corref{cor1}\fnref{label2}}
%% \ead{email address}
%% \ead[url]{home page}
%% \fntext[label2]{}
%% \cortext[cor1]{}
%% \address{Address\fnref{label3}}
%% \fntext[label3]{}
\title{AST: A library for modelling and manipulating coordinate systems\tnoteref{ascl}}
\tnotetext[ascl]{This code is registered at the ASCL with the code
entry ascl: \href{http://www.ascl.net/1404.016}{1404.016}}
%% use optional labels to link authors explicitly to addresses:
%% \author[label1,label2]{<author name>}
%% \address[label1]{<address>}
%% \address[label2]{<address>}
\author[jac,eao]{David S.\ Berry\corref{cor1}}
\ead{[email protected]}
\author[ral]{Rodney F.\ Warren-Smith}
\author[jac,lsst]{Tim Jenness}
\cortext[cor1]{Corresponding author}
\address[jac]{Joint Astronomy Centre, 660 N.\ A`oh\=ok\=u Place, Hilo, HI
96720, USA}
\address[eao]{East Asian Observatory, 660 N.\ A`oh\=ok\=u Place, Hilo, HI
96720, USA}
\address[ral]{RAL Space, STFC Rutherford Appleton Laboratory, Harwell Oxford, Didcot, Oxfordshire OX11 0QX, UK}
\address[lsst]{LSST Project Office, 933 N.\ Cherry Ave, Tucson, AZ~85721, USA}
\begin{abstract}
In view of increased interest in object-oriented systems for describing
coordinate information, we present a description of the data model used
by the Starlink AST library. AST provides a comprehensive range of
facilities for attaching world co-ordinate systems to astronomical data,
and for retrieving and interpreting that information in a variety of
formats, including FITS-WCS. AST is a mature system that has been in use
for more than 17 years, and may consequently be useful as a means of
informing development of similar systems in the future.
\end{abstract}
\begin{keyword}
%% keywords here, in the form: keyword \sep keyword
%% MSC codes here, in the form: \MSC code \sep code
%% or \MSC[2008] code \sep code (2000 is the default)
WCS \sep data models \sep Starlink
\end{keyword}
\end{frontmatter}
% \linenumbers
%% Journal abbreviations
\newcommand{\mnras}{Mon Not R Astron Soc}
\newcommand{\aap}{Astron Astrophys}
\newcommand{\aaps}{Astron Astrophys Supp}
\newcommand{\pasp}{Pub Astron Soc Pacific}
\newcommand{\apj}{Astrophys J}
\newcommand{\apjs}{Astrophys J Supp}
\newcommand{\qjras}{Quart J R Astron Soc}
\newcommand{\an}{Astron.\ Nach.}
\newcommand{\ijimw}{Int.\ J.\ Infrared \& Millimeter Waves}
\newcommand{\procspie}{Proc.\ SPIE}
\newcommand{\aspconf}{ASP Conf. Ser.}
%% ASCL
\newcommand{\ascl}[1]{\href{http://www.ascl.net/#1}{ascl:#1}}
%% main text
\section{Introduction}
\label{sec:intro}
The Starlink AST library \citep[][\ascl{1404.016}]{SUN211} provides a
generalised scheme for modelling, manipulating and storing inter-related
coordinate systems. Whilst written in C, it has bindings for several
other languages including Python, Java, Perl and Fortran. It has
specialised support for many of the coordinate systems and projections
commonly used to describe astronomical World Coordinate Systems (WCS),
including all the celestial and spectral coordinate systems described by
the FITS-WCS standard \citep{FITSWCSI,FITSWCSII,FITSWCSIII}, plus various
popular distortion schemes currently in use. However, it is not limited to
WCS, and may be used in any situation requiring transformation between
different coordinate systems.
Unlike FITS-WCS, which supports only a relatively small set of prescribed
transformation recipes reflecting the coordinate transformations within
an optical telescope, AST allows arbitrarily complex transformations to be
constructed by combining simple atomic transformations. This allows a much
wider range of transformations to be
described than is possible using FITS-WCS, and so can accommodate a wider
range of data storage forms without the need to re-grid the data.
AST was released in 1998 \cite[][included in ``Twenty Years of
ADASS''; \citet{adass20}]{1998ASPC..145...41W}. Since then it has been in
continuous use within the Starlink Software Collection \citep[][\ascl{1110.012}]{2014ASPC..485..391C}
and is also used by various other major astronomical software tools such as
DS9 \citep[][\ascl{0003.002}]{2003ASPC..295..489J} and SPLAT-VO \citep[][\ascl{1402.008}]{2014A&C.....7..108S}.
Interest in flexible schemes for representing inter-related coordinate
systems has increased recently --- for instance, in the discussions
about possible successors to the FITS format
\citep{2015Mink,2015A&C....12..240G,2016Shortridge}, and within the
Astropy \citep{2013A&A...558A..33A,P028_adassxxv} and International
Virtual Observatory Alliance (IVOA) (STC2; Rots, in preparation)
projects. These discussions suggest it is an appropriate time to
review the lessons learned from AST.
This paper first presents an account of the historical issues that drove
the initial development of AST, together with the reasoning behind some
of the design decisions, and then presents an over-view of the more
important aspects of the data model used by AST.
\section{Historical Perspective}
\subsection{Initial Problems}
The first public release of the AST library was in 1998
\citep{1998StarB..20....6L,1998StarB..20....7D} but some of the
underlying concepts date from the late 1980s, when the Starlink
Project was designing its NDF data format for gridded astronomical
data \citep[see][and references therein]{2015Jenness}. There was clearly a need to relate positions
within gridded data, using coordinates based on pixel indices, to
real-world positions on the sky, wavelengths in a spectrum and so
on.
Calibration of spectra, for example, was commonly performed by fitting
a polynomial to express wavelength as a function of pixel position and
then either storing the polynomial coefficients, or tabulating the
polynomial value at each pixel centre. While not completely general,
the latter option was an acceptable solution and was adopted as part
of the Starlink data format. An array giving the central wavelength at
each pixel was stored as the \texttt{AXIS} component in the NDF data
structure and did good service in spectroscopic applications. It was
also possible, in a simple minded way, to attach an \texttt{AXIS}
array to each dimension of an image or any gridded data set of higher
dimensionality. This allowed each of its axes to be calibrated in
terms of world coordinates.
This approach was adequate if the axes represented independent
quantities (like wavelength and position for a long-slit spectrum),
but did not suffice if the axes were inter-independent. Unfortunately,
in the common case of celestial coordinates (such as Right Ascension
and Declination), the axes are almost always inter-dependent. This is
because the sky is essentially spherical and its coordinates are
therefore naturally curvilinear when projected into two
dimensions. This inter-dependence is a common feature of world
coordinate systems in practice, so a solution was clearly needed that
addressed it properly.
The Flexible Image Transport System
\citep[FITS;][]{1981A&AS...44..363W,1995ASPC...77..233G}, at that
time, addressed the issue in a better but still rudimentary way. In
essence, it stored a physical pixel size (\emph{e.g.}, in seconds of arc),
allowed for a linear scaling of an image (typically to allow for the
position angle rotation of the telescope) and then projected it on to
the celestial sphere using one of a defined set of map
projections. This representation was clearly based on a model of a
physical telescope and how it imaged an observed region of the sky in
its focal plane.
While successfully accommodating the curvilinear nature of sky
coordinates, this FITS approach was still limited in many ways. In
essence, it defined a small set of functional forms (based on map
projections) through which pixel coordinates could be mapped on to
celestial coordinates and back again. However, if the actual
relationship between pixel coordinates and world coordinates didn't
correspond to one of these functional forms, then it wasn't possible
to use FITS to store the coordinate information\footnote{Unless the data was
first re-gridded into a form supported by FITS.}.
For instance, if astronomical instrumentation were to use a novel map
projection, if arbitrary instrumental distortions were present or if
the data were re-gridded into a non-physical space, then the FITS
approach would fail. It also had limited support for high-accuracy
astrometry, where the departure of the sky from a perfect sphere, for
a variety of reasons, has to be taken into account. In addition, there
are many other non-celestial world coordinate systems that one might
use (involving energy, velocity, time, frequency, \emph{etc}.) that no
contemporary system could represent adequately.
Unfortunately, this list of limitations only scratches the surface of
the problem as it was perceived at the time. Other considerations,
such as the time-dependent relationship between non-inertial celestial
coordinate systems, the dependence of apparent positions on the
position and velocity of the observer (and also on the wavelength of
observation and atmospheric conditions) and periodic revisions to the
fundamental definitions of celestial coordinate and time systems would
all have to be accommodated, as would numerous other issues specific
to particular domains (celestial coordinates, time systems, radial
velocities, wavelength/energy, \emph{etc}.). This was several years before the
FITS community commenced work on what was eventually to become the current
FITS-WCS standard.
\subsection{TRANSFORM}
In the late 1980s, no immediate and general solution to these problems
could be seen. Recognising the limitations in the FITS approach,
however, the Starlink Project decided to take a hard line and to omit
completely any component dedicated to world coordinate systems from
its new NDF data format. Instead, this \emph{astrometry extension}
(from which the name AST is derived) was to be added at a later date
when a suitable solution had been formulated.
This decision was undoubtedly strongly influenced by Patrick Wallace's
presence in the Project and the major work he had done on the SLALIB
library \citep[][\ascl{1403.025}]{1994ASPC...61..481W} to encapsulate best-practice in
astrometric calculations (and also in other domains such as time
systems). Discussion within the Project rapidly convinced us that if
we adopted the FITS approach as it existed at the time, we would cut
ourselves off from the proper rigorous treatment of astrometric data
that is needed for the highest accuracy.
Consequently, a pilot project was conducted to explore alternative
approaches. The most important limitation of the FITS approach was felt
to be the use of a fixed set of functional forms (map projections) each
of which was associated with a small fixed set of parameters. This
simplified storing the information in 80-character FITS \emph{header cards},
but clearly the set of functional forms that might ultimately be needed
was much larger than had been recognised. Adding new ones might become a
never-ending project and that, in turn, raised the prospect of
continually upgrading all software that had to read and process FITS
headers and handle coordinate systems.
The alternative approach that we explored was to write an expression
parser that would accept sets of arithmetic expressions similar to those
used in Fortran and C, along with the usual set of mathematical
functions. Together with a method of passing named parameter values into
these expressions, this greatly increased the set of functional forms
that could be represented. The expressions themselves (encoded as
character strings) and the associated parameter values could easily be
stored in astronomical data sets. Typically, one set of expressions would
relate pixel coordinates to world coordinates (\emph{e.g.}, sky coordinates) and
a second, optional, set would define the inverse transformation. The
expression syntax was powerful enough to represent a wide range of map
projections plus many other transformations into alternative world
coordinate systems.
A processing engine was also provided that could use the stored
expression data to transform actual coordinate values.
A library implementing this, called \textsc{transform}, was released
in 1989 \citep{SUN61,1989StarB...4....7L}. It stored its data (the
expressions and parameters) in Starlink's Hierarchical Data Format
\citep[HDS;][]{SUN92,SSN27,2015HDS} and was thus able to integrate with the
Starlink NDF data format to attach arbitrary world coordinates to
gridded astronomical data sets.
\subsection{TRANSFORM Lessons}
Ultimately, \textsc{transform} turned out not to be a full solution to
the WCS problem and did not become part of the NDF data
format\footnote{Although it was the precursor of the
\texttt{MathMap} class in AST.}. It was, however, used for two
initially unforeseen purposes which turned out to be very significant:
\begin{enumerate}
\item Associating coordinate systems with plotting surfaces in a
``graphics database'' \citep[see \emph{e.g.},][]{SUN48}. This allowed
plotting applications to store a coordinate system for (say) a graph
plotted in logarithmic coordinates so that those coordinates could
later be recovered from the position of a cursor. This demonstrated
that plotting was a major application area for this type of
technology, especially when using curvilinear coordinates such as
Right Ascension and Declination which are notoriously difficult to
handle properly with standard plotting software.
\item Transformation and combination of bulk image data using general
arithmetic expressions (as an alternative to combining images using
a manual sequence of add/subtract/multiply/divide and similar
applications). This showed that (a) the approach could easily be
efficient enough to handle large data sets and (b) the data values in
an image were just another coordinate that could be transformed into
different representations (logarithmic, different units, \emph{etc}.) in
much the same way as its axes.
\end{enumerate}
With these insights, it was clear that the ideas behind
\textsc{transform} had potential, but some serious deficiencies had
also emerged:
\begin{itemize}
\item Arithmetic expressions, while fairly general, could not easily
cope with coordinate transformations that required iterative
solution, nor with discontinuous transformations, nor with look-up
tables or a variety of other computational techniques. While
arithmetic expressions provided a valuable increase in the
flexibility of coordinate transformations, clearly other classes
were still needed.
\item It was a major problem for the average writer of astronomical
software to formulate the required coordinate expressions correctly
even when dealing with quite simple sky coordinate systems. The core
of this issue is that celestial coordinate systems are rather
complex and a good deal of specialist knowledge is needed to
formulate even simple cases correctly. Clearly a better solution
would be to encapsulate this knowledge in the WCS software and
provide a simpler API that dealt only in high-level concepts.
\item For high accuracy work, further complex calculations
arise. These are related, for example, to atmospheric refraction and
special \& general relativistic effects (like the observer's motion
and the sun's gravity). These require the use of a dedicated
library of astrometric functions and cannot in practice be handled
by simple expressions. They also require additional data about the
observing context (time, position, velocity, wavelength, \emph{etc}.) and
any practical solution must define how these are stored and
processed.
\item \textsc{transform} had no ability to store additional
information about data axes, such as labels and units.
\item It became clear that coordinate transformations frequently
needed to be combined, for example by applying one transformation
after another, and that this process was often inefficient. The key
to better efficiency lay in knowing more about each transformation,
like whether it was linear or had a variety of other
properties. With this information it was possible to merge (or
cancel out) consecutive transformations for better
efficiency. \textsc{transform} had a rudimentary system for encoding
this information, but it was not really up to the task.
\item Tying WCS software to a particular (Starlink) data system was a
mistake and limited the uses to which it could be put. It would
clearly be better if the data could be encoded (serialised) in
alternative ways to make it data-system agnostic. The same
agnosticism should also apply to other likely dependencies, like
graphics systems and error reporting. The ability to implement
these services in alternative ways would be especially important
when designing graphical user interfaces that processed WCS
information.
\end{itemize}
\subsection{Developments in FITS WCS}
At about the same time, the wider FITS community also came to recognise
some of the limitations of WCS handling within FITS, and in 1992 work commenced
on a new standard for storing WCS information within FITS files. However,
in view of the ``once FITS, always FITS'' principle \citep[see \emph{e.g.},][]{1997ASPC..125..257W}, that work consisted
mainly in formalising and extending existing practices. So for instance, new
keywords were defined to store the extra meta-data needed for a complete
description of a celestial coordinate system, and new projection types were
added, but the basic model remained unchanged. The new standard still required
that the transformation be split into three components applied in series; an
affine transformation that converts pixel coordinates into \emph{intermediate
world coordinates}, a spherical projection that converts these into \emph{native
spherical coordinates}, and a spherical rotation that converts these into the
final world coordinate system.
In view of the decision to stay with this rigid and restrictive model,
and the expected length of time needed to agree a new standard\footnote{An
expectation that was justified when the standard was finally published in 2002.},
the Starlink project decided in early 1996 to develop its own WCS system, informed
by the earlier experiments with \textsc{transform}, rather than adopt the new FITS
standard.
\subsection{AST Principles}
One of the first decisions was to
separate the representation of a coordinate system (that we called a
Frame) from the computational recipe that transforms between
coordinate systems (a Mapping). From the \textsc{transform} experience
we knew we would need multiple classes of both these data types, all
of which would need to support the same basic operations, but each
having its own specialisation. The correspondence with sub-classing in
object-oriented (OO) programming was irresistible and the decision to
use an OO design immediately followed.
This raised the issue of an implementation language. We planned to use
the SLALIB library for astrometric calculations\footnote{A later version
of AST eventually replaced SLALIB with SOFA and PAL.}. This had been
developed with extreme portability in mind and had recently been
re-written in ANSI C. We didn't want to compromise this portability,
so decided also to work in strict ANSI C and to minimise software
dependencies as much as possible. This meant providing portable
interfaces to facilities that were intrinsically less portable, such
as data file access, plotting, error reporting, \emph{etc}. and providing
simple implementations that users could re-write if necessary.
Deciding to write an OO system in a non-OO language took considerable
thought. We needed to provide a Fortran-callable interface but, at the
time, the portability of C++ code was quite limited if one needed to
call it from Fortran, so that route was unattractive\footnote{Further, there
was no official C++ standard available at the time - the first international
standard for C++ (ISO/IEC 14882:1998) was published in 1998.}. Eventually, we
were guided by the approach described by \citet{1992Holub} for
handling objects in C and were able to hide the detail from users
using pre-processor macros.
One consequence of this is that users cannot easily create new
sub-classes from AST objects without learning the internal conventions
that it uses. At the time, this was seen as something of an advantage.
The library is intended for data interchange and creating new
sub-classes would inevitably allow persistent objects to be created
that other users could not access. However, with hind-sight a more open
architecture may have encouraged involvement from a wider user-base
\footnote{For Mappings, where this
issue is most relevant, the problem has been mitigated in a
controlled way by the IntraMap class that allows separately-compiled
code to be imported into the library.}.
As noted previously, we wanted the AST API to deal in high-level
concepts and to hide as much specialist detail as possible from the
user. This principle arose from considering the complex calculations
involved in handling celestial coordinates and time systems. However,
we soon realised that two other areas were similarly complex and could
benefit from the same approach.
The first area was graphics. Plotting in curvilinear coordinates is a
complicated business if one wants to handle all the corner cases
correctly. Plotting and labelling celestial coordinate axes, for
example, presents many problems; especially near the poles of an all-sky
projection. It is made even harder if the projection contains
discontinuities. But the high-level concepts involved in such plotting
(coordinate systems and the mappings between them) are such a natural
fit with other AST concepts that it seemed obvious to implement a class
of coordinate system that is specialised for graphics. The high-level
operations it supports would then hide the details of the complex and
generalised plotting algorithms involved.
The second area is an aspect of data storage -- namely the handling of
FITS header cards. While the AST library could provide ways to
serialise its own data transparently, possibly in multiple ways, it
also needed to inter-operate with FITS. WCS data in FITS data files is
stored in a series of 80-character header cards and, over the years,
the number of different ways the information can be stored in these
headers has grown. The complexity involved is now considerable
\citep[see \emph{e.g.},][]{2015Thomas}. Again, detailed specialist knowledge
is needed to extract this information reliably and to write it back
(possibly modified) in a form that gives other FITS-handling software
a chance of using it while not conflicting with the many other FITS
headers typically present.
For a user of the AST library, we wanted this process of accessing FITS
headers to appear as much like a simple read/write operation as
possible, with all the implementation details hidden. This requirement
arose from more than simply ease-of-use. FITS header conventions (many
of them informal) are in constant flux and if these details are embedded
in applications programs, those programs must constantly receive
attention if they are to remain up to date. Embedding all these details
in the AST library allows the problem to be addressed in one place and
by someone with the necessary expertise.
FITS header handling has proved one of the most complex areas to tame in
AST. But introducing the concept of a \emph{destructive read} (which
reads WCS data from FITS headers and simultaneously deletes the relevant
headers) has made it possible to write applications with very little code
that have completely general handling of FITS WCS headers (see
section~\ref{sec:fitsencodings}).
\section{The AST Data Model}
\label{sec:model}
The most basic principle behind the AST data model is a clear distinction
between a \emph{transformation} and a \emph{coordinate system}.
A \emph{transformation} is a mathematical recipe for converting a numerical
input vector into a corresponding numerical output vector. The
transformation itself has no knowledge of what the values within these
vectors represent, other than that each one constitutes a position within
some unspecified N-dimensional space\footnote{The dimensionality of the
output space need not be the same as that of the input space.}.
A \emph{coordinate system} is a collection of meta-data describing a set of
one or more axes. This will include:
\begin{itemize}
\item the number of axes (\emph{i.e.}, dimensionality of the space)
\item the physical quantity described by each axis
\item the units used by each axis
\item the geometry of the space that they describe (flat, spherical,
\emph{etc})
\item the nature of the coordinate system (Cartesian, polar, \emph{etc})
\item other meta-data that may be needed to specify the coordinate system
fully.
\end{itemize}
These two concepts are encapsulated within two separate classes in the
AST data model: the \emph{Mapping} class and the \emph{Frame} class. This
separation underlines the fundamental difference between the two main
requirements of any coordinate handling system:
\begin{enumerate}
\item knowing \emph{how} to convert numerical positions from one coordinate
system to another.
\item knowing \emph{what} those coordinate systems represent.
\end{enumerate}
As an example of the practical consequences of this distinction,
the \emph{pixel size} of a typical 2-dimensional image of the sky is
\emph{not} considered to be a property of the (RA,Dec) Frame, since it is
determined by the nature of the transformation from pixel coordinates to
(RA,Dec) coordinates, rather than being an intrinsic property of the (RA,Dec)
coordinate system itself.
The two classes, \emph{Mapping} and \emph{Frame}, are extended
to create a wide variety of sub-classes, each of which describes a
specific form of transformation or coordinate system. New sub-classes can
be added as required and slot naturally into the existing
infra-structure provided by the rest of AST.
In addition, this separation into two ``orthogonal'' classes makes it
easy to create complex compound objects from simple component objects.
For instance, multiple Mappings can be combined into a new object, and
the resulting object will itself be a Mapping. Likewise, multiple Frames
can be combined into a new object, and the resulting object will itself be
a Frame\footnote{For instance, a 2-dimensional (RA,Dec) Frame can be
combined with a 1-dimensional wavelength Frame to create a 3-dimensional
(RA,Dec,Wavelength) Frame.}.
However, at some point these two classes need to be brought together to
provide a complete description of a set of related coordinate systems.
The \emph{FrameSet} class is used for this purpose. A \emph{FrameSet}
encapsulates a collection of two or more Frames, with the Mappings that
describe the transformation between the corresponding coordinate systems.
The simplest FrameSet contains two Frames, together with a single Mapping that
describes how to convert positions between these two Frames (see
Fig.~\ref{fig:simple-frameset}).
\begin{figure}[h]
\centering
\includegraphics[width=0.5\columnwidth]{simple-frameset}
\caption{A FrameSet that describes two coordinate systems and the
transformations between them. The Mapping's \emph{forward} transformation
transforms positions in \emph{Frame 1} to the corresponding position in
\emph{Frame 2}. The Mapping's \emph{inverse} transformation
transforms positions in \emph{Frame 2} to the corresponding position in
\emph{Frame 1}. }
\label{fig:simple-frameset}
\end{figure}
More complex FrameSets can be created that describe the relationships between
multiple Frames in the form of a tree structure (see
Fig.~\ref{fig:complex-frameset}).
\begin{figure}[h]
\centering
\includegraphics[width=\columnwidth]{complex-frameset}
\caption{A FrameSet that describes five inter-related coordinate systems
and the transformations between them. }
\label{fig:complex-frameset}
\end{figure}
\subsection{Transformations and Mappings}
\label{sec:mappings}
Within AST, most \emph{Mappings} encapsulate two transformations - one is
designated as the \emph{forward} transformation and the other as the
\emph{inverse} transformation. When a Mapping is used to transform a set
of positions, the caller must indicate if the forward or inverse
transformation is to be used. The \emph{forward transformation} converts
positions within the input space of the Mapping into corresponding
positions within the output space, and the \emph{inverse transformation}
converts positions within the output space of the Mapping into corresponding
positions within the input space. A Mapping can be \emph{inverted}, which
results in the two transformations being swapped.
For most classes of Mapping, the inverse transformation is a genuine
mathematical inverse of the forward transformation. However, this is not
an absolute requirement, and there are a few classes of Mapping where
this is not the case (for instance the \emph{PermMap} class, when the
axes of the output space are a permuted subset of the axes of the input
space). In addition, the Mapping class does not require that \emph{both}
transformations are defined. For instance, the \emph{MatrixMap} class, which
multiplies each input vector by a specified matrix to create the output
vector, will only have an inverse transformation if the matrix is square
and invertable.
\subsubsection{Atomic Mappings Provided by AST}
AST provides many classes of Mapping that implement a wide range of
different transformations. Most of these are \emph{atomic} Mappings that
implement a specific numerical transformation and, if possible, its inverse.
But some are \emph{compound} Mappings that combine together other Mappings
(atomic or compound) in various ways to create a more complex Mapping.
A compound Mappings does not define its own transformations, but instead
inherits the transformations of the individual component Mappings which
it encapsulates.
The most significant atomic Mapping classes are:
\begin{description}
\item[UnitMap:] Copy positions from input to output without any change.
\item[WinMap:] Transform positions by scaling and shifting each axis.
\item[ZoomMap:] Transform positions by zooming all axes about the origin.
\item[ShiftMap:] Translate positions by adding an offset to each axis.
\item[MatrixMap:] Transform position vectors by multiplying by a matrix.
\item[PolyMap:] A general N-dimensional polynomial transformation.
\item[PermMap:] Transform positions by permuting and selecting axes.
\item[LutMap:] Transform 1-dimensional coordinates using a look-up table.
\item[MathMap:] Transform coordinates using general algebraic mathematical expressions.
\item[WcsMap:] Implements a wide range of spherical projections.
\item[SlaMap:] Transform positions between various celestial coordinate systems.
\item[SpecMap:] Transforms positions between various spectral coordinate systems.
\item[SphMap:] Map 3-d Cartesian to 2-d spherical coordinates.
\item[TimeMap:] Transform positions between various time coordinate systems.
\item[PcdMap:] Apply 2-dimensional pincushion/barrel distortion.
\item[DssMap:] Transform positions using Digitised Sky Survey plate solutions.
\item[GrismMap:] Models the spectral dispersion produced by a grism.
\item[IntraMap:] Transform positions using an externally supplied transformation function.
\item[SelectorMap:] Locates positions within a set of Regions (see
section~\ref{sec:cmpmap}).
\end{description}
All classes of Mapping are immutable. That is, a Mapping cannot be changed
once it has been created. This is unlike Frame and FrameSet objects, which
\emph{can} be changed.
\subsubsection{Compound Mappings}
\label{sec:cmpmap}
The two most significant compound Mapping classes are:
\begin{description}
\item[CmpMap:] The CmpMap class is a subclass of Mapping that encapsulates
two other Mappings either in series or in parallel. Either or both of the two
encapsulated Mappings can itself be a CmpMap, allowing arbitrarily complex
Mappings to be created.
In a \emph{series} CmpMap, each input position is transformed by the
first component Mapping, and the output from that Mapping is then
transformed by the second component Mapping. Consequently, the output
dimensionality of the first Mapping must be the same as the input
dimensionality of the second Mapping.
In a \emph{parallel} CmpMap, the input space is split into two sub-spaces.
The first component Mapping is used to transform the axis values
corresponding to the first subspace, and the second component Mapping is
used to transform the axis values corresponding to the second subspace.
Thus the input dimensionality of the CmpMap is equal to the sum of the
input dimensionalities of the two component Mappings, and the output
dimensionality of the CmpMap is equal to the sum of the output
dimensionalities of the two component Mappings.
\item[SwitchMap:] The SwitchMap class is a subclass of Mapping that allows
a different transformation to be used for different regions within the input
space.
Each SwitchMap encapsulates any number of other Mappings, known as ``route''
Mappings, and one ``selector'' Mapping. All of these Mappings must have the
same input dimensionality, and all the route Mappings must have the same
output dimensionality. The selector Mapping must have a one-dimensional
output space.
Each input position supplied to the SwitchMap is first transformed by the
selector Mapping. The scalar output from the selector Mapping is used to
index into the list of route mappings. The selected route mapping is then
used to transform the input position to generate the output position
returned by the SwitchMap.
There is a specialised subclass of Mapping, the SelectorMap class, that
is designed specifically to fulfil the role of the selector Mapping
within a SwitchMap, but in principle \emph{any} suitable form of Mapping
may be used. The SelectorMap class encapsulates several Regions (see
section~\ref{sec:region}) and returns an output value that indicates
which of the Regions (if any) contained the input value. Thus, the
SwitchMap would typically contain one route Mapping for each of the
Regions contained within the SelectorMap.
\end{description}
\subsubsection{Simplification}
\label{sec:simplification}
There are a wide range of possible transformations that could
potentially be applied to a data set during analysis. These
include simple things such as rotation, scaling, shear, \emph{etc}., but
could in principle include more complex transformations such as
re-projection, dis-continuous ``patchwork'' transformations, or even
transformation using a general algebraic expression. A coordinate
handling system should make it possible for a user to apply an
arbitrary set of such transformations in series to a data set, without
losing track of the coordinates of each data point. With a
prescriptive scheme such as FITS-WCS this would require each
transformation to locate the appropriate component of the FITS-WCS
pixel to world coordinate mapping, and modify the corresponding
headers in a suitable way. This is often a difficult, if not
impossible, task. Within AST, the chaining of transformations is
accomplished simply by creating a Mapping that describes each new
transformation and concatenating it with the existing pixel to world
coordinate mapping by creating a new CmpMap (see section~\ref{sec:cmpmap}).
However, by itself this can lead to Mappings that become increasingly
complex as transformations are stacked on top of each other. This is
a problem because it leads to:
\begin{enumerate}
\item slower evaluation of the total transformation,
\item less accurate evaluation of the total transformation, and
\item more room being needed for storage.
\end{enumerate}
To avoid this, the Mapping class provides a method that takes a
potentially complex Mapping and simplifies it as far as possible. Doing
such simplification in a general and effective manner is one of the most
difficult challenges faced by the AST model, but experience has shown
that the current scheme handles most cases
sufficiently well. The steps involved in simplification depend on the
nature of the component Mappings in the total CmpMap. Each class of
Mapping provides its own rules that indicate when and how it can be
simplified, or combined with an adjacent Mapping in the chain. To
illustrate the principle, some of the simplest examples include:
\begin{enumerate}
\item any Mapping can be combined with its own inverse to create a UnitMap,
\item UnitMaps can be removed entirely,
\item adjacent MatrixMaps in series can be combined using matrix
multiplication to create a single MatrixMap,
\item adjacent MatrixMaps in parallel can be combined to create a
single MatrixMap of higher dimensionality (filling the off-diagonal
quadrants with zeros),
\item adjacent ShiftMaps can be combined to form a single ShiftMap
(either in series or in parallel).
\end{enumerate}
The whole simplification process is managed by the CmpMap class - it
expands the compound Mapping into a list of atomic Mappings to be applied
in series or parallel, and then for each Mapping in the list, invokes
that Mapping's protected \texttt{astMapMerge} method. This method is
supplied with the entire list of atomic Mappings, and determines if the
nominated Mapping can be merged with any of its neighbours. If so, a new
list of Mappings is returned containing the merged Mapping in place of
the original mappings. Once all atomic Mappings in the CmpMap have been
checked in this way, the same process is repeated again from the
beginning in case any of the changes that have been made to the list
allow further simplifications to be performed. This process is repeated
until no further simplifications occur.
There are in general multiple ways in which a list of Mappings (either
series or parallel) can be simplified. Since each class of Mapping has
its own priorities about how to merge itself with its neighbours, it is
possible for the simplification process to enter an infinite loop in
which neighbouring Mappings disagree about the best way to simplify the
Mapping list. When a particular Mapping is asked to merge with its
neighbours, it may make changes to the Mapping list that are then undone
when a neighbouring Mapping is asked to merge with its neighbours. The
simplification method takes care to spot such loops and to assign priority
to one or the other of the conflicting Mappings.
\subsubsection{Missing or Bad Axis Values}
\label{sec:bad}
AST flags unknown or missing axis values using a special numerical value,
known as the ``bad'' value. If an input position supplied to a Mapping
contains one or more bad axis values, then in general all output axis
values will be bad\footnote{One exception is that a parallel CmpMap may
be able to generate non-bad values for some of its output axes.}.
Mappings may also generate bad output axis values if the input position
corresponds to a singularity in the transformation, or is outside the
region in which the transformation is defined.
\subsection{Frames and Domains}
As described in section~\ref{sec:model}, each instance of the Frame class
contains all the meta-data necessary to give a complete description of
a particular coordinate system. Each Frame is associated with a specific
\emph{domain} as explained in the next section.
Unlike Mappings, the properties of a Frame can be changed at any time.
\subsubsection{What is a Domain?}
\label{sec:domain}
AST uses the word \emph{domain} to refer to a physical (or abstract)
space such as ``time'', ``the sky'', ``the electro-magnetic spectrum'',
``the focal plane'', ``a pixel array''. Points within such a space can in
general be described using any one of several coordinate systems. For
instance, any position on the sky can be described using ICRS
coordinates, Galactic coordinates, \emph{etc}. Similarly, positions in
the electro-magnetic spectrum can be described using frequency,
wavelength, velocity, \emph{etc}.
Each subclass of Frame represents a specific domain with a domain name (a
string) which is the same for all instances of the class. However, each
instance of the class represents just one particular coordinate system
within that domain. In general, each Frame sub-class will also
encapsulate all the information needed to create a Mapping between any
pair of supported coordinate systems within its domain.
For example, the \emph{SkyFrame} class represents the astronomical sky (its
domain) and has the domain name ``SKY''. It knows about a range of celestial
coordinate systems that can be used to describe positions on the sky.
However, an instance of the SkyFrame class represents only one of these
celestial coordinate systems at a time. The particular one it represents is
stored in its ``System'' attribute (an instance variable) which takes
values such as ``ICRS'', ``FK5'', ``Galactic'', \emph{etc.}
The SkyFrame class extends the Frame class by incorporating various other
items of metadata necessary to perform conversion between the supported
celestial coordinate systems, the main items being the epoch of observation
and the reference equinox \footnote{The base Frame class includes the
observer's geodetic position.}. These are also instance variables and
therefore specific to each SkyFrame instance. The coordinate system
represented by an instance of the SkyFrame class can be changed at any time
simply by assigning new values to any of the relevant attributes, as in
the following Python example:
\begin{lstlisting}
import starlink.Ast as Ast
# Create a Frame describing ICRS Right
# Ascension and Declination. This is the
# default system for the SkyFrame class.
frame1 = Ast.SkyFrame()
# Create a deep copy, and change the System
# attribute of the copy so that it represents
# Galactic longitude and latitude.
frame2 = frame1.copy()
frame2.System = "Galactic"
\end{lstlisting}
\subsubsection{Coordinate Conversion within Domains}
\label{sec:domConversion}
Given the two SkyFrames from the example in the previous section, it is
possible to create a Mapping between them (\emph{i.e.}, from ICRS (RA,Dec)
to Galactic ($\ell,b$)) based on the meta-data stored within the two
SkyFrames. This Mapping can then be used to convert (RA,Dec) positions
into equivalent (l,b) positions, or vice-versa. The process of creating
this Mapping is implemented within the \texttt{astConvert} method of the
Frame class. For instance:
\begin{lstlisting}
my_frameset = frame1.convert(frame2)
\end{lstlisting}
will, if possible, generate a Mapping from \texttt{frame1} to
\texttt{frame2}. The returned Mapping is encapsulated within a FrameSet that
also includes copies of \texttt{frame1} and \texttt{frame2}.
Likewise, the SpecFrame class encapsulates all the information needed to
create Mappings between any pair of supported spectral coordinate system,
accounting for rest frequency, standard of rest, celestial reference position,
\emph{etc.}, as in the following example:
\begin{lstlisting}
# Create a Frame describing helio-centric
# Radio Velocity in units of km/s, with a rest
# frequency of 345.8 GHz.
frame1 = Ast.SpecFrame("System=VRAD,RestFreq=345.8")
# Create a deep copy, and change the attributes
# so that the copy describes frequency units of
# "Hz" with respect to the kinematic Local Standard
# of Rest.
frame2 = frame1.copy()
frame2.System = "FREQ"
frame2.Unit_1 = "Hz"
frame2.StdOfRest = "LSR"
# Create a FrameSet that contains the Mapping
# between these two spectral coordinate systems.
my_frameset = frame1.convert(frame2)
\end{lstlisting}
The principle that each class of Frame contains all the meta-data and
intelligence required to create a Mapping between any two coordinate
systems within the Frame's domain, extends to compound Frames as well as
atomic Frames, as described in section~\ref{sec:comConversion}.
The base Frame class itself is slightly unusual in that it can be used to
describe any generic domain, and is restricted to a single Cartesian
coordinate system within that domain. The domain associated with a basic
Frame is specified by the caller and can be any arbitrary string. Clearly,
this restricts the usefulness of a basic Frame (compared to more
specialised classes of Frame) in that it is not possible to include any
knowledge about multiple coordinate systems given the arbitrary nature of
the domain. The only exception is that the basic Frame class
knows how to convert between different dimensionally equivalent units.
Thus the implementation of the \texttt{astConvert} method provided by the
basic Frame class can generate a Mapping between two basic Frames if they
have the same domain name, the same number of axes, and the axes have
dimensionally equivalent units.
\subsubsection{Atomic Frame Classes Provided by AST}
AST provides several classes of Frame that describe different specialised
domains. As with Mappings, these can be divided into \emph{atomic} Frames that
describe a single specific domain, and \emph{compound} Frames that combine
together other Frames (atomic or compound) to create a Frame describing a