PIXELS, GRIDS & WORLD COORDINATES

This document is an analysis of GIPSY's (legacy) system of pixels,
grids and world coordinates. We describe a proposal to clean up the
existing code to make the system less sensitive to different definitions,
applied in the past.

A source of information are the minutes of the DEUK meetings,
The DEUK group consisted of a number of experienced
GIPSY users and programmers.

First some definitions:

Nearest integer
NINT() in (pre april 2009) GIPSY is either a function or a macro which rounds a
number to
the nearest integer. In tasks written in C usually defined as:
#define NINT(a)        ( (a) < 0 ? (int)((a)-.5) : (int)((a)+.5) )

This definition follows the definition of the Fortran NINT() function:

Returns a with the fractional portion of its magnitude eliminated by rounding to the
nearest whole number and with its sign preserved, converted to type Integer
A fractional portion exactly equal to `.5' is rounded to the whole number that
is larger in magnitude.

FITS pixel system
The FITS pixel system is a system where the first pixel is labeled 1 and
in a sequence of N pixels, the last pixel is labeled N. The system is an integer
system and the label corresponds with the center of a pixel.

|___|___|___|___|___|___|___|
1    2    3   4               N

CRPIX
CRPIX is an item from a FITS header and sets the location of the
reference point (CRVAL) of the axis. If CRPIX is an integer, the reference
is at the center of a pixel.

If CRPIX ends on .5, the reference is on the border

between two pixels.

GIPSY grid system
The GIPSY 'grid' system sets the pixel that corresponds to CRVAL to 0.
That pixel is set by the number of CRPIX. Values of CRPIX are not always
integer. Therefore we need to round its value. In GIPSY the NINT macro
is used for this rounding. The lower limit in grids is LO = 1 - NINT(CRPIX).

e.g. CRPIX = 3.3 then LO = 1 - NINT(3.3) = 1 - 3 = -2

|___|___|___|___|___|___|___|
-2   -1   0    1              N-2

The motivation for this choice was that it is convenient to have the
projection centre in/near (0,0) in transformations between image- and UV plane.

From DEUK meeting: deuk44.19jan90.txt:

PC
"Two symbols are proposed which denote one position.  They are: PC (which
stands for projection centre and is effectively (0.0,...)) and AC (which
stands for axis centre and is effectively ((LHI+LLO-1)/2,..))."

AC
"It must of course also be possible to give a central position and a
size.  The size is indicated by D (DELTA) followed by grids or physical
units.  For example to select an area in an RA-FREQ map one could type:
AREA= AC D 0.5 DEGREE 500 KM/S"

The definition shows that at that time there was no special treatment
for CRPIX values that were not integer.

Detected inconsistency in the pre 01-04-2009 version of GIPSY
The definition of 'PC' and the implementation in the routines dealing with coordinate
transformations are different. One needs to include an offset caused by non-integer
values of CRPIX in the header of a set or FITS file.
In function cotrans.c the correction that has been  applied for the value of 'PC'
is given in the code as:

set_info.ax[n].grid = set_info.ax[n].offset;

This offset is calculated with a call to a special function: offset(CRPIX)
which, in a previous version of  cotrans.c is defined as:

static double offset( double x )
{
double r = x - floor( x );

if (r > 0.5) r -= 1.0;
return( r );
}

i.e.:

offset(-0.6) = -0.6 - floor(-0.6) = -0.6 - (-1) = 0.4 ==> r = 0.4
offset(-0.5) = -0.5 - floor(-0.5) = -0.5 - (-1) = 0.5 ==> r = 0.5
offset(-0.3) = -0.3 - floor(-0.3) = -0.3 - (-1) = 0.7 ==> r = 0.7 - 1 = -0.3
offset(0.0)  = -0.0 - floor(0.0)  = -0.0 - (-0.0) = 0.0 ==> r = 0.0
offset(+0.3) = +0.3 - floor(0.3)  =  0.3 - (0) = 0.3 ==> r = 0.3
offset(+0.5) = +0.5 - floor(0.5)  =  0.5 - (0) = 0.5 ==> r = 0.5
offset(+0.6) = +0.6 - floor(0.6)  =  0.6 - (0) = 0.6 ==> r = 0.6 - 1 = -0.4

If the fractional part of CRPIX > 0.5 then the integer part should be  increased with 1.
So CRPIX = 0.6 then the integer part is 1 and the offset is -0.4
and CRPIX = -0.6 then the integer part is -1 and the offset is 0.4
The integer values can be obtained by applying:

CRPIX_int = CRPIX - offset

This gives interesting values for:
CRPIX=-0.5: CRPIX_int = -0.5 - 0.5 = -1
and:
CRPIX=0.5: CRPIX_int = 0.5 - 0.5 = 0

The integer values of CRPIX are used to set the transformation between
the FITS pixel system and the GIPSY grid system.

Later, we found that this function behaved differently compared to the routines
which obtain boxes (axes limits in GIPSY grids) with macro NINT().

static double offset( double x )
{
return( x - NINT(x) );
}

In cotrans.c the NINT is defined as:

#define NINT(a)        ( (a) < 0 ? (int)((a)-.5) : (int)((a)+.5) )

offset(-0.6) = -0.6 - NINT(-0.6) = -0.6 - (int) (-0.6-0.5) = -0.6 +1 = 0.4
offset(-0.5) = -0.5 - NINT(-0.5) = -0.5 - (int) (-0.5-0.5) = -0.5 +1 = 0.5
offset(-0.3) = -0.3 - NINT(-0.3) = -0.3 - (int) (-0.3-0.5) = -0.3 -0 = -0.3
offset( 0.0) =  0.0 - NINT( 0.0) =  0.0 - (int) ( 0.0+0.5) =  0.0 -0 =  0.0
offset( 0.3) =  0.3 - NINT( 0.3) =  0.3 - (int) ( 0.3+0.5) =  0.3 -0 =  0.3
offset( 0.5) =  0.5 - NINT( 0.5) =  0.5 - (int) ( 0.5+0.5) =  0.5 -1 =  -0.5
offset( 0.6) =  0.6 - NINT( 0.6) =  0.6 - (int) ( 0.6+0.5) =  0.6 -1 =  -0.4

Compared to the previous list one notices that the offset functions differ for
CRPIX=0.5.
With the same formula to obtain the integer part:
CRPIX_int = CRPIX - offset
one gets for:
CRPIX = -0.5: CRPIX_int = -0.5 + 0.5 = 0
CRPIX =  0.5: CRPIX_int = 0.5 - (-0.5) = 1

The CRPIX_int value is a measure for the translation from the GIPSY grid
system to the 1-based pixel system.
The conclusion then is that for (at least) CRPIX=0.5 one gets different values
for this shift between the grid- and pixel systems. Other functions that deal with
this conversion between systems (gds___unpack.c, gdsinp.c, gdsbox.c) use the
NINT macro. To prevent the mismatch we just described, it was decided to
replace the floor() based offset function by the NINT() based offset function.

We can make this more obvious in a plot where we compare the NINT macro
with C's floor() function in the offset function. It is obvious that the results differ for all
CRPIX > 0 for which the fractional part is 0.5.

The problems arise when the fractional part of CRPIX is exactly 0.5 which results
in a one pixel shift of the grid coordinates.  In practice there were numerous examples
of sets with CRPIX header values that fall into this category.
In some cases the choice seems to have a valid reason and in others it is probably a
mistake. The change in the offset function made the system more consistent but
less correct.

The behaviour of the NINT macro is point symmetric around zero. This causes
zero to be special point. There is no good reason why zero should be special.
The pixel system in FITS is starts to label pixels with '1' and the GIPSY coordinate
word system does the same. The motivation for the cotrans.c definition of
nearest integer is not documented, but private communications with
the original author of this function confirms that in fact negative values for
CRPIX were not taken in account, which seems valid for at least the majority of
FITS files, but invalid for sets processed by e.g. GIPSY tasks (e.g. all applications
that change the size of the output set with function gdscss.c.

In the next plot we show this and compare this
behaviour with the floor() function. The routines are evaluated for  x in an
interval [-4.5, 4.5> in steps of 0.25.
In the plot it is shown that the floor and NINT macro differ for CRPIX < 0
when its fractional part is 0.5.

gds___unpack.c
Function gdsc_grids() calculates the grid values for coordinate word values of the limits
and uses function gds___unpack.c for this. The coordinate words in this routine
transform a coordinate word into a 1-based pixel system for each axis in a data set.
This rounding function in the unpack routine has a a different but
comparable definition of NINT.
#define NINT(a) ( (a)<0 ? (fint)((a)-.5) : (fint)((a)+.5) )

Just like the 1-based FITS pixel system, the coordinate word pixels start
to count with '1'. To return a GIPSY grid value it has to include the
value of CRPIX.
For a pixel position p it calculates the grid g with: g = p - NINT(CRPIX).

To calculate a lower axis limit 'blo' that corresponds with a FITS pixel position
equal to 1, we get for different values of CRPIX:

crpix = -2.5 ==> blo = NINT(3.5)  = int (3.5+0.5) = 4
crpix = -1.5 ==> blo = NINT(2.5)  = int (2.5+0.5) = 3
crpix = -0.5 ==> blo = NINT(1.5)  = int (1.5+0.5) = 2
crpix =  0.5 ==> blo = NINT(0.5)  = int (0.5+0.5) = 1
crpix =  1.5 ==> blo = NINT(-0.5) = int (-0.5-0.5) = -1
crpix =  2.5 ==> blo = NINT(-1.5) = int (-1.5-0.5) = -2

And here it becomes obvious that for CRPIX with a fractional part of 0.5
there is a jump from 1 to -1. For this fraction one cannot find a CRPIX
where the corresponding 'blo' is 0.

Conclusion: A grid coordinate system which converts from a 1-based pixel system
using the NINT macro as defined above, either excludes the use of CRPIX values
that have fractional parts equal to 0.5 or it excludes the existence of grid 0
for this category of CRPIX values.

Example: Application SLICE can extract a slice of data in a data cube
(e.g. RA-DEC-FREQ).
Then a set of sample points is defined in the spatial part of the data set.
One of the axes in the output set has a range [blo,bhi] = [0,0].
When the original CRPIX ends on .5 then after the creation of this output set,
there are no GIPSY applications that display the axis range correct
See also section: gdscss.c

With the NINT definition from above, is there a difference between:
g1 = p - NINT(CRPIX)  and g2 = NINT(p-CRPIX) ?

Assume CRPIX=-2.5 and p = 1
g1 = 1 - NINT(-2.5) = 1 - (int) (-2.5-0.5) = 1 - (-3) = 4
g2 = NINT(1+2.5) = NINT(3.5) = (int) (3.5+0.5) = 4

This could be expected because of the symmetry of NINT in 0.
Only the 'jump' i

Later we will discuss the use of the floor function to get a nearest integer.
Then these two definitions to calculate g1 and g2 are not equal because
the floor function has no point symmetry in 0.

gdsbox.c
In gdsbox.c the NINT is used in the following definition:
#define  NINT(x)     ( x > 0.0 ? (fint) ( x + 0.5 ) : (fint) ( x - 0.5 ) )

which differs from the previous definitions but -again- gives the same  results.

In gdsbox.c positions are calculated with the NINT macro.
For a central position and a size, it calculates the limits as:

fint c = NINT( pos[k] );                /* central position */
fint s = bhi[k];                             /* size of box */
pos[k+subdim] = (double) ( c + s / 2 );      /* upper right */
pos[k] = pos[k+subdim] + (double) ( 1 - s );  /* lower left */

and somewhere else in the code:

fint c = NINT( pos[k] );          /* centre of box */
fint l;                              /* lower grid */
fint s = NINT( pos[k+subdim] );     /* size of box */
fint u;                              /* upper grid */

u = NINT( (double) ( c + s / 2 ) );         /* u-r */
l = NINT( (double) ( u + 1 - s ) );         /* l-l */

gdsinp.c
In gdsinp.c the upper and lower limits of set axes are set by the relations:

r.ax[n].low = 1 - NINT( crpix );
r.ax[n].upp = naxis - NINT( crpix );

which is based on the formula: g = p - NINT(CRPIX)
and the fact that the first pixel in FITS is '1'.

Further there is in gdsout (also in gdsinp.c) a routine to obtain a value
for CRPIX if we have to create a new set and have a (non integer) pixel value
for the lower axis limit. Then an iterative procedure tries to solve :

/* We want: NINT(set_info.ax[m].crpix) == ( 1 - low). */

That is, one requires: low = NINT(1-CRPIX) = 1 - NINT(CRPIX) and, given
a value for 'low', a new value of CRPIX is calculated with: NINT(CRPIX) = 1 - low
In the proposed iteration, the start value of CRPIX is copied from the original (e.g. from
the input set), a shift in integer pixels is applied and then an iteration is started
by increasing or decreasing the value by a small number.
So for those cases where low == 0 (where we proved that we could not find a CRPIX
that ended on .5) this routine could find a CRPIX very close (DBL_EPSILON) to the
original value.
If the NINT() macro is replaced by a definition with C's floor() function, then probably
this iteration needs not to be applied anymore. To be save we should not clean up the
iteration code, because it is difficult to predict whether the iteration is necessary in
other circumstances.

gdscss.c
Another function in gdsinp is gdscss.c It changes/sets the axes limits in an output
set.

out_buf[k].ax[n].naxis = bhi[n] - blo[n] + 1;
out_buf[k].ax[n].crpix += (double) ( out_buf[k].ax[n].low - blo[n] );
out_buf[k].ax[n].low = blo[n];
out_buf[k].ax[n].upp = bhi[n];

With these relations we can show that there is an inconsistency in the transformations
with the NINT macro. Here is a recipe:

1) The NINT is defined as: x = NINT(a)  ==>   a > 0  x = int (a + 0.5) else x = int (a - 0.5)
2) The lower axis limit is defined in blo = 1.0 - NINT(crpix)
3) Assume CRPIX = -3.5  then blo = 1 - NINT(-3.5) = 1 - int (-3.5-0.5) = 1 - (-4) = 5
4) In gdscss a new CRPIX is calculated using the old value and then a value
is added which depends on the new blo, the lower axis limit:
out_buf[k].ax[n].crpix += (double) ( out_buf[k].ax[n].low - blo[n] );
or: crpix_new = crpix_old + (blo_old - blo_new)
5) Assume we want blo_new = 0 as output and use this value in gdscss. The new CRPIX
becomes:
crpix_new = -3.5 + (5 - 0) = 1.5
6) Is this the CRPIX that gives the right blo according to another formula used
in gdsinp:
blo_check = 1 - NINT(crpix) = 1 - NINT(1.5) = 1 - int(1.5+0.5) = 1 - 2 = -1
7) Conclusion : blo_check != blo_new

This problem can be avoided by using C's function floor(x+0.5).
Let's follow the same recipe:

1) Replace NINT(x) by floor(x+0.5)
2) The lower axis limit is defined in blo = 1.0 - NINT(crpix)
3) Assume CRPIX = -3.5  then blo = 1 - floor(-3.5+0.5) = 1 - (-3) = 4
4) In gdscss a new CRPIX is calculated using the old value and then a value
is added which depends on the new blo, the lower axis limit:
out_buf[k].ax[n].crpix += (double) ( out_buf[k].ax[n].low - blo[n] );
or: crpix_new = crpix_old + (blo_old - blo_new)
5) Assume we want blo_new = 0 as output and use this value in gdscss. The new CRPIX
becomes:
crpix_new = -3.5 + (4 - 0) = 0.5
6) Is this the CRPIX that gives the right blo according to another formula used
in gdsinp:
blo_check = 1 - floor(crpix+0.5) = 1 - floor(0.5+0.5) = 1 - 1 = 0
7) Conclusion : blo_check == blo_new

What we change after the introduction of function floor() is the behaviour of
the transformation from pixels to grids at the border of the pixels.
With floor() the left border of each pixel (in the 1-dim. case) belongs to the pixel
itself. The right border belongs to the next (right) pixel. With the NINT macro
this border behaviour is symmetric around 0 which gives the unwanted behaviour
for blo_new = 0.

Function floor() and the transformation between grids and pixels
For FITS pixels or coordinate word pixels that start with '1' we can calculate
the value of the lower limit of an axis in two ways:
g1 = p - floor(CRPIX+0.5)  and g2 = NINT(p-CRPIX+0.5) ?

Assume CRPIX=-2.5 and p = 1
g1 = 1 - floor(-2.5+0.5) = 1 - (-2) = 3
g2 = floor(1+2.5+0.5) = floor(4) = 4

These conflicts do not arise for CRPIX values that do not have a fractional part
equal to 0.5. To get a consistent system we have to decide which formula is
correct.  In the next figure we plotted values for g1 and g2 for a
range of x values in steps of 0.25.

The formula should be consistent with the requirement that we want to include the
left border in the pixel. Then for CRPIX = -2.5 we want the same grid as for
CRPIX=-2.4 (i.e. 3). This can only be obtained by the first definition.

Conclusion: When floor(x+0.5) gets the role of the macro NINT then it is important
for consistency how the transformation from pixel to grid is defined.
The proposed formula is:

grid = pixel - floor(CRPIX+0.5)

Definition of 'AC'
For 'AC' the centre of a data set:
set_info.ax[n].grid = 0.5 * ( set_info.ax[n].naxis + 1.0 )
- set_info.ax[n].crpix + set_info.ax[n].offset;
This also differs from the original definition from the same deuk document.

MathWorld definition of nint()
The nearest integer function nint(x) of , illustrated above and also called nint
or the round function, is defined such that is the integer closest to .
Since this definition is ambiguous for half-integers, the additional rule that
half-integers are always rounded to even numbers is usually added in order to
avoid statistical biasing. For example, , , , , etc.
This convention is followed in the C math.h library function rint

We examined the all the routines dealing with coordinate transformations
and defined 4 categories of code:

1. NINTs in functions
*gdsbox.c:        All NINTs are used for positions
*cotrans.c:       NINT only used in an offset function
*gdsinp.c:         NINT only used for coordinate transformations
*gds_unpack.c  NINT only used for coordinate transformations
*gdspos.c          NINT defined but not used

A related function is gds_extend. This routine defines the origin of an axis
and uses CRPIX for this. The routine is called in gdsinp.c and several
tasks that build a new set. All these calls use CRPIX to define the origin
of an axis. There is no need to change this function because there are no
NINT's involved.

Less important functions:
*fie.c                   Provides a NINT for expression evaluation. According to the
source code it behaves like the classic NINT macro, but it is
defined as a function.
*dms.c                  Fred Lahuis version of nint. No coordinates involved. Do not change.
*drawaxis.c         Deals with coordinates. Tasks gplot.src and sliceview.src depend
on this function.
*ellipsefill.c         NINT for positions. Do change.
*fillgauss2d.c      Two NINTs for coordinates. Do change
*gauestd.c           Uses NINT in coordinate like calculations. Do change.
*herinp.c             dcd_nint defined. The routine herinp.c seems not to be used
in other sources.
*hms.c                  Fred Lahuis version of nint. No coordinates involved. Do not change.
*interpol.c            NINTs used to find image value in interpolation routine.
No need to change because the interpolation is also correct for
values on the border of pixels. Do not change.
*scanline.c           NINTs used for coordinates. Do change.

2. nint() in functions
*pgdriv.src        nint() not for coordinates. nint() here defined as floor()
*gdsd_wfits.shl  nint() only used to distinguish floats and doubles
from integer. Therefore harmless and there is no need
to change the code.
*irpl_snip.shl     nint() only used to convert a sqrt to integer.
Do not change
*dydx.src           Harmless. Only used to set derivative to 0. Do not
change.

Proposal:
For the functions in 1. one can change the current macro
#define  NINT(x)     ( x > 0.0 ? (fint) ( x + 0.5 ) : (fint) ( x - 0.5 ) )

into:

#define NINT(a) ( (int) floor( (double) (a) + 0.5 ) )

This will not interfere with other use of the NINT macro.
For category 2. there is nothing to do.

3. NINTs in C tasks
*antpat.src       Unused NINT()
*funplot.src      NINT used only in a list of supported functions.
Depends on function fie.c
*gdsserver.src  NINT defined but not used.
*gids.src           It defines (yet) another version for the nearest integer.
int     misc_nint( double d )

{
int  r;

if (d > 0) r = d + 0.5; else { r = -d + 0.5; r = -r; }
return( r );
}
It is not used for grid coordinates so we leave it unaltered.
*gaufit2d.src    NINT() defined but not used
*cola.c              NINTs for coordinate transformations only
-hermes.src      NINTs for coordinate transformation and 1 other situation
*gplot.src          Some NINTS for positions
*pyblot.src        A Python program using the Fortran definition of nint().
*qplot.c             NINT 1 time for a coordinate
*regrid.c           NINT two times for a coordinate involving cdelt not crpix
*reproj.c           NINTs used for coordinates
*rotmas.src       NINT defined but not used
*sliceview.src    NINTs used for coordinates
*trace.c             NINTs for positions
*wfits.c              NINTs for scaling numbers in the data. Not coordinates.
We leave this application unaltered.
*xgaufit.src       NINT defined but not used
*xgauprof.src    NINT defined but not used

4. nint() in tasks
*ellfit.c               nint() for positions. nint() as a real function
*gmarker           nint() for positions. Used as macro equivalent to NINT
#define nint(f)         (f>0.0?(fint)(f+0.5):-1*((fint)(0.5-f)))

-hermes.src       nint_i() use unknown (with 'dangerous' in its comment).
No need to change (J.P.T)
*reswri.c           nint() used for coordinates. Used as macro NINT
*rfits.c               Use of function nint(double). All related to coordinates.
*tracks.c            IRAS. nint() used for coordinates. nint(a) == ( (int)( (a) + 0.5 ) ) which
behaves only as floor() for x > 0.0. The macro is used for
world coordinates only. Do not change.

5. NINT() in Fortran tasks
*combin.shl       Only nint in documentation. It depends on fie.c
*create.shl         Only nint in documentation. It depends on fie.c
*galmod.f           Uses Fortran IDNINT(real*2) for setting the integer
value of crpix3 and therefore should only be important
for velocity axes with negative half integer crpix.
More research needed. Low priority.
*ipaccal.src        IRAS. Fortran nint() not used for coordinates.
*points.src          IRAS. nint defined but not used
*plate.src           IRAS. nint not used for coordinates. There is one with
nint(crval) but this are world coordinates.
*render.src        NINTs used for internal positions. Probably not dangerous
*zodycal.src       Is a Fortran program. nint() used for world coordinates
not grids. Do not change.

Proposal:
The NINT's from cola.c are used in a way similar to gdsbox() and must be updated with
the same priority as gdsbox.c. For other tasks it is important to update when
transformations are involved (reproj.c, sliceview.src). For those where NINT
is used to calculate positions the priority is lower, but they should also be
updated for consistency.

In rfits.c the pixel to grid transformation is needed to be able to prompt the
user with a default for the axis limits. Program gmarker.c , reswri.c and ellfit.c
should be updated for consistency. These last three applications do not propagate
coordinates so the priority to update them is not high.

From grids to world coordinates
Another problem is related to the use of proco.c in some tasks (e.g. reproj.c).
In reproj.c there is a conversion between grids and world coordinates using the function
proco.c. This routine does not take non integer crpix into account and therefore
does not convert properly.
This becomes obvious in cotrans.c. Here an offset function is defined which corrects
GIPSY grids for the fractional parts of CRPIX.

#define GETGRID( n )            \
set_info.ax[n].grid - set_info.ax[n].offset
#define SETGRID( n )            \
set_info.ax[n].grid += set_info.ax[n].offset

The offset function has been discussed in a previous section. If one wants to convert
grid position 0,0 to world coordinates and the fractional part of CRPIX is not 0.0 then
0,0 does not present the projection center. This is important to know: 0,0 only indicates
in which pixel the projection center is, but not exactly where in that pixel. Remember that the
transformation between (FITS) pixels and GIPSY grids is an integer translation.
Only when transformations from and to world coordinates are involved we have to be
careful (e.g. when using proco() instead of cotrans()).

Assume we have CRPIX = 3.4, then the offset = 3.4 - floor(3.4+0.5) = 0.4
The center of grid 0 is at distance -0.4 from the projection center. This is the value in grids we
have to supply for the projection transformation function proco(). The function itself
has no information about CRPIX or offsets. Therefore functions and applications
that use proco() for their transformations to or from world coordinates, need to apply
a correction like cotrans().

So the offset is defined as:

offset = CRPIX - floor(CRPIX+0.5)

and a grid for proco() should be corrected with an offset:

grid_proco = grid - offset

which implies that after a transformation from a world coordinate to a grid,
one should add the offset:

grid = grid_proco + offset

A projection center in cotrans() entered as 'PC' is first calculated with
grid = 0.0 + offset and then: grid_proco = grid - offset = 0.0+offset-offset = 0.0

Applications cplot.c, dssload.c, reproj.c, slice.c all use proco() without this correction.
This can be fixed independently from the change of macro NINT.
Functions drawaxis.c and skypro.c use proco() but the first corrects already for offsets
and the latter is used in the context of cotrans() only and cotrans() applies the offset
correction already. In drawaxis() the NINT macro is used to calculate the offset.
This macro should be replaced by the one containing the floor() function.

Another important application is copy.shl. It is written in Sheltran and it also deals with
coordinates (gdscss()). The code itself does not contain any references to macro NINT so
after the update of gdsinp.c the program will work correctly.

What has been done?
09-04-2009: Application slice.c has been updated. The output grids in proco() needed to
be corrected the fractional part of CRPIX. The input grids were left unmodified.
They represent correct positions w.r.t. the selected rotation center.

11-04-2009: Application reproj.c is adapted for tne new macro for NINT. More important,
the coordinate transformation in function transform() is corrected for non
integer values of CRPIX. Before this change, all rotations around the projection
center were effectifely rotations around (0,0).

11-04-2009: gdsbox.c, cotrans.c, gdsinp.c, gds_unpack.c, gdspos.c
Changed definition of NINT, and tested all changes (with half integer
values of CRPIX). The reported exception in gdscss() (part of gdsinp.c)
doed not occur anymore. The iteration with DBL_EPSILON in gdsinp.c
triggered on positive half integer values of CRPIX. After the update to
the new definition of NINT() we did not find any iterations anymore
(for a suite of CRPIX values). The updated code seems more consistent.

12-04-2009: cola.c, rfits.c, only the definition of NINT was changed.
Program header.c had a hidden problem. It calculates the grid that corresponds
to the projection center but it did not calculate the offsets correctly
((int) instead of NINT). This has been restored with the new macro of NINT.
Header now displays the same offsets as program coords (with POS=PC).
Program sliceview.src uses macro NINT for positions. For consistency in
the definitions of coordinates the macro has been changed to the new definition.
The program uses routine grtoph() and phtogr() for the conversion between
grids and world coordinates. It does this in the context of cotrans.c which keeps
track of non-integer values of CRPIX and calculates the right offsets for
the position of the projection center. Plot program cplot.c has the same
problem as the applications reproj.c and slice.c i.e. there is no correction for
non integer values for CRPIX before and after a call to proco(). This problem
has also been corrected in this April 12 release.

14-04-2009: Changed NINT definitions in gplot.srcqplot.c, regrid.c, trace.c.
Cleaned up the code in render.src, but the use of NINT() in the Fortran
code was not changed. Note that the Fortran NINT() behaves exactly the
same as the C NINT() macro. However, the core of render (which is Fortran)
does not use NINT() for coordinate conversions and therefore we left the
core unaltered.
Program antpat.src was cleaned up a bit. Macro NINT() was not used and therefore
deleted.
Function fie.c defined a nearest integer function called fie_nint() which behaved like
the old version of NINT(). It was changed to the floor() version. After that,
program funplot.src could be tested. This program is based on the expression
evaluation of fie.c.
In application gaufit2d.src the NINT() macro was defined but not used, so we
removed it.
Function drawaxis.c plots labeled frames around images and has a close relation
to coordinates involving CRPIX. The NINT() macro was used to calculate offsets
similar to cotrans.c. With this latest update, it is compatible with cotrans.c.
A bug was removed also. The grid offsets were initialized to 0 in a loop.
Then always one of the offsets was wrong. Now also sliceview.src is consistent.
The mouse positions are calculated with grtoph() and the axis labels with proco()
and they are equal now for all values of CRPIX.

15-04-2009: Changed nint() definition in reswri.c. This macro is only used to
get values of a box and therefore could be inconsistent with functions that
deal with coordinates. Two includes were missing and an 'or' statement was wrong.
Both problems were fixed.
In pyblot.src (Python source) we changed the definition of nint() because it is used
in the context of coordinates.
Macro NINT was removed from xgaufit.src, xgauprof.src and rotmas.src.
These last three application were also cleaned upo a bit (missing includes) and
from rotmas.src a bug with an uninitialized string was removed.
Changed NINTs in herinp.c, fillgauss2d.c, ellipsefit.c, gauest.c, ellipsefill.c,
interpol.c, scanline.c
The application ellfit.c uses a NINT for coordinates and therefore we changed its
definition. Regretably this application only works with a worning version of GIDS.
The same remarks apply to gmarker.c.