inspector.src

        Copyright (c) Kapteyn Laboratorium Groningen 2000
        All Rights Reserved.

Name:         inspector.src
Creator:      vogelaar
Host:         hipparchus
Date:         Mar 28, 2000
Contents:     inspector.make inspector.dc1 inspector.tex inspector.h gui.h
              colormenu.h rotpar.h input.h labels.h helptext.h
              coledit.h markers.h blinker.h inspector.c gui.c
              utils.c colormenu.c rotpar.c input.c labels.c
              plot.c coledit.c markers.c blinker.c

#>            inspector.make
#----------------------------------------------------------------------
# Makefile for INSPECTOR, Aug 25, 1999
#
#
# Pack sources into inspector.src with:  > make -f inspector.make pack
# Clean executable and objects           > make -f inspector.make clean
# Unpack source files with:              > $gip_exe/xfile inspector.src
# Install source file with:              > p -reserve inspector.src
#                                        > p -install inspector.src
#
# Local compilation for testing:         > p inspector.make
#
# The CC_OPTS etc. are set by 'compile'
#----------------------------------------------------------------------

SHELL = /bin/sh

OPTS  = $(CC_OPTS)
COMP  = $(CC_COMP)
LIBS  = $(CC_LIBS)

.SUFFIXES: .c .o

DOCUMENTS = inspector.dc1 inspector.tex

INCLUDES  = inspector.h gui.h colormenu.h rotpar.h input.h labels.h helptext.h coledit.h markers.h blinker.h 

SOURCES   = inspector.c gui.c utils.c colormenu.c rotpar.c input.c labels.c plot.c coledit.c  markers.c blinker.c

OBJECTS   = inspector.o gui.o utils.o colormenu.o rotpar.o input.o labels.o plot.o coledit.o markers.o blinker.o


default:: inspector

pack::
	$${gip_sys}/pack.csh inspector.src inspector.make \
        $(DOCUMENTS) $(INCLUDES) $(SOURCES)

clean::
	rm -f inspector $(OBJECTS)

inspector.o: inspector.h

dofit.o:    inspector.h

allocs.o:   inspector.h

gui.o: gui.h


.c.o:  
	@echo "$(CC_COMP) -c $(CC_OPTS) $(X11_OPTS) $< ";\
	$(CC_COMP) -c $(CC_OPTS) $(X11_OPTS) $<

inspector: $(OBJECTS)
	@echo "$(CC_COMP) -o inspector $(OBJECTS) $(X11_LIBS) $(CC_LIBS) $(XT_LIBS)";\
	$(CC_COMP) -o inspector $(OBJECTS) $(X11_LIBS) $(CC_LIBS) $(XT_LIBS)

#<

#>            inspector.dc1
Program:       INSPECTOR

Purpose:       INSPECTOR writes slices through a 3d GIPSY data cube.
               It overlays radial HI velocities (i.e. 'tilted ring' 
               circular velocities) on velocities in XV diagrams and
               lets you adjust the tilted ring parameters to get a 
               better fit.

Category:      ANALYSIS, PLOTTING, ROTATION-CURVES, VELOCITY-FIELDS

File:          inspector.src

Author:        M.G.R. Vogelaar

Notes:         1) Use keyword XYSIZE= to set the INSPECTOR window size
                  in pixels, e.g.: INSPECTOR XYSIZE=800 600 
               2) Selecting a menu item only works if the keyboard CAPS-LOCK 
                  is OFF!
               3) If your sample size is too big, you won't see any slice data,
                  but you still can have overlays of tilted ring velocities.
                  This can be confusing.
 
Mouse buttons: LEFT :      For dragging the closest tilted ring marker. The 
                           focus remains on that marker until the button is
                           released.
               MIDDLE:     Let Tilted Ring (TR) parameters markers follow the 
                           mouse in vertical direction. The closest marker 
                           is moved.
               SHIFT LEFT: In current panel, draw segment line 1
                           then 2, remove 1, then 2 etc.
               CTRL RIGHT: Move all markers within segment. Action depends 
                           on the current segment mode.
               CTRL LEFT:  Select a new segment mode.
                            
               
Recipe:        You will need a GDS data cube with dimension >= 3
               The program works with physical coordinates only if
               the subset axes are spatial and the profile axis is a
               velocity axis (FREQ or VELO). Otherwise calculations
               and plotting is done in pixel coordinates.

               With such a cube (in fact a set of channel maps) you are able 
               to display slices through the data in so called 
               XV diagrams. Data along a slice is extracted and this process
               is repeated for all subsets to get an XV image.
               
               If you have data that describes a tilted ring model for
               a galaxy (velocity, inclination & position angle as 
               function of radius stored in a GDS table or an ASCII file on 
               disk) then this program calculates the line-of-sight velocities
               and marks those velocities in your XV maps. It displays
               the tilted ring parameters in three plots to the right
               of the mosaic with XV maps. The tilted ring parameters
               can be changed using the mouse. 

               A full description of how to derive rotation curves with
               INSPECTOR and its advantages and disadvantages over
               existing methods is given in 'Dark Matter in 
               Late-type Dwarf galaxies', Rob Swaters, PhD thesis
               Rijkuniversiteit Groningen, Oct 1999.


Example:       This is a list with a minimum number of keywords extracted 
               from the program after a test run. The data file 'voginp.dat'
               contains test data. Its contents is:

               0   0   60  50
               10  10  60  50
               20  20  60  50
               30  30  60  50 etc.
              
             
               These are the keywords: 
               
               BOX= -10 -10 10 10
               CLIP= 0 10
               GALCENTRE= 0 0
               INCLINATION= file(voginp.dat,3,1:)
               INSET=vogtest f
               POSANG= file(voginp.dat,4,1:)
               RADII= file(voginp.dat,1,1:)
               SAMPLE= 5
               SKYANGLES= 0 50 90 230 310
               SLICEX0Y0= 0 0
               VELOCITY= file(voginp.dat,2,1:)
               VSYS= 603.5


Defaults:      Defaults in the input window:

               (GALCENTRE=) Default galaxy center is 0 0 if that is within 
               the box, else it is the grid center of the map. 

               (BOX=) The default box appears after the set input. If you press
               enter in an empty box field, a default box, (entire subset 
               area) will appear. 
               
               (CLIP=) A default clip appears after the set input. The default
               is read from the header (Header items datamin and datamax).
               The default re-appears if you press enter in an empty field.
               
               (XUNITS=) The default units for the X axis of an XV diagram
               are arcsec if the subset axes are both spatial and the
               header units can be converted to degrees.
               
               (VUNITS=) The default units are km/s.

               (SLICEX0Y0=) Default appears after defining the slice angles. 
               The default of the slices centre is 0,0 i.e. if 0,0 
               is within the given box. Else the grid centre of the box is the 
               default. 
               
               (SAMPLE=) Default appears after defining the slice angles. 
               The default sampling is 1 in current XUNITS= units. 
               
               (OVERSUB=) Default subset is the middle subset. The default
               appears in the 'Overlay on subset' input window after the input
               set in entered.



               Defaults in the Tilted Ring window:
               
               (TRXUNITS=) The default appears after the set input. The units
               are copied from XUNITS=

               (TRVUNITS=) The default appears after the set input. The units
               are copied from VUNITS=.

               
               If the data in the parameter plots is constant (e.g. constant
               inclination) then the upper limit is increased by 20 and
               the lower limit is decreased by 20. The default limits can 
               be adjusted in the Tilted Rings input window under 'limits'.

               

               Defaults in the Label window:
               
               (TITLEX=) The default title if Offset (...). For the dots
               is substituted the contents of XUNITS=
               
               (TITLEY=) The default title if Velocity (...). For the dots
               is substituted the contents of VUNITS=
              
               (MOSROWS=) A default for the number of rows with which to 
               build a mosaic of XV plots, appears after the input of slice
               angles. The default tends to make the mosaic a square.


               
               Defaults in other windows: 

               There are numerous defaults for colours, size of markers
               symbols etc. 
              

Keywords:      

               BLINK=              Start blinker window 
               BOX=                Data limits of input subsets
               CLIP=               Data between these two values are mapped 
                                   onto XV images
               COLEDIT=            Start the colour editor
               CONCOL=             Set the colour of the contours
               CONSET=             The source of the contour data
               CONTOURS=           The contour levels 
               EXTRACT=            (Re)plot 
               FILE=               Menu for saving and quitting
               FIXINC=             Convert galaxy angles to sky angles with
                                   this fixed inclination
               FIXPHI=             Convert galaxy angles to sky angles with
                                   this position angle
               GALANGLES=          Slice angles in galaxy system
               GALCENTRE=          Center of galaxy
               GALMODE=            Set angle mode to sky or galaxy
               GIDSSLICES=         Overlay subset slices on 
               HCDEVICE=           Hardcopy device
               HCFILENAME=         Hardcopy PostScript file name
               HELP=               Display help document
               INCLINATION=        Tiltes ring inclinations
               INCMINMAX=          Min. & max. of these inclinations
               INPUT=              Pop up INPUT window
               INSET=              The GIPSY data cube to extract slices from
               LABCOL=             Colour of labels in XV mosaic plots
               LABDELTAX=          Step size for tick marks in X direction
               LABDELTAY=          Step size for tick marks in V direction
               LABELS=             Pop up LABELS window
               LABSTARTX=          Start value for labels in X direction
               LABSTARTY=          Start value for labels in V direction
               LABSUBX=            Number of minor tick marks in X direction
               LABSUBY=            Number of minor tick marks in Y direction
               M0_APPLY=           Refresh Slices plot markers
               M0_COLOUR=          Colour of slice plot markers
               M0_LINEWIDTH=       Linewidth of slice plot markers 
               M0_SIZE=            Size (character height)
               M0_SYMBOL=          Symbol used for markers
               M1_APPLY=           Tilted ring markers
               M1_COLOUR=
               M1_LINEWIDTH=
               M1_SIZE=
               M1_SYMBOL=
               M2_APPLY=           Line-of-sight velocity markers
               M2_COLOUR=
               M2_LINEWIDTH=
               M2_SIZE=
               M2_SYMBOL=
               M3_APPLY=           Colour of moving markers in slices plot
               M3_COLOUR=
               M3_LINEWIDTH=
               M3_SIZE=
               M3_SYMBOL=
               MAPUNITS=           Units of data in GIPSY set
               MARKERS=            Pop up MARKERS window
               MONITOR=            Monitor events in Hermes
               MOSROWS=            Number of rows in mosaic
               M_APPLY=            Refresh all markers
               OVERSUB=            Subset number used for overlay of slice lines
               PHIMINMAX=          Min. & max. of of position angles
               POSANG=             Tilted ring position angles
               QUIT=               Close application and quit
               RADII=              Tilted ring radii
               RADMINMAX=          Min and max radii for plot
               RESET=              Reset plots
               ROTPAR=             Pop up rotation curve input window
               SAMPLE=             Sample size in current units
               SAVESLICES=         Save extracted slice data to set on disk
               SAVETILT=           Save modified TR data to file on disk
               SEGMODE=            Set mouse action in segment
               SKYANGLES=          Angle of slices in sky 
               SKYGAL=             Flag for galaxy or sky angle mode
               SKYMODE=            Slice angles defined in sky
               SLICEX0Y0=          Center of slice in grids or ph. coords.
               TITLEX=             Plot title along X
               TITLEY=             plot title along Y
               TRVUNITS=           Units for TR velocities (km/s,...)
               TRXUNITS=           Untis for TR offsets (arcsec,...)
               VELMINMAX=          Min., max velocity in TR plot
               VELOCITY=           The TR velocities
               VELSET=             One (sub)set with velocities for overlay
               VSYS=               Systemic velocity in VUNITS= units
               VUNITS=             Units of velocities in XV diagram
               XUNITS=             Units of offset in XV diagram
               XYSIZE=             Size of INSPECTOR window in pixels

limits:        The maximum number of input subsets (channel maps) is 4096.
               The maximum number of Tilted Ring radii is 1024.
               The maximum number of XV slices (is the maximum number of
               slice angles) is 1024.
               The maximum number of contours is 128.

               
Related docs:  See inspector.dvi or inspector.ps in 
               www.astro.rug.nl/~gipsy/htm/inspector


Description:   Calculate line of sight velocities from tilted ring
               parameter data and mark those velocities in XV maps.
               The XV maps are slices at arbitrary angles through a
               data cube.
               Changing the tilted ring parameters in one of the
               panels with a graphics cursor, results in an update
               of the velocities in the XV diagrams.
               Rather than using the tilted ring parameters, it is also
               possible to supply the program with an initial guess of
               the rotation curve. This initial guess can then be
               adjusted interactively.
               A full description of how to derive rotation curves with
               INSPECTOR and its advantages and disadvantages over
               existing methods is given in Swaters' PhD thesis
               (Rijkuniversiteit Groningen, in preparation).

               INSPECTOR compares radial HI velocities, obtained
               from (fitted) 'tilted ring' circular velocities, with
               projected velocities in data slices.
               A slice is a description of coordinates in a
               two dimensional subset at which image values are extracted
               from the data set in INSET=  A slice has a central position
               (SLICEX0Y0=) in the subset (entered in grids or physical
               coordinates), and an angle (ANGLE=). The central position
               can differ from the rotation centre (CENTRE=). The angle
               is entered in degrees and is defined wrt. the north in the
               direction of the east. This angle is constant along the
               slice therefore the projection system is taken into account
               for spatial systems.
               Samples along the slice are separated at distance
               SAMPLE= which must be specified in XUNITS=
               The offsets in X of a slice are positive towards the
               receding part of a galaxy.


Updates:       Oct 17, 1999: VOG, Document created.



#<

#>            inspector.tex
\documentclass[11pt,a4paper]{article}
\usepackage{graphicx}

\parskip=0.8cm

\begin{document}

\begin{center}
{\Large INSPECTOR}\\[1.5cm]
{\large Compares radial HI velocities, obtained from fitted 'tilted ring' 
circular velocities, with velocities in data slices}\\[1.5cm]
Nov 19,  1999
\end{center}

%\vspace*{\fill}
%\tableofcontents
%\clearpage
%\section{Introduction}

\noindent
{\bf 1. Purpose}

\noindent
Mass distributions in spiral galaxies can be studied by measuring the
{\it circular velocity} $V_C$ as function of distance $R$ to the centre. 
A plot of $V_{C}(R)$ is called a rotation curve. These velocities cannot be
obtained directly, we only observe the line-of-sight velocities. 
A map with the line-of-sight velocities as function of position
on the sky is called a {\it radial velocity field}. If one assumes
that gas is rotating symmetrically in a disk, a number of {\it tilted ring}
model parameters define the observed velocity field of a galaxy. The 
{\it tilted ring} model, as modified in {\it INSPECTOR\ }, consists of a 
set of concentric rings characterized by:

\noindent Geometrical components:

\noindent
$\bullet$ $X_0,Y_0$: sky coordinates of the rotation centre of the galaxy\\
$\bullet$ $i(R)$: the inclination, the angle between the normal to the plane of 
          the galaxy and the line-of-sight.\\
$\bullet$ $\phi(R)$: the position angle of the major axis of a ring projected
          onto the sky. This is an angle taken in anti-clockwise direction
          between the north direction on the sky and the major axis
          of the receding half of the galaxy.\\

\noindent Kinematical components:\\
$\bullet$ $V_0$: the velocity of the centre of the galaxy with respect
          to the sun, the so called {\it systemic velocity}.\\
$\bullet$ $V_{C}(R)$: the circular velocity at distance $R$ from the 
          centre. This velocity is constant in a ring.\\


\noindent
Programs that fit  $V_{C}(R)$, $i(R)$ and $\phi(R)$ 
to the data (like {\bf GIPSY} progam {\it ROTCUR}) produce best fit parameters
for each defined ring. Program {\it INSPECTOR\ } 
lets you inspect (by eye) the fit while displaying a position-velocity 
(XV) map taken along slices through the observed galaxy. On such a map
a set of radial velocities are plotted. These velocities are 
obtained from fitted circular velocities. {\it INSPECTOR} allows you to
change parameters $V_{C}(R)$, $i(R)$ and $\phi(R)$ interactively using the
mouse.
The results of the manual adjustments can be written to an Ascii file
on disk.


\noindent
{\bf 2.  Description of {\it INSPECTOR\ }}

\noindent
You will need a GDS data cube with dimension $>= 3$
The program works with physical coordinates( i.e. it can translate
degrees to grids) only if the subset axes are spatial and the profile 
axis is a velocity axis (FREQ or VELO). Otherwise calculations
and plotting is done in grid coordinates.

\begin{figure}[h]
  \centering
  \includegraphics[height=8cm,width=10cm]{overview.ps}
  \caption{\it Snapshot of the INSPECTOR panels showing the XV diagrams,
   the tilted ring parameters (right) and the buttons to control the 
   program }
  \label{fig:overview}
\end{figure}

\noindent
With such a cube (a set of channel maps) you are able   
to display slices through data in the selected subsets in so called   
XV diagrams.
In {\it INSPECTOR\ }, these diagrams are X-window images extracted 
from your cube. The colours in these images are controlled
by a colour map editor which allows you to modify the colour map and
the colour of the blanks in the data.
The main {\it INSPECTOR\ } window is colour window with
mouse and button controls (figure~\ref{fig:overview}). 
You can change its default size with keyword XYSIZE= which must be
specified at the same time as the name of the program. If the program
is running, the window is not resizeable.
If you have data that describes a tilted ring 
model for a galaxy ($V_{C}(R)$, $i(R)$, $\phi(R)$, the velocity, 
inclination and position angle a function of radius)
then this program calculates the line-of-sight velocities
and marks those velocities in your XV maps and displays
the tilted ring parameters in three plots to the right
of a mosaic with the extracted XV maps. The tilted ring parameters
can now be changed using the mouse.


\noindent
{\bf 3.  Coordinate systems}

\noindent
In this section we will discuss a mathematical transformation of 
coordinates defined in the plane of the sky and coordinates 
defined in the plane of a tilted ring. The resulting formulas 
are not used in the program directly, but they were essential in
plotting the figures in this document. With these figures, we 
had an extra check of the reliability of the geometry used in 
{\it INSPECTOR\ }.

\noindent
The central part of a galaxy can be described by constant $i$ en $\phi$, i.e.
a disk. Note that measuring $i$ en $\phi$ does not fix the orientation
of a galaxy in space. Without extra information, we don't know what the front 
or back is of a galaxy. Therefore, angles $i$ en $\phi$
allow two directions of the spin vector of the disk. 

\begin{figure}
   \centering
   \includegraphics[bb=130 270 486 603]{fig1.ps}
   \caption{\it Relation between coordinate system of observation ($X,Y,Z$) and the 
    system $\vec p, \vec q, \vec s$ which defines the central disk of a galaxy in 
    terms of inclination $i$ and position angle $ \alpha = \pi /2 - \phi$. Angle 
    $\theta$ (defined in the system of the galaxy) is the angle between the 
    major axis and a slice. $V_c$ is the circular velocity in a ring with 
    angles $i$ and $\alpha$. $Z$ is line-of-sight}
   \label{fig:fig1}
\end{figure}
\noindent Define a coordinate system coupled to the observations. 
The $XY$ plane is the plane of the sky. The $Z$ axis is the line-of-sight. 
The $Y$ axis is directed to the north (upwards if seen from under) and the 
$X$ axis to 
the east (i.e. to the left). We use capitals for this coordinate system 
and lower case characters $x,y,z$ for a system corresponding to 
the tilted-ring. Spin vector $\vec s$ is the unit vector along the rotation
axis of a ring.
For the central disk: ${\vec s} = {\vec s}_0$. In a physical system, $\phi$
is the angle between the north and the major axis of the galaxy. In this
chapter we want to derive some formulas that connect a position in the $xyz$ 
system to a position in the $XYZ$ system vv. In this mathematical system 
we define a position
angle measured from the $X$ axis, called $\alpha$:
$$ \alpha = \pi /2 \; - \; \phi. $$

\noindent
The components of $ \vec s$ are:
\begin{equation}
\begin{array} {rccl}
s_X & = &   & \sin \alpha \sin i \nonumber\\
s_Y & = & - & \cos \alpha \sin i \\
s_Z & = &   & \cos i             \nonumber
\end{array}
\end{equation}
Define a unit vector $\vec p$ along the receding part of the major axis.
The components of $\vec p$ are:
\begin{equation}
\begin{array} {rcl}
p_X & = &  \cos \alpha \nonumber\\
p_Y & = &  \sin \alpha \\
p_Z & = &  0           \nonumber
\end{array}
\end{equation}
\noindent
Finally, define a unit vector $ \vec q \perp \vec p$ and $ \perp \vec s$
according to $\vec q = \vec s \times \vec p$. The components of $\vec q$ are:
\begin{equation}
\begin{array}{rclccl}
q_X & = &  s_Y p_Z - s_Z p_Y & = & - & \sin \alpha \cos i  \nonumber\\
q_Y & = &  s_Z p_X - s_X p_Z & = &   & \cos \alpha \cos i  \\
q_Z & = &  s_X p_Y - s_Y p_X & = &   & \sin i              \nonumber
\end{array}
\end{equation}
\noindent
Define a second coordinate system, coupled to the central disk
of the tilted ring model: the $z$ axis along the rotation axis of 
the central disk i.e. $z \parallel {\vec s}_0$ and the $x$ axis along
the major axis of the central disk i.e. $x \parallel {\vec p}_0$.
Further, $y \parallel{\vec q}_0$. If we use the unit vectors $\vec e$ then
$\vec e_x = \vec p_0, \: \vec e_y = \vec q_0, \: \vec e_z = \vec s_0$.
This is the intrinsic coordinate system for the tilted ring system.
The relation between this system and the system in which we observe ($X,Y,Z$):
\begin{equation}
\begin{array} {rcclclcl}
\vec e_x & = &   & \cos \alpha _0 \: \vec e_X
             & + & \sin \alpha _0 \: \vec e_Y
             &   &      \nonumber\\
\vec e_y & = & - & \sin \alpha _0 \cos i_0 \: \vec e_X
             & + & \cos \alpha _0 \cos i_0 \: \vec e_Y
             & + & \sin i_0 \: \vec e_Z   \\
\vec e_z & = &   & \sin \alpha _0 \sin i_0 \: \vec e_X
             & - & \cos \alpha _0 \sin i_0 \: \vec e_Y
             & + & \cos i_0 \: \vec e_Z   \nonumber \\
\end{array}
\end{equation}

\noindent
Inverse:
\begin{equation}
\begin{array} {rcclclcl}
\vec e_X & = &   & \cos \alpha _0 \: \vec e_x
             & - & \sin \alpha _0 \cos i_0 \: \vec e_y
             & + & \sin \alpha _0 \sin i_0 \: \vec e_z   \nonumber\\
\vec e_Y & = &   & \sin \alpha _0 \: \vec e_x
             & + & \cos \alpha _0 \cos i_0 \: \vec e_y
             & - & \cos \alpha _0 \sin i_0 \: \vec e_z   \\
\vec e_Z & = &   &
             &   & \sin i_0 \: \vec e_y
             & + & \cos i_0 \: \vec e_z   \nonumber \\
\end{array}
\end{equation}

\noindent
Note that the coefficient-determinant of (4) is equal to 1.
For a point P($x,y,z$) in the tilted ring system the $X,Y,Z$
coordinates are:
($ x \vec e_x + y \vec e_y +z \vec e_z
=  X \vec e_X + Y \vec e_Y +Z \vec e_Z $):
\begin{equation}
\begin{array} {rclclcl}
X & = & x \cos \alpha _0 & - & y \sin \alpha _0 \cos i_0
                         & + & z \sin \alpha _0 \sin i_0 \nonumber \\
Y & = & x \sin \alpha _0 & + & y \cos \alpha _0 \cos i_0
                         & - & z \cos \alpha _0 \sin i_0 \\
Z & = &                  &   & y \sin i_0
                         & + & z \cos i_0 \nonumber
\end{array}
\end{equation}

\noindent
For a point P($X,Y,Z$) in the system of observation, the tilted ring 
coordinates $x,y,z$ are:
\begin{equation}
\begin{array} {rclllllll}
x & = &+& X \cos \alpha _0          & + & Y \sin \alpha _0          &   & \nonumber \\
y & = &-& X \sin \alpha _0 \cos i_0 & + & Y \cos \alpha _0 \cos i_0 & + & Z \sin i_0\\
z & = &+& X \sin \alpha _0 \sin i_0 & - & Y \cos \alpha _0 \sin i_0 & + & Z \cos i_0 \nonumber\\
\end{array}
\end{equation}

\noindent
All rings get their own coordinate system. For these systems
the same equations are applied but then $(i_0, \alpha_0)$ must be replaced
by $(i_k, \alpha _k)$.

\begin{figure}
   \centering
   \includegraphics[bb=130 270 486 603]{fig2.ps}
   \caption{\it Determine the line-of-sight component $V(X,Y)$ in terms of the 
     circular velocity $V_C$ (in the $xy$ plane), inclination $i$ 
     and azimuth $\beta$. $R$ is the radius in the plane of the galaxy and $R'$
     is the projection of $R$ onto the plane of the sky ($X,Y$).}
   \label{fig:fig2}
   \end{figure}
\noindent
{\bf 4.  Relation between radial velocity and circular velocity}

\noindent
Vector $\vec V_C$ represents the circular velocity in a tilted ring.
$\vec V_C$ is perpendicular to the ring and has a component in $x$ and 
$y$ direction. If $\theta$ is the azimuthal angle in the plane of the galaxy,
then:
\begin{equation}
\begin{array} {rcl}
V_x & = & V_c \sin \theta \nonumber \\
V_y & = & V_c \cos \theta \nonumber 
\end{array}
\end{equation}
$V_x$ is parallel to the $XY$ plane and has no component in the $Z$ direction.
However, $V_y$ has a component in the $Z$ direction: $V_Z = V_y \sin i$. This is
the observed radial velocity that we observe at a point $X,Y$. 
It must be corrected for the systemic (line-of-sight) velocity $V_0$.
The relation between the radial velocity and the circular velocity becomes:
\begin{equation}
V(X,Y) = V_0 + V_C \sin i \cos \theta
\end{equation}
The inclination $i$ is known, but the value of $\theta$, which depends on
$i$ and $\alpha$ must be calculated. In the next chapter we 
will demonstrate how to do this.


\noindent
{\bf 5. The azimuthal angle in the plane of the galaxy ($\theta$)}

\noindent
Angle $\theta$ depends on inclination $i$ and the angle between 
the major axis in the $XY$ plane and a line that connects 
$X_0,Y_0$ with $R'$ (the projection
of $R$ onto the $XY$ plane). Call this angle $\beta$. 
A slice through the projected galaxy ($XY$ plane) has a centre position
$X_s,Y_s$ and angle $\beta$ with the $X$ axis. Distinguish two situations:
\begin{enumerate}
\item $X_s,Y_s = X_0,Y_0$. The slice angle is equal to $\beta$.
\item $X_s,Y_s \ne X_0,Y_0$. Angle $\beta$ can be derived from 
the central position and the angle of the slice after intersecting the
slice with the ellipse which is a projection of the ring onto the $XY$ plane.
\end{enumerate}
In situation 1 there is a simple relation. Suppose the tilted ring coordinates
of $R$ are $x,y$, then $tan \theta = y/x$ and $tan \beta = {y cos i}/x$ and 
we have the relation:
\begin{equation}
\theta = \arctan {{\tan\beta}\over{\cos i}}
\end{equation}
For the radii $R$ and $R'$ we have $R \cos \theta = R' \cos \beta$. The length 
of the projected radius $R'$ is then:
\begin{equation}
R' = R {{\cos \theta}\over{\cos \beta}}
\end{equation}

\begin{figure}
   \centering
   \includegraphics[bb=130 270 486 603]{fig3.ps}
   \caption{\it Situation where a slice in the $XYZ$ plane has 
    centre $C$ which is not the centre of the projected ellipse. 
    Define $\gamma$ as an angle between this slice and the $X$ axis.}
   \label{fig:fig3}
\end{figure}
\noindent
Situation 2 is more complicated. Angle $\beta$ can be calculated if we 
know at which point(s) the slice intersects the projected ring in the
$XY$ plane. For a ring characterized by $R$, $\alpha$ and $i$, each point
on the ring must satisfy the equations $x^2+y^2=R^2$ and $z=0$. 
According to (7):
$$
\begin{array} {rllllllll}
x & = &+&X \cos \alpha        & + & Y \sin \alpha            &   & \nonumber \\
y & = &-&X \sin \alpha \cos i & + & Y \cos \alpha \cos i & + & Z \sin i\nonumber\\
0 & = &+&X \sin \alpha \sin i & - & Y \cos \alpha \sin i & + & Z \cos i \nonumber\\
\end{array}
$$
The last equation sets a constraint on $Z$:
$$
Z = {{-X\sin \alpha \sin i + Y\cos \alpha \sin i}\over{\cos i}}
$$
Substituting this expression for $Z$ in the expression for $y$ gives the 
relations for $x$ and $y$ in terms of the projected values $X$ and $Y$:
$$
\begin{array} {rllllll}
x & = & + & X \cos \alpha          & + & Y \sin \alpha  \nonumber\\
y & = & - & X \sin \alpha / \cos i & + & Y \cos \alpha / \cos i \nonumber\\
\end{array}
$$
The circle $x^2+y^2=R^2$ can be expressed in $X$ and $Y$:
$$ {(X \cos \alpha + Y \sin \alpha)}^2 
+ { ({{-X \sin \alpha  + Y \cos \alpha}\over{\cos i}}) }^2 = R^2$$
which is a rotated ellipse in the $XY$ plane:
\begin{equation}
A X^2 + B XY + C Y^2 = R^2
\end{equation}
with:
\begin{equation}
\begin{array} {rllll}
A & = & {\cos}^2 \alpha + {\sin}^2 \alpha / {\cos}^2 i  \nonumber\\
B & = & -2 \cos \alpha \sin \alpha \ {\tan}^2 i\\
C & = & {\sin}^2 \alpha + {\cos}^2 \alpha / {\cos}^2 i  \nonumber\\
\end{array}
\end{equation}
(for $\alpha=0$ the result is obvious: $X^2+{Y^2\over{\cos i}}= R^2$\\
so we could have started with this formula and insert the rotation 
expressions.)


\noindent
If the angle between the slice and the $X$ axis is $\gamma$ and the central
position $C$ of the slice has coordinates $X_s,Y_s$, then the line can 
be represented by $Y = mX + b$, \  $b = Y_s - m X_s$ and  
$m = \tan \gamma$.
Substitution of $Y = mX + b$ in (12) results in two solutions:
$$
X_{1,2} = {{-q \pm \sqrt{q^2-4pr}} \over{2p}}, \ \ \ \  Y_{1,2} = mX_{1,2} + b
$$
with $p,q,r$ equal to
\begin{equation}
\begin{array} {rllll}
p & = & A + Bm + C m^2  \nonumber\\
q & = & Bb + 2mbC\\
r & = & Cb^2 - R^2 \nonumber\\
\end{array}
\end{equation}
and $m = \tan \gamma$,  $b = Y_s - mX_S$.
We were looking for an expression for $\beta$: 
\begin{equation}
\tan \beta_{1,2} = {Y_{1,2}\over{X_{1,2}}}
\end{equation}
The variables $X_S, Y_S, \gamma$ are properties of a slice. The units 
in which we have to specify $X_S, Y_S$ must be the same as the units of the
radius in the tilted ring. For slices with angles $\gamma$ near $90^o$. a
problem arises because $\tan \gamma \rightarrow \infty$.
For $45^o < \gamma < 135^o$ and $225^o < \gamma < 315^o$  determine 
the intersection(s) with the ellipse and
$X = {1\over{m}}Y + b$. Because ${1\over{\tan \gamma}} = \tan (90^o-\gamma)$
we can also write $X = m' Y + b$ with $m' = \tan (90^o-\gamma)$. 
Then we find:
$$
Y_{1,2} = {{-q \pm \sqrt{q^2-4pr}} \over{2p}}, \ \ \ \  X_{1,2} = m'Y_{1,2} + b
$$
with $p,q,r$ equal to
\begin{equation}
\begin{array} {rllll}
p & = & A {m'}^2 + Bm' + C  \nonumber\\
q & = & Bb + 2m'bA\\
r & = & Ab^2 - R^2 \nonumber\\
\end{array}
\end{equation}
and again $$\tan \beta_{1,2} = {Y_{1,2}\over{X_{1,2}}}$$
 

\noindent
{\bf 6. Calculation of slice sample points on a sphere}


\noindent
Slice sample points are all separated at distance $b$. Suppose the slice
has angle $\alpha$ on a sphere. If we know a starting point $Q_1$ on
that sphere then the coordinates of the next sample point is calculated
using some spherical geometry.
For an arbitrary spherical triangle with sides $a, b, c$ and angles $\alpha \beta, \gamma$
opposite to these sides, there are a number of formulas that can be used to 
derive some useful relations.
First there is the sine formula:
$${\sin a \over{\sin \alpha}} = {\sin b \over{\sin \beta}} = {\sin c \over{\sin \gamma}}$$
Then there is the cosine formula for sides:
\begin{equation}
\begin{array} {rllll}
  \cos a &=& \cos b\ \cos c &+& \sin b\ \sin c\ \cos \alpha \\
  \cos b &=& \cos c\ \cos a &+& \sin c\ \sin a\ \cos \beta \\
  \cos c &=& \cos a\ \cos b &+& \sin a\ \sin b\ \cos \gamma \\
\end{array}
\end{equation}
\noindent
And the cosine formula for angles:
\begin{equation}
\begin{array} {rllll}
  \cos \alpha &=& -\cos \beta\  \cos \gamma &+& \sin \beta\  \sin \gamma\ \cos a \\
  \cos \beta  &=& -\cos \gamma\ \cos \alpha &+& \sin \gamma\ \sin \alpha\ \cos b \\
  \cos \gamma &=& -\cos \alpha\ \cos \beta  &+& \sin \alpha\ \sin \beta\  \cos c \\
\end{array}
\end{equation}

\noindent
\begin{figure}
   \centering
   \includegraphics[bb=90 237 422 533]{fig4.ps}
   \caption{\it A spherical triangle has sides a, b and c. Opposite to these
    sides are the angles $\alpha,  \beta, \gamma$. Angle $\beta$ is equal to the
    hour angle in degrees. 
    If the position of $Q_1$ is known and the length of $Q_1Q_2$ is $b$,
    what then are the coordinates of $Q_2$ if the angle between $PQ_1Q_2$
    is equal to $\alpha$?   }
   \label{fig:fig4}
\end{figure}

\noindent
Suppose you have the coordinates in hour angle and declination, 
$H_1, \delta_1$ of a point $Q_1$
on the sphere and you want to know the coordinates of a point $Q_2$ at 
distance $b$ so that the angle $PQ_1Q_2$ is $\alpha$.
This angle, the {\it slice angle}, is defined wrt. the positive Y-axis ($Q_1P$) in the 
direction of the positive latitude axis. Two situations are distinguished:
first, the direction of the positive latitude axis is to the right,
(as in the figure)
and second, the more usual case of an equatorial sky system, 
where the direction of positive latitude axis is to the left.
In the latter case, you can put $\alpha$ in the plot to the 
left of the axis $Q_1P$, and check that the same relations 
for a spherical triangle with the properties as described above,
will hold.

\noindent
We are interested in the position $Q_2= H_2, \delta_2$. 
Use the cosine relation:
$$\cos a = \cos b\ \cos c + \sin b\ \sin c\ \cos \alpha $$
This results in:
$$\cos (90-\delta_2) = \cos b\ \cos (90-\delta_1) +
 \sin b\ \sin (90-\delta_1)\ \cos \alpha $$
which is the same as:
\begin{equation}
\sin \delta_2 = \cos b\ \sin \delta_1 + \sin b \cos \delta_1 \cos \alpha 
\end{equation}
The value of $\delta_2$ can be found by taking the arcsine of the right part 
of the formula above.
From the sine formulas we use the relation:
$${\sin b \over{\sin \beta}} = {\sin a \over{\sin \alpha}}$$
which transforms to:
\begin{equation}
{\sin b \over{\sin (H_2-H_1)}} = {\sin (90-\delta_2) \over{\sin \alpha}}
\end{equation}
and therefore:
\begin{equation}
H_2-H_1 = \arcsin({{\sin \alpha.\sin b}\over{\cos\delta_2}})
\end{equation}
$\alpha$ and $b$ are properties of the sample, $H_1$ is the longitude
of $Q_1$, $\delta_2$ is calculated using formula (19). Then $H_2$
follows from the formula above.

\noindent
To check whether the new positions are reliable, you can put them 
in the distance formula:
$$\cos b = \cos c\ \cos a + \sin c\ \sin a\ \cos \beta \rightarrow$$
$$\cos b = \cos(90-\delta_1) \cos(90-\delta_2) + 
\sin(90-\delta_1) \sin(90-\delta_2)\cos(H_2-H_1) \rightarrow$$
\begin{equation}
\cos b = \sin\delta_1 \sin\delta_2+\cos\delta_1 \cos\delta_2 \cos(H_2-H_1)
\end{equation}
which gives the distance $b$ if both positions $Q_1$ and $Q_2$ are known.

\noindent
{\bf 6. Literature}

\noindent
Begeman, K.: 1987 Ph.D. thesis, Groningen University, chapter 3.2\\
Smart, W. M.: Textbook on Spherical Astronomy, Cambridge press 1977

\end{document}


#<

#>            inspector.h
/*
                           COPYRIGHT (c) 1999
                      Kapteyn Astronomical Institute
                University of Groningen, The Netherlands
                           All Rights Reserved.
*/


/*  inspector.c: include files     */

#include    "stdio.h"        /* Defines ANSI C input and output utilities */
#include    "stdlib.h"       /* Defines the ANSI C functions for number */
                             /* conversion, storage allocation, and similar tasks.*/
#include    "string.h"       /* Declares the ANSI C string functions*/
                             /* like:strcpy, strcat etc.*/
#include    "math.h"         /* Declares the mathematical functions and macros.*/
#include    "gipsyc.h"       /* Defines the ANSI-F77 types for Fortran to C intface */
                             /* including def. of char2str,str2char,tofchar,zadd */
                             /* and macros tobool and toflog */
#include    "float.h"        /* Definition of FLT_MAX etc.*/
#include    "ctype.h"        /* Declares ANSI C functions for testing characters */
                             /* like: isalpha, isdigit etc. also tolower, toupper.*/


/* Common includes */

#include    "init.h"         /* Declare task running to HERMES and initialize.*/
#include    "finis.h"        /* Informs HERMES that servant quits and cleans up the mess.*/
#include    "anyout.h"       /* General character output routine for GIPSY programs.*/
#include    "setfblank.h"    /* Subroutine to set a data value to the universal BLANK.*/
#include    "error.h"        /* User error handling routine. */
#include    "myname.h"       /* Obtain the name under which a GIPSY task is being run.*/
#include    "nelc.h"         /* Characters in F-string discarding trailing blanks.*/
#include    "minmax1.h"      /* Min, max of an array */
#include    "axunit.h"       /* Returns the units of the physical coordinates of an axis in a set */
#include    "cotrans.h"      /* Transformation from grid coordinates to physical coordinates vv. */
#include    "axtype.h"
#include    "grtoph.h"
#include    "phtogr.h"
#include    "factor.h"
#include    "skyrot.h" 


/* User input routines */

#include    "userfio.h"      /* Easy-C companions for user interface routines.*/
#include    "userint.h"      /* User input interface routines.*/
#include    "userlog.h"
#include    "userreal.h"
#include    "userdble.h"
#include    "usertext.h"
#include    "userchar.h"
#include    "usercharu.h"
#include    "userfio.h"      /* Easy-C companions of userxxx_c */
#include    "reject.h"       /* Reject user input.*/
#include    "cancel.h"       /* Remove user input from table maintained by HERMES.*/
#include    "keyevents.h"    /* Definitions for event-driven operation */
#include    "events.h"       /* Describes routines for event-driven programs.*/
#include    "eventmonitor.h" 
#include    "wkey.h"
#include    "deputy.h"
#include    "subst.h"
#include    "status.h"
#include    "setdblank.h"
#include    "getaxname.h"


/* Input of sets */

#include    "gdsinp.h"       /* Input of set, subsets, return # subsets.*/
#include    "gdsout.h"       /* Set for output */
#include    "gdspos.h"       /* Define a position in a subset.*/
#include    "gdsbox.h"       /* Define a box inside/around a subset.*/
#include    "gdsasn.h"       /* Copy input set */
#include    "gdscpa.h"       /* Change properties of an axis in output set */
#include    "gdscss.h"       /* Change size of output before gdsout call */
#include    "gds_exist.h"
#include    "gds_close.h"    /* Close set. */
#include    "gdsc_range.h"   /* Return lower left and upper right corner of a subset.*/
#include    "gdsc_word.h"
#include    "gdsc_ndims.h"   /* Return the dimensionality of a coordinate word.*/
#include    "gdsc_grid.h"    /* Extract grid value.*/
#include    "gdsc_fill.h"    /* return coordinate word filled with a grid */
                             /* value for each axis.*/
#include    "gdsi_read.h"    /* Reads data from (part of) a set.*/
#include    "gdsi_write.h"
#include    "gdsc_name.h"
#include    "gdsd_rchar.h"
#include    "gdsd_rreal.h"
#include    "gdsd_rdble.h"
#include    "gds_delete.h"

#include    "ggi.h"


#define MYMAX(a,b)     ( (a) > (b) ? (a) : (b) )
#define MYMIN(a,b)     ( (a) > (b) ? (b) : (a) )
#define BETWEEN(a,b,c) ( (a) < (b) ? (b) : ((a) > (c) ? (c) : (a) ) )
#define NINT(a)        ( (a) < 0 ? (int)((a)-.5) : (int)((a)+.5) )
#define ABS(a)         ( (a) < 0 ? (-(a)) : (a) )
#define PI             3.141592653589793
#define RAD(a)         ( (a) * 0.017453292519943295769237 )
#define DEG(a)         ( (a) * 57.295779513082320876798155 )

#define RELEASE        "2.1"      /* Version number */
#define MAXAXES        10         /* Max. axes in a set */
#define MAXSUBSETS     4096       /* Max. allowed subsets */
#define STRLEN         256        /* Max length of strings */
#define FILENAMELEN    256        /* Max length of file names */
#define FITSLEN        20         /* Max length of header items etc.*/
#define NOERRORLOOP    100        /* Special code for gdsinp */
#define NONE           0          /* Default levels in userxxx routines */
#define REQUEST        1
#define HIDDEN         2
#define EXACT          4
#define YES            1          /* C versions of .TRUE. and .FALSE. */
#define NO             0
#define FIRSTLINE      0
#define SECONDLINE     1
#define DEBUG          16

#define VELOCITY       0
#define INCLINATION    1
#define POSANG         2


/* Modify curve in user defined segment */

#define SEG_FACTOR     0
#define SEG_CONST      1
#define SEG_SLOPE      2

#define XOFFSETL       0.12       /* Viewport offset in normalized coordinates */
#define YOFFSETB       0.1        /* Left, Bottom, Right, Top */
#define XOFFSETR       0.06
#define YOFFSETT       0.05

/* Axis type */

#define LONGITUDE      1
#define LATITUDE       2  
#define FREQAXIS       3
#define VELAXIS        4


#define MAXSLICES      1024
#define MAXRINGS       1024
#define MAXCONTOURS    128        /* Max contour levels */ 


/* PGPLOT colours */

#define BACKGROUND    0         /* Color definitions for PGPLOT. */
#define FOREGROUND    1         /* Black if background is white. */
#define RED           2
#define GREEN         3
#define BLUE          4
#define CYAN          5
#define MAGENTA       6
#define YELLOW        7
#define ORANGE        8
#define GREENYELLOW   9
#define GREENCYAN    10
#define BLUECYAN     11
#define BLUEMAGENTA  12
#define REDMAGENTA   13
#define DARKGRAY     14
#define LIGHTGRAY    15


#define NORMAL       1                                                          
#define ROMAN        2
#define ITALIC       3
#define SCRIPT       4

#define DOTTED       4
#define DASHED       2
#define FULL         1

#define DOTCIRCLE    9
#define SMALLCIRCLE  4
#define PLUS         2
#define STAR         3    
#define CROSS        5


/* Initialize Fortran compatible string with macro 'fmake' */

#define fmake(fchr,size) { \
                           static char buff[size+1]; \
                           int i; \
                           for (i = 0; i < size; buff[i++] = ' '); \
                           buff[i] = 0; \
                           fchr.a = buff; \
                           fchr.l = size; \
                         }


float   fblank;
double  dblank;
fint    setlevel;
char    taskname[20];



typedef struct
/*------------------------------------------------------------*/
/* 'markertype'. There are three different markers: 1) The    */
/* markers in the tilted ring plots. 2) The markers for the   */
/* converted TR velocities in the slice plots. 3) The         */
/* velocity markers in the slice plots from e.g. a moments    */
/* map.                                                       */
/*------------------------------------------------------------*/
{
   int     color;
   float   size;
   int     width;
   int     symbol;      
}
markertype;




typedef struct
/*------------------------------------------------------------*/
/* 'sliceplottype'.                                           */
/* Define a structure that contains the properties of each    */
/* slice. Common properties are stored in a 'commonslicetype' */
/* structure. A number of functions set values to the struct. */
/* members.                                                   */
/* A slice is a cut with a fixed angle in a subset. If we     */
/* assemble such cuts for a number of subsets (in the         */
/* direction of the profile axis) and extract data along      */
/* these slices then you and up with image data that can be   */
/* plotted in what we call XV plots.                          */
/* The x-axis of such a plot is the slice axis, usually not   */
/* aligned with RA or DEC (==> X), the y-axis is usually a    */
/* velocity (==> V). Note that the user is not bound to these */
/* directions. He can define slices/profiles in any direction.*/
/* However, then all calculation will be in grids and there   */
/* are no conversions to physical coordinates. In the documen-*/
/* tation we use XV plot for all slices even if V is not the  */
/* the velocity in your profile.                              */
/*------------------------------------------------------------*/
{
   double  *posX;        /* Array that contains the X-coordinates of sample */
                         /* points on a slice for each subset */
   double  *posY;        /* Same for the Y-coordinates. Note that these */
                         /* positions can be used to extract image data of */
                         /* any subset in V-direction */
   double  cpos[2];      /* The central position of a slice in grids */
   double  off[2];       /* Offsets in user units or grids  wrt. the user */
                         /* given central position of a subset */
   double  angle;        /* Position angle slice usually wrt. the North in */
                         /* the direction of the East. In fact, the direction */
                         /* depends on the sign of cdelt[0] and whether we */
                         /* are working in a grid or physical coordinate */
                         /* system. The units are DEGREE */
   double  xlo;          /* Lower coordinate of the X-axis in the XV plot */
                         /* The units are stored in the 'slicecommontype' */
                         /* struct-member 'XUnits' */
   double  xhi;          /* Same for the upper coordinate. Note that the */
                         /* lower coordinate is always closest to the */
                         /* position angle of the slice */
   int     xlogr;        /* Min,max of X axis in grids */
   int     xhigr;   
   float   *image;       /* Data extracted from a data cube at points given */
                         /* by 'posX' and 'posY' in a certain subset */
                         /* The number of elements is equal to the number of */
                         /* pixels in a slice times the number of subsets */
   float   *conimage;    /* Same, but for contours of another set. */
   float   *velpoints;   /* Velocities read from 1 subset of another set. */
   int     ndat;         /* Number of sample points in current slice */
   float   VPxlo;        /* Viewport in normalized coordinates ([0..1]) */
   float   VPxhi;        /* The values depend on the slice number and the */
   float   VPylo;        /* number of mosaic rows and columns */
   float   VPyhi;
   float   VPxlopix;     /* Viewport in X-window pixel coordinates */
   float   VPxhipix;
   float   VPylopix;
   float   VPyhipix;
   int     leftpanel;    /* Axis labeling of current XV-plot at left axis? */
   int     bottompanel;  /* Axis labeling of current XV-plot at bottom axis? */
   float   *markerloX;
   float   *markerhiX;
   float   *markerloY;
   float   *markerhiY;         
}
sliceplottype;



typedef struct
/*------------------------------------------------------------*/
/* 'slicecommontype'. Store properties common to all slices   */
/* in one structure.                                          */
/*------------------------------------------------------------*/
{
   int     Xingrids;      /* In a XV plot, calculations and plotting is done */
                          /* in grid coordinates. There are no conversions */
                          /* possible to seconds of arc for both axis */
                          /* Note that the user can set this value always */
                          /* to TRUE */
   int     Vingrids;      /* Same for the V-axis (= y-axis) */
   fchar   XUnits;        /* Units are GRIDS if 'Xingrids' is true. Else, the */
                          /* units are entered by the user. Then, only units */
                          /* compatible with spatial axes are allowed */
   char    Xunits[FITSLEN+1];     /* Buffer for the X-units */
   double  Xunits2head;   /* Conversion factor between Xunits and the units */
                          /* found in the header */
   fchar   HeaderVunits;  /* Profile axis units, usually km/s */
   char    headerVunits[FITSLEN+1];     /* Buffer for HeaderVunits */
   fchar   HeaderXunits;  /* Profile axis units, usually km/s */
   char    headerXunits[FITSLEN+1];     /* Buffer for HeaderXunits */         
   fchar   VUnits;        /* Yunits are default the units found in the header */
                          /* of the input set. The user can enter different */
                          /* but compatible units */
   char    Vunits[FITSLEN+1];     /* Buffer for the V-units */
   double  Vunits2head;   /* Conversion factor between Vunits and the units */
                          /* found in the header */

   fint    mosaic[2];     /* Each slice gets a position in a mosaic. The */
                          /* number of columns is stored in the first array */
                          /* element*/
   double  Vsys;          /* The systemic velocity of your object (galaxy). */
                          /* If the V-axis in a XV plot is a real velocity */
                          /* axis, then the systemic velocity is the velocity */
                          /* where you add or subtract projected circular */
                          /* (ring) velocities. Units are km/s */
   fint    Vaxnum;        /* Set axis number that corresponds to the V-axis */
   fint    Vcword;        /* Coordinate word of the V-axis (=profile axis) */
   double  Vmingr;        /* Grid corresponding to first velocity */
   double  Vmaxgr;        /* Grid corresponding to last velocity */
   double  Vminph;        /* Physical coord. corresp. to first vel. in VUnits */
   double  Vmaxph;        /* Physical coord. corresp. to last vel. in VUnits */
   fint    Vlen;          /* Number of pixels in V direction */
   int     numslices;     /* Number of slices specified by user */
   double  delta;         /* Take image samples along the slice at distances */
                          /* 'delta' starting at the central position */   
   int     rows;          /* Number of rows with XV plots. */
   int     cols;          /* Number of columns with XV plots. */
   int     numrings;      /* Number of overlay velocities, one for each */
                          /* tiltes ring */
   double  xloall;        /* Min. value of all xlo's of the X-axis */
   double  xhiall;
   int     ndatmax;       /* Max. number of sample points */
   int     xloallgr;      /* Min max of X axis in grids of all slices */
   int     xhiallgr;         

   /* Labels along the plot axes: */

   fchar   labXUnits;     /* Units that will be used in the plot. */
   char    labXunits[FITSLEN+1];
   double  Xunits2labels; /* Convert user X-units to units of plot axis */
   fchar   labVUnits;     /* Units that will be used in the plot. */
   char    labVunits[FITSLEN+1];
   double  Vunits2labels; /* Convert user V-units to units of plot axis */
   double  labstartxy[2]; /* Overrule default label values */
   double  labdeltaxy[2];
   fint    labsubxy[2];   /* Minor ticks */
   fint    labcol;        /* Colour of axis and labels */
   float   chsize;        /* This fraction times current ch.size is ch.size */
   float   symsize;       /* Same for the symbol size */
   int     plotexist;
   float   contours[MAXCONTOURS];       /* Max. 128 contour levels allowed */
   int     numcontours;
   int     concol;        /* Colour of contours */
   float   maxlablen;     /* Max.chars in labels at left side of slice plot */
   markertype  marker1;   /* Converted TR overlay velocities */
   markertype  marker2;   /* Line of sight velocities from set */
   markertype  marker3;   /* Moving markers */
}
slicecommontype;



typedef struct
/*------------------------------------------------------------*/
/* 'parameterplottype'.                                       */
/*------------------------------------------------------------*/ 
{
   char    name[15];
   int     ndata;
   float   *wdata;             /* Work data = changed 'sdata' */
   float   *sdata;             /* Storage for original data */
   float   minmaxdat[2];
   float   VPxlo;              /* Viewport in normalized coordinates ([0..1]) */
   float   VPxhi;
   float   VPylo;
   float   VPyhi;
   float   Wxlo;               /* Window in world coordinates */
   float   Wxhi;
   float   Wylo;
   float   Wyhi;
   char    xtitle[80];
   char    ytitle[80];
   char    plotstr[10];        /* Code for PGPLOT for axis labeling */
   int     exist;
   int     segcount;
   float   xseg1;
   float   xseg2;
   float   yseg1;
   float   yseg2;
}
parameterplottype;


typedef struct 
/*------------------------------------------------------------*/
/* 'paramcommontype'                                          */
/* attributes etc that are common to all parameter plots      */
/*------------------------------------------------------------*/
{
   markertype marker;
   float      *radii;             /* Radii common to all parameter plots */
   int        numTRrad;           /* Number of tilted ring radii */
   float      minrad;
   float      maxrad;
   float      deltaradmin;        /* Smallest distance between two radii */   
   int        segmode;      
   fchar      TRVUnits;           /* Yunits are default the units found in the header */
                                  /* of the input set. The user can enter different */
                                  /* but compatible units */
                                  
   char       TRVunits[FITSLEN+1];  /* Buffer for the V-units */
   fchar      TRXUnits;                                  
   char       TRXunits[FITSLEN+1]; 
   double     TRXunits2Xunits;
   double     TRVunits2head;
}
paramcommontype;



typedef struct
/*------------------------------------------------------------*/
/* 'settype'. Define a struct for a set with its properties.  */
/*------------------------------------------------------------*/
{
   char    buf[FILENAMELEN+1];   /* A buffer for the set name */
   fchar   Name;                 /* The set name */
   fint    subsets[MAXSUBSETS];  /* Array for the subset coordinate words */
   fint    setdim;
   fint    subdim;
   fint    flo[MAXAXES];         /* Low  edge of frame in grids */
   fint    fhi[MAXAXES];         /* High edge of frame in grids */
   fint    blo[MAXAXES];         /* User low  edge of frame in grids */
   fint    bhi[MAXAXES];         /* User high edge of frame in grids */
   fint    nsubs;                /* Number of subsets entered in 'gdsinp' */
   fint    axnum[MAXAXES];       /* Array of size MAXAXES containing the */
                                 /* axes numbers.  The first elements (upto */
                                 /* the dimension of the subset) contain the */
                                 /* axes numbers of the subset, the other */
                                 /* ones ontain the axes numbers outside the */
                                 /* the subset ordered ccording to the */
                                 /* specification by the user. */
   fint    axcount[MAXAXES];     /* Array of size MAXAXES containing the */
                                 /* number of grids along an axes as */
                                 /* specified by the user. The first elements */
                                 /* (upto the dimension of the subset) */
                                 /* contain the length of the subset axes, */
                                 /* the other ones contain the the number of */
                                 /* grids along an axes outside the subset. */
                                 /* the operation for each subset, Class 2 */
                                 /* is for applications for which the */
                                 /* operation requires an interaction between */
                                 /* the different subsets. */
   double  cdelt[MAXAXES];       /* The grid spacings in header units */
   double  maprotation;          /* Rotation from header for spatial map */
   double  cpos[2];              /* Central position */
   bool    grids;                /* Physical coordinates involved for subsets?*/
   float   clip[2];              /* Clip levels used to display the XV plots */
   float   defclip[2];           /* Default clip levels from header */
   int     exist;                /* Is this set used? */
   char    dataunits[FITSLEN+1];
}
settype;



typedef struct
/*------------------------------------------------------------*/
/* 'gidstype'. For overlay's.                                 */
/*------------------------------------------------------------*/
{
   char      buf[FILENAMELEN+1];                 /* A buffer for the set name */
   bool      overlay;                               /* Overlay plot in GIDS ? */
   fint      blo[2], bhi[2];
   fint      subset;
   float     flo[2], fhi[2];           /* Corners of total GIDS area in grids */
   fchar     set;   
}
gidstype;


typedef struct
{
   settype              inset;            /* Extract XV data from this set */
   settype              contourset;       /* Overlay contours from this set */
   settype              velocityset;      /* Overlay vel. symbols from this set */   
   settype              outset;
   slicecommontype      slicecom;
   sliceplottype        *slice;   
   parameterplottype    parplot[3];       /* Velocity, Inclination, p. angle */
   paramcommontype      parcommon;
   int                  boxexist;
   ident                mainplotGGIid;
   ident                parplotGGIid[3];
   fint                 PGmainplotid;
   fint                 PGparplotid[3];
   ident                plotcursorid[3];  /* Ggi plot cursor id */
   int                  hardcopy;
   fint                 PGhardcopyid;
   char                 PSname[FILENAMELEN];    /* File name for hard copy */
   char                 savedatname[FILENAMELEN];  /* File name to write modified TR data */   
   gidstype             gids;
   int                  radminmaxnoevent;
} globaltype;


extern void setcolor( int col );
extern void plsymbol( float x, float y, int symbol );
extern void setchsize( float ch );
extern void setlinestyle( int  style );
extern void setwidth( int width );
extern void setfont( int font );
extern void plmove( float x, float y );
extern void pldraw( float x, float y );
extern void pltext( float   x,
                    float   y,
                    float   angle,
                    float   fjust,
                    char   *text );
extern void plrect( float  *box, int     colour );
extern void setmarker( float x, float y, fint sym );
extern void clearpage( void );
extern void opengidsplot( void );
extern void closegidsplot( void );
extern void plotslice( globaltype *g, int i );
extern void setwindowandviewport( sliceplottype     *slice,
                                  slicecommontype   *slicecom );
extern fint initplot( char *, int, int );
extern void plotaxis( sliceplottype     slice,
                      slicecommontype  *slicecom,
                      fchar             Setin );
extern void plotTRdata( globaltype *g, int );
extern void plpoints( float *x, float *y, int ndat, int symbol );
extern void getplotpositions( sliceplottype    *slice,
                              slicecommontype  *slicecom,
                              int              hardcopy );
extern void selectplot( int id );
extern void mouse1( ident         id,
                    GgiPlotInfo   info,
                    void          *arg );
extern void plotaxis( sliceplottype     slice,
                      slicecommontype  *slicecom,
                      fchar             Setin );
extern void plotcontours( globaltype *g, int i );
extern void putmarkersfirsttime( globaltype *g );

extern void plotsetvelocities( globaltype *g, int );
extern void clearstr( fchar );
extern void hermesstatus( char * );
extern void error_status( char *, char *, char * );
extern void clearstatus( void );
extern bool UserLog( char * );
extern void clearfchar( fchar );
extern int dispcoord( double, double, double, double, double *, double *, double  * );
extern float **fmatrix( int, int, int, int );
extern void freefmatrix( float **, int, int );
extern float getipval( float, float, fint *, fint *, float ** );
extern int Vc2Vxy( double   *cpos,
                   double    Vsys,
                   double    radius,
                   double    Vcirc,
                   double    phideg,
                   double    incdeg,
                   double    sl_angledeg,
                   double   *sl_off,
                   double   *projvel,
                   double   *projrad );
extern double distance( double    long1,
                        double    lat1,
                        double    long2,
                        double    lat2 );
extern void annotatesliceplot( globaltype *g ) ;
extern void writeslices( globaltype *g );
extern void overlayslices( globaltype *g ); 
extern double atantan( double   beta,
                       double   y );
extern int emptykey( char * );
#<

#>            gui.h
#if !defined(_gui_h_)
#define _gui_h_
void gui(int xsize, int ysize);             /* activate GUI */
void gui_status(char *message, int lineno); /* display status message */
void gui_error(char *message);              /* display error message */
void gui_applycol(void);                    /* apply colors from map */
void gui_datarange(float v1, float v2);     /* set data range for wedge */
void gui_units(char *units);                /* set units for wedge */
#endif

#<

#>            colormenu.h
#if !defined(_colormenu_h_)
#define _colormenu_h_
ident ColorMenu(char *key, char* label, char *help);
#endif
#<

#>            rotpar.h
#if !defined(_rotpar_h_)
#define _rotpar_h_
void Rotpar(char *key);
#endif
#<

#>            input.h
#if !defined(_input_h_)
#define _input_h_
void Input(char *key);
#endif
#<

#>            labels.h
#if !defined(_labels_h_)
#define _labels_h_
void Labels(char *key);
#endif
#<

#>            helptext.h
#if !defined(_helptext_h)
#define _helptext_h

#define H_QUIT       "Exit program"
#define H_SAVETILT   "Save modified tilted ring parameters to file"
#define H_INSET      "Enter subsets to extract slices from\n e.g. u5414cl_s FREQ  or u5414cl_s FREQ -10:10"                   
#define H_BOX        "Limit subset size to these ranges: xlo, ylo, xhi, yhi"
#define H_CLIP       "Map this data range on available colours"
#define H_CONSET     "Overlay contours from this set"
#define H_VELSET     "Overlay velocities from this (sub)set on\n the XV diagrams."
#define H_VUNITS     "Set tilted ring velocity units\n i.e. one of MM/S, CM/S, M/S, KM/S"
#define H_XUNITS     "Set tilted ring radii units\n i.e. one of ARCSEC, ARCMIN or DEGREE"
#define H_RADII      "Enter at most 1024 radii, e.g. file(asciifile.dat,1,1:)"
#define H_VELOCITY   "Enter tilted ring velocities"
#define H_INC        "Enter tilted ring inclinations in degrees"
#define H_PHI        "Enter tilted ring position angles in degrees"
#define H_RADMINMAX  "Increase or decrease the limits of the radius axis in\n the tilted ring parameter plots"
#define H_VELMINMAX  "Increase or decrease the limits of the velocity axis in\n the tilted ring parameter plots"
#define H_INCMINMAX  "Increase or decrease the limits of the inclinations axis in\n the tilted ring parameter plots"
#define H_PHIMINMAX  "Increase or decrease the limits of the position anglesaxis in\n the tilted ring parameter plots"
#define H_VSYS       "Systemic velocity in current units"
#define H_CENTRE     "The centre of rotation = position of central disk."
#define H_CONTOURS   "Max. 128 values for contour levels"
#define H_CONTCOL    "PGPLOT colour of contours"
#define H_LABCOL     "Colour of plot labels"
#define H_LABSTARTX  "Enter (plot-label) start value for x axis"
#define H_LABSTARTY  "Enter (plot-label) start value for v axis"
#define H_LABDELTAX  "Enter step size for x axis"
#define H_LABDELTAY  "Enter step size for v axis"
#define H_LABSUBX    "Number of sub intervals between X-ticks."
#define H_LABSUBY    "Number of sub intervals between Y-ticks."
#define H_LABUNITSV  "Label velocity in XV diagrams in these units"
#define H_LABUNITSX  "Label radius offset in XV diagrams in these units"
#define H_MOSROWS    "Number of rows containing XV images"
#define H_MOSCOLS    "Number of columns containing XV images"
#define H_EXTRACT    "Make slices plot"
#define H_GIDSSLICES "Overlay sample positions on set in GIDS"
#define H_SLICEX0Y0  "Enter central position of slice"
#define H_SKYANGLE   "Slice angles defined in sky plane"
#define H_GALANGLE   "Slice angles defined in galaxy plane" 
#define H_FIXINC     "Inclination (degrees) to convert angles from galaxy to sky plane"
#define H_FIXPHI     "Position angle (degrees) to convert angles from galaxy to sky plane"
#define H_ANGLE      "Position angles (degrees) of slices"
#define H_SAMPLE     "Enter sample spacing in current units"
#define H_HELP       "Show help document. Press HELP again to remove"
#define H_FILE       NULL
#define H_INPUT      NULL
#define H_ROTPAR     NULL
#define H_LABELS     NULL
#define H_SLICES     NULL
#define H_SEGMODE    "Modify curve within segment with mouse button 3\n in one of these modes (selection also\n with CTRL mouse 1)." 
#define H_OVERSUB    "Subset on which slice lines\n are plotted in GIDS"
#define H_TRXUNITS   "Tilted ring radiii units"
#define H_TRVUNITS   "Tilted ring velocity units"
#endif

#<

#>            coledit.h
#if !defined(_coledit_h_)
#define _coledit_h_
void Coledit(char *key, int ci, int nc, int bl);
#endif
#<

#>            markers.h
#if !defined(_markers_h_)
#define _markers_h_
void Markers(char *key);
#endif
#<

#>            blinker.h
#if !defined(_blinker_h_)
#define _blinker_h_
void  Blinker(char *key, int f1, int f2);
#endif
#<

#>            inspector.c
/* inspector.c -XT
                            COPYRIGHT (c) 1999
                      Kapteyn Astronomical Institute
                University of Groningen, The Netherlands
                           All Rights Reserved.

*/

#include    "inspector.h"    /* Defines a.o. the global structure to work with */
#include    "cmain.h"        /* Defines the main body of a C program with */
                             /* MAIN_PROGRAM_ENTRY and IDENTIFICATION */
#include    "gui.h"          /* Graphical user interface */
#include    "pgeras.h"
#include    "pgiden.h" 
#include    "pgclos.h" 


float      **subsetdata = NULL;          /* Buffer for subset image data */






static void getTRdatalimits( ident id,
                             char  *key,
                             int   code,
                             void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: VEL/INC/PHI/MINMAX= Keyword handler.              */
/*------------------------------------------------------------*/
{
   globaltype  *g = (globaltype*) arg;   
   float       userval[2];
   fint        nitems = 2;
   fint        dfault = HIDDEN;
   fint        r;    
   int         thisplot;   
   
  
   if (strncmp(key, "VEL", 3) == 0)
      thisplot = VELOCITY;
   else if (strncmp(key, "INC", 3) == 0)
      thisplot = INCLINATION;
   else if (strncmp(key, "PHI", 3) == 0)
      thisplot = POSANG;
   else 
      return;

   r = userreal_c( userval,
                   &nitems,
                   &dfault,
                   tofchar(key),
                   tofchar(" ") );                      
    
   if (userval[0] > userval[1])
   {
      float swap = userval[0];
      userval[0] = userval[1];
      userval[1] = swap;
   }            
   g->parplot[thisplot].minmaxdat[0] = userval[0];
   g->parplot[thisplot].minmaxdat[1] = userval[1];   
   
   plotTRdata( g, thisplot ); 
}




static void getTRdata( ident id,
                       char  *key,
                       int   code,
                       void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: VELOCITY= INCLINATION= and POSANG= keyword        */
/* handler                                                    */
/*------------------------------------------------------------*/
{
   globaltype  *g = (globaltype*) arg;
   fint         nitems;
   fint         dfault  = HIDDEN;
   float        inputdata[MAXRINGS];
   fint         r; 
   int          i;
   int          ndat;
   int          thisplot = -1;
   float        minval, maxval;
   char         keyword[20];
   static int   first;


   if (strncmp(key, g->parplot[VELOCITY].name, 8) == 0)
      thisplot = VELOCITY;
   if (strncmp(key, g->parplot[INCLINATION].name, 11) == 0)
      thisplot = INCLINATION;
   if (strncmp(key, g->parplot[POSANG].name, 6) == 0)
      thisplot = POSANG;
 
   clearstatus();
  
   ndat = g->parplot[thisplot].ndata;  
  
   if (ndat == 0)
   {
      error_status( "No tilted ring radii entered!", 
                    "First enter radii to define number of tilted rings.", "" );
      return;
   }
   nitems = ndat;   

   inputdata[0] = 0.0;
   r = userreal_c( inputdata,
                   &nitems,
                   &dfault,
                   tofchar(key),
                   tofchar(" ") );
      
   for (i = 0; i < r; i++)
   {
      g->parplot[thisplot].sdata[i] = 
      g->parplot[thisplot].wdata[i] = inputdata[i]; 
   }
   for (i = MYMAX(r,1); i < ndat; i++)         /* Add what is missing */
   {
      g->parplot[thisplot].sdata[i] =
      g->parplot[thisplot].wdata[i] = g->parplot[thisplot].sdata[i-1];
   }     

   g->parplot[thisplot].minmaxdat[0] = 
   g->parplot[thisplot].minmaxdat[1] = g->parplot[thisplot].sdata[0]; 
   
   minval = maxval = g->parplot[thisplot].sdata[0];
   for (i = 0; i < ndat; i++)
   {         
      float val;
      val = g->parplot[thisplot].sdata[i];

      if (val < minval)
         minval = val;
      if (val > maxval)
         maxval = val;
   }
   g->parplot[thisplot].minmaxdat[0] = minval;
   g->parplot[thisplot].minmaxdat[1] = maxval;
   anyoutf( DEBUG, "DEBUG (getTRdata): data min, max = %g %g", 
            g->parplot[thisplot].minmaxdat[0], g->parplot[thisplot].minmaxdat[1] );
   plotTRdata( g, thisplot );
   g->parplot[thisplot].exist = YES;   
   if (g->slicecom.plotexist && g->parplot[0].exist && g->parplot[1].exist && g->parplot[2].exist)
   {
      /* Draw TR markers in slice plots */
      putmarkersfirsttime( g );
   }


   if (thisplot == VELOCITY)
      strcpy( keyword, "VELMINMAX=");
   else if (thisplot == INCLINATION)
      strcpy( keyword, "INCMINMAX="); 
   else if (thisplot == POSANG)
      strcpy( keyword, "PHIMINMAX=");       
      
   if (first)
   {
      if (emptykey(keyword))      
      {
         /* Place values in field only */
         SuppressKeyevent( getTRdatalimits, keyword );
         wkeyf( "%s %g %g", keyword, minval, maxval );
      }
      else 
      {
         TriggerKey( keyword );
      }
      first = NO;
   }
   else
   {
      /* Replotting is already done by this function.  */
      /* There is no need to trigger replotting again. */
      SuppressKeyevent( getTRdatalimits, keyword );      
      wkeyf( "%s %g %g", keyword, minval, maxval );
   }   
}




static void getRlimits( ident id,
                        char  *key,
                        int   code,
                        void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: RADMINMAX= Keyword handler.                       */
/*------------------------------------------------------------*/
{
   globaltype  *g = (globaltype*) arg;
   fint       nitems = 2;
   fint       dfault = HIDDEN;
   fint       r;
   float      userval[2];
   int        j; 
   
  
   if (g->radminmaxnoevent)
   {
      /* Place values in input field and return immediately */
      g->radminmaxnoevent = NO;
      return;
   }
   r = userreal_c( userval,
                   &nitems,
                   &dfault,
                   tofchar(key),
                   tofchar(" ") );
                       
   if (r > 0)
      g->parcommon.minrad = userval[0];
   if (r > 1)
      g->parcommon.maxrad = userval[1];
   
   if (g->parcommon.minrad > g->parcommon.maxrad)
   {
      float swap = g->parcommon.minrad;
      g->parcommon.minrad = g->parcommon.maxrad;
      g->parcommon.maxrad = swap;
   }
   for (j = 0; j < 3; j++)   
   {
      plotTRdata( g, j ); 
   }   
}




static void getTRradii( ident id,
                        char  *key,
                        int   code,
                        void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: RADII= keyword handler.                           */
/* Read tilted ring radii. These are the points on the X axis */ 
/* of the parameter plots for which Velocities, Inclinations  */
/* and Position Angles are given. The number of radii sets    */
/* the number of data points to store in the arrays 'wdata'   */
/* and 'sdata'. The first array is a work copy of the last    */
/* and is the modified array written to disk if requested.    */
/*------------------------------------------------------------*/
{
   globaltype  *g = (globaltype*) arg;
   fint         nitems  = MAXRINGS;
   fint         dfault  = HIDDEN;
   fint         r; 
   float        radii[MAXRINGS];     
   int          i;
   float        dmin;
   static int   first = YES;
      

   clearstatus();

   r = userreal_c( radii,
                   &nitems,
                   &dfault,
                   tofchar(key),
                   tofchar(" ") );

   if (g->parcommon.radii == NULL) 
      free( g->parcommon.radii );      

   g->parcommon.radii = (float *) calloc( (int) r, sizeof(float) );
   if (g->parcommon.radii == NULL)
   {
      error_status( "Cannot allocate enough memory!",
                    "No memory for all your rings",
                    "Perhaps you should restart with less rings or slices" );
      return;      
   }

   for (i = 0; i < 3; i++)
   {
      if (g->parplot[i].wdata == NULL)  /* Free what was previously allocated */
         free( g->parplot[i].wdata );
      if (g->parplot[i].sdata == NULL) 
         free( g->parplot[i].sdata );

      g->parplot[i].wdata = (float *) calloc( (int) r, sizeof(float) );
      g->parplot[i].sdata = (float *) calloc( (int) r, sizeof(float) );

      if (g->parplot[i].wdata == NULL ||
          g->parplot[i].sdata == NULL )
      {
         error_status( "Cannot allocate enough memory!",
                       "No memory for all your rings",
                       "Perhaps you should restart with less rings or slices" );
         g->parplot[VELOCITY].ndata = 0;
         g->parplot[INCLINATION].ndata = 0;
         g->parplot[POSANG].ndata = 0;
         return;
      }
   }
   
    
   g->parcommon.numTRrad = (int) r;
   for (i = 0; i < 3; i++) 
   {
      g->parplot[i].ndata = (int) r;
   }
   g->parcommon.minrad = g->parcommon.maxrad = 0.0;      
    
   dmin = ABS( radii[1] - radii[0] );    
   for (i = 0; i < r; i++)
   {
      g->parcommon.radii[i] = ABS( radii[i] );
      if (g->parcommon.radii[i] > g->parcommon.maxrad)
         g->parcommon.maxrad = g->parcommon.radii[i];

      if (i > 0)
      {
         float d;
         d = ABS( g->parcommon.radii[i] - g->parcommon.radii[i-1] );
         if (d < dmin )
            dmin = d;
      }         
   }
   g->parcommon.deltaradmin = dmin;
   
   
   anyoutf( DEBUG, "DEBUG (getTRradii): radius min, max = %g %g deltamin = %g", 
            g->parcommon.minrad, g->parcommon.maxrad, g->parcommon.deltaradmin );
         
   /*--------------------------------------------------*/
   /* Force reading keywords that set the velocities,  */
   /* inclinations and position angles. By this, avoid */
   /* interference with events that would act on       */
   /* inconsistent parameter plot arrays.              */
   /*--------------------------------------------------*/
   getTRdata( NULL, "VELOCITY=", KEYCHANGE, g );              /* Pseudo event */
   getTRdata( NULL, "INCLINATION=", KEYCHANGE, g );
   getTRdata( NULL, "POSANG=", KEYCHANGE, g ); 


   if (first)
   {
      if (emptykey("RADMINMAX="))      
      {
         /* Place values in field only */
         SuppressKeyevent( getRlimits, "RADMINMAX=" );
         wkeyf( "RADMINMAX=%g %g", g->parcommon.minrad, g->parcommon.maxrad );
      }
      else 
      {
         TriggerKey("RADMINMAX=");
      }
      first = NO;
   }
   else
   {
      /* Replotting is already done by this function.  */
      /* There is no need to trigger replotting again. */
      SuppressKeyevent( getRlimits, "RADMINMAX=" );      
      wkeyf( "RADMINMAX=%g %g", g->parcommon.minrad, g->parcommon.maxrad );
   }
}




static void getcliplevels( settype *set )
/*------------------------------------------------------------*/
/* PURPOSE: define the range of image values that will be     */
/*          assigned the maximum range of available colour-   */
/*          numbers.                                          */
/* The default values are extracted from the set header.      */
/*------------------------------------------------------------*/
{
   fint   r;
   float  clip[2];
   float  minclip = fblank;
   float  maxclip = fblank;
   float  dummy = 0.0;
   int    i;


   for (i = 0; i < set->nsubs; i++)
   {
      r = 0;
      gdsd_rreal_c( set->Name, tofchar("DATAMIN"), &(set->subsets[i]),
                    &dummy, &r );
      if (r >= 0)
      {
         /* Found a value */
         if (minclip == fblank)
            minclip = dummy;
         else
            if (dummy < minclip)
               minclip = dummy;
      }


      r = 0;
      gdsd_rreal_c( set->Name, tofchar("DATAMAX"), &(set->subsets[i]),
                    &dummy, &r );
      if (r >= 0)
      {
         /* Found a value */
         if (maxclip == fblank)
            maxclip = dummy;
         else
            if (dummy > maxclip)
               maxclip = dummy;
      }
   }

   clip[0] = minclip;
   clip[1] = maxclip;
   if (clip[0] == clip[1])
   {
      clip[1] = clip[0] + 1.0; 
   }
   if (clip[0] != fblank && clip[1] == fblank)
   {
      clip[1] = clip[0] + 1.0;      
   }
   if (clip[0] == fblank && clip[1] != fblank)
   {
      clip[0] = clip[1] - 1.0;      
   }   
   set->defclip[0] = clip[0];
   set->defclip[1] = clip[1];
   wkeyf( "CLIP=%g %g", set->defclip[0], set->defclip[1] );
}



#if (0)
static void getbool( ident  id,
                     char  *key,
                     int    code,
                     void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: Keyword handler for bools.                        */
/*------------------------------------------------------------*/                
{
   bool       *userval = (bool  *) arg;   
   fint       nitems  = 1;
   fint       dfault  = HIDDEN;

   
   *userval = toflog( *userval );   
   (void) userlog_c( userval,
                     &nitems,
                     &dfault,
                     tofchar(key),
                     tofchar(" ") );
   *userval = tobool( *userval );
} 
#endif



static void getdble( ident id,
                     char  *key,
                     int   code,
                     void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: Keyword handler for doubles.                      */
/*------------------------------------------------------------*/
{
    double     *userval = (double *) arg;
    fint       nitems = 1;
    fint       dfault = HIDDEN;

    (void) userdble_c( userval,
                       &nitems,
                       &dfault,
                       tofchar(key),
                       tofchar(" ") );
}



static void getint( ident id,
                    char  *key,
                    int   code,
                    void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: Keyword handler for integers.                     */
/*------------------------------------------------------------*/
{
    fint      *userval = (fint *) arg;
    fint       nitems = 1;
    fint       dfault = HIDDEN;

    (void) userint_c( userval,
                      &nitems,
                      &dfault,
                      tofchar(key),
                      tofchar(" ") );
}




static void getfloat( ident id,
                      char  *key,
                      int   code,
                      void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: Keyword handler for floats.                       */
/*------------------------------------------------------------*/
{
    float     *userval = (float *) arg;
    fint       nitems = 1;
    fint       dfault = HIDDEN;

    (void) userreal_c( userval,
                       &nitems,
                       &dfault,
                       tofchar(key),
                       tofchar(" ") );
}




static void getrows( ident id,
                     char  *key,
                     int   code,
                     void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: MOSROWS= keyword handler.                         */
/*------------------------------------------------------------*/
{
   globaltype  *g = (globaltype*) arg;
   fint         nitems  = 1;
   fint         dfault  = HIDDEN;
   fint         r;
   fint         rows;


   r = userint_c( &rows,
                  &nitems,
                  &dfault,
                  tofchar( key ),
                  tofchar(" ") );

   if (r == 0)
   {
      rows = (int) sqrt( (double) g->slicecom.numslices );
      if (rows == 0)
         rows = 1;
         
      /* Fill the MOSROWS input field only */
      SuppressKeyevent( getrows, "MOSROWS=" );
      wkeyf( "MOSROWS=%d", rows );      
   }
   g->slicecom.rows = ABS( rows );
   g->slicecom.cols = (int) ceil( (double)g->slicecom.numslices/(double)g->slicecom.rows );
   anyoutf( DEBUG, "DEBUG (getrows): Mosaic row, columns = %d %d", 
            g->slicecom.rows, g->slicecom.cols );
}



static void calcoffsets( globaltype *g )
/*------------------------------------------------------------*/
/* PURPOSE: Calculate offsets from galaxy center.             */
/* Function is called after events GALCENTRE=  and SLICEX0Y0= */
/*------------------------------------------------------------*/
{
   int    i;
   
   for (i = 0; i < g->slicecom.numslices; i++) 
   {
      /*--------------------------------------------------*/
      /* Calculate the offsets from the slice centre to   */
      /* the (user given) centre of the set in user units.*/
      /* For the default galaxy center the value 0,0 is   */
      /* used.                                            */
      /*--------------------------------------------------*/
      if (g->slice[i].cpos[0] == g->inset.cpos[0] && g->slice[i].cpos[1] == g->inset.cpos[1])
      {
         g->slice[i].off[0] = g->slice[i].off[1] = 0.0;
      }
      else
      {
         double  longlat1[2], longlat2[2];
         double  lpos[2];
         fint    r;

         r = grtoph_c( g->inset.Name, &(g->inset.subsets[0]), 
                       g->inset.cpos, longlat1 );
         lpos[0] = g->slice[i].cpos[0];
         lpos[1] = g->inset.cpos[1];
         r = grtoph_c( g->inset.Name, &(g->inset.subsets[0]), lpos, longlat2 );
         g->slice[i].off[0] = distance( longlat1[0], longlat1[1],
                                        longlat2[0], longlat2[1] );
         g->slice[i].off[0] /= g->slicecom.Xunits2head;     /* From degree to user units */
         if (g->slice[i].cpos[0] - g->inset.cpos[0] < 0.0)
            g->slice[i].off[0] = -1.0 * fabs( g->slice[i].off[0] );

         lpos[0] = g->inset.cpos[0];
         lpos[1] = g->slice[i].cpos[1];
         r = grtoph_c( g->inset.Name, &(g->inset.subsets[0]), lpos, longlat2 );
         g->slice[i].off[1] = distance( longlat1[0], longlat1[1],
                                        longlat2[0], longlat2[1] );
         g->slice[i].off[1] /= g->slicecom.Xunits2head;     /* From degree to user units */
         if (g->slice[i].cpos[1] - g->inset.cpos[1] < 0.0)
            g->slice[i].off[1] = -1.0 * fabs( g->slice[i].off[1] ); 
      }              
      anyoutf( DEBUG, "DEBUG (calcoffsets): offset wrt gal. center=%g %g", 
               g->slice[i].off[0], g->slice[i].off[1] );
   }
}



static void getslicex0y0( ident id,
                          char  *key,
                          int   code,
                          void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: Keyword handler for SLICEX0Y0=.                   */
/* This routine sets reads for n slices (n given by the number*/
/* of slice angles in ANGLE=) the central positions. These    */
/* positions set the sample starting points for each slice.   */
/* The positions are stored in grids. The default position    */
/* is 0,0. If less than n positions are entered, the remain-  */
/* ing positions are copied from the last one.                */
/*------------------------------------------------------------*/
{
   globaltype  *g = (globaltype*) arg;
   fint         nitems;
   fint         dfault = HIDDEN;
   fint         r;
   int          i;
   double       inpslicex0y0[2*MAXSLICES];   /* Slice centers entered by the user */   


   if (!g->inset.exist)
   {
      error_status( "No set available yet", "Enter set/subsets first!", "" );
      return;
   }
   if (!g->slicecom.numslices)
   {
      error_status( "No angles!", "Please enter angles first", 
                    "Number of angles sets the number of slices." );
      return;
   }
   
   /* Check length of input. If 0 then use default */   
   {
      fchar   Dummytxt;
      char    dummytxt[STRLEN];
      fint    dfault = HIDDEN;
      fint    l;                                                            
      
      Dummytxt.a = dummytxt; Dummytxt.l = STRLEN-1; clearfchar(Dummytxt);
      l = usertext_c( Dummytxt, &dfault, tofchar(key), tofchar("") );
      if (l == 0)
      {
         /* Use the  default 0,0 and retry */
         int xlo = g->inset.blo[0];
         int ylo = g->inset.blo[1];
         int xhi = g->inset.bhi[0];
         int yhi = g->inset.bhi[1];
         int x0 = 0, y0 = 0;
         if (xlo > 0 || xhi < 0)  x0 = (xhi + xlo) / 2;
         if (ylo > 0 || yhi < 0)  y0 = (yhi + ylo) / 2;
         /* Only fill input field */
         SuppressKeyevent( getslicex0y0, "SLICEX0Y0=" );
         wkeyf( "SLICEX0Y0=%d %d", x0, y0 );  
      }
   } 

   /* Now the keyword is NOT empty */
   nitems = MYMIN( g->slicecom.numslices, MAXSLICES );
   r = gdspos_c( inpslicex0y0,
                 &nitems,
                 &dfault,
                 tofchar( key ),
                 tofchar(""),
                 g->inset.Name,
                 &(g->inset.subsets[0]) );
                 
   /* Function gdspos can return with r <= 0 if an error occurred */

   if (r <= 0)
   {
      fchar   Errtxt;
      char    errtxt[STRLEN];
      fint    dfault = -1;            /* Special default to get UCA message */
      fint    l;                                                            
      
      Errtxt.a = errtxt; Errtxt.l = STRLEN-1; clearfchar(Errtxt);
      l = usertext_c( Errtxt, &dfault, tofchar("UCAMESSAGE="), tofchar("") );
      Errtxt.a[l] = '\0';
      error_status( Errtxt.a,
                    "Check input slice centers.", "" );
      return;
   }      
      
                 
   for (i = 0; i < r; i++)
   {
      g->slice[i].cpos[0] = inpslicex0y0[i*2+0];
      g->slice[i].cpos[1] = inpslicex0y0[i*2+1];
   }
   /* Copy the last x,y position into the unspecified */      
   for (i = MYMAX(r,1); i < g->slicecom.numslices; i++)
   {
      g->slice[i].cpos[0] = g->slice[i-1].cpos[0];
      g->slice[i].cpos[1] = g->slice[i-1].cpos[1];      
   }
   for (i = 0; i < g->slicecom.numslices; i++) 
   {
      anyoutf( DEBUG, "DEBUG (getslicex0y0): Slice %d has center %g %g (grids)", 
               i, g->slice[i].cpos[0], g->slice[i].cpos[1] );
   }
   calcoffsets( g );
   clearstatus();
}



static void getsamples( ident  id,
                        char  *key,
                        int    code,
                        void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: SAMPLE= keyword handler.                          */
/* Get the sample distance for each slice.                    */
/*------------------------------------------------------------*/
{
   globaltype   *g=(globaltype*)arg;
   double       slicedelta;
   fint         nitems;
   fint         dfault = HIDDEN;
   fint         r;


   if (!g->slicecom.numslices)
   {
      error_status( "No angles!", "Please enter angles first", 
                    "Number of angles sets the number of slices." );
      return;
   }

   /* Check length of input. If 0 then use default */   
   {
      fchar   Dummytxt;
      char    dummytxt[STRLEN];
      fint    dfault = HIDDEN;
      fint    l;                                                            
      
      Dummytxt.a = dummytxt; Dummytxt.l = STRLEN-1; clearfchar(Dummytxt);
      l = usertext_c( Dummytxt, &dfault, tofchar(key), tofchar("") );
      if (l == 0)
      {
         /* Fill the input field only */
         SuppressKeyevent( getsamples, "SAMPLE=" );
         wkeyf("SAMPLE=1");       /* Set a default to 1 in any units */
      }
   }   
  
   nitems = 1;
   r = userdble_c( &slicedelta, 
                   &nitems, 
                   &dfault,
                   tofchar(key), 
                   tofchar("") );

   g->slicecom.delta = slicedelta;
 
   if (g->slicecom.delta == 0.0)
   {
      error_status( "Sample spacing 0 detected!",
                    "Sample spacing cannot be zero", "" );
      return;
   }
   anyoutf( DEBUG, "DEBUG (getsamples): Sample delta = %g", g->slicecom.delta );
   
   /*----------------------------------------*/
   /* If a slice plot exists and the sample  */
   /* spacing changed, then release the      */
   /* previous values for the label delta's  */
   /* and the sub ticks.                     */
   /*----------------------------------------*/
   if (g->slicecom.plotexist)
   {  
      wkeyf("LABDELTAX=");
      wkeyf("LABSUBX="  );   
      wkeyf("LABDELTAY=");
      wkeyf("LABSUBY="  );         
   }   
}



static void getangles( ident id,
                       char  *key,
                       int   code,
                       void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: Keyword handler for SKYANGLES=.                   */
/* This routine sets the number of slices and allocates       */
/* memory for the 'sliceplottype' structs. The number of      */
/* these structs is the number of angles that the user        */
/* enters here for SKYANGLES=.                                */
/*------------------------------------------------------------*/
{
   globaltype  *g = (globaltype*) arg;
   fint         nitems = MAXSLICES;
   fint         dfault = HIDDEN;
   fint         r;
   int          i;
   double       inpangles[MAXSLICES];        /* Angles as entered by the user */



   clearstatus();
   if (!g->inset.exist)
   {
      error_status( "No set available yet", "Enter set/subsets first!", "" );
      return;
   }
   
   if (g->slice != NULL) 
   {
      for (i = 0; i < g->slicecom.numslices; i++)
      {
         if (g->slice[i].posX != NULL) free( g->slice[i].posX );
         if (g->slice[i].posY != NULL) free( g->slice[i].posY );         
         if (g->slice[i].image != NULL) free( g->slice[i].image );
         if (g->slice[i].conimage != NULL) free( g->slice[i].conimage );
         if (g->slice[i].velpoints != NULL) free( g->slice[i].velpoints );
         if (g->slice[i].markerloX != NULL) free( g->slice[i].markerloX );
         if (g->slice[i].markerhiX != NULL) free( g->slice[i].markerhiX );
         if (g->slice[i].markerloY != NULL) free( g->slice[i].markerloY );
         if (g->slice[i].markerhiY != NULL) free( g->slice[i].markerhiY );
      }
      free( g->slice );      
   }
   
   g->slicecom.numslices = 0;
   r = userdble_c( inpangles,
                   &nitems,
                   &dfault,
                   tofchar(key),
                   tofchar(" ") );

   g->slice = (sliceplottype *) calloc( (int) r, sizeof(sliceplottype)  );
   if (g->slice == NULL)
   {
      error_status( "Cannot allocate enough memory!",
                    "No memory for all your slices",
                    "Perhaps you should restart with less slice angles" );
      return;
   }
   g->slicecom.numslices = (int) r;
   for (i = 0; i < g->slicecom.numslices; i++)
   {
      g->slice[i].angle = inpangles[i];
      g->slice[i].posX = NULL;
      g->slice[i].posY = NULL;
      g->slice[i].image = NULL;
      g->slice[i].conimage = NULL;
      g->slice[i].velpoints = NULL;
      g->slice[i].markerloX = NULL;
      g->slice[i].markerhiX = NULL;
      g->slice[i].markerloY = NULL;
      g->slice[i].markerhiY = NULL;                  
      anyoutf( DEBUG, "DEBUG (getangles): Angle %d = %g", i, g->slice[i].angle );
   }
     
   if (!TriggerKey("MOSROWS="));
      wkeyf( "MOSROWS=" );

   g->slicecom.plotexist = NO;        /* Reset to avoid plotting markers */
   if (!TriggerKey("SLICEX0Y0="))
      wkeyf( "SLICEX0Y0=" );   
   if (!TriggerKey("SAMPLE="))        /* Use default 1 in whatever units */
      wkeyf( "SAMPLE=1" );   
        
   TriggerKey("EXTRACT=");
   /*   getTRradii( NULL, "RADII=", KEYCHANGE, g ); */   /* Pseudo event */
/*   wkeyf("EXTRACT=Y");   */
}



static void convertangles( ident id,
                           char  *key,
                           int   code,
                           void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: Keyword handler for GALANGLES=.                   */
/*------------------------------------------------------------*/
{   
   globaltype  *g = (globaltype*) arg;
   fint         nitems;
   fint         dfault;
   fint         r;
   double       inc, phi, cosinc;
   double       inpangles[MAXSLICES];        /* Angles as entered by the user */
   int          i;
   char         mes[1024];
      
   
   if (UserLog("GALMODE="))
   {
      nitems = MAXSLICES;
      dfault = HIDDEN;
      r = userdble_c( &inc, &nitems, &dfault, tofchar("FIXINC="), tofchar("") );
      r = userdble_c( &phi, &nitems, &dfault, tofchar("FIXPHI="), tofchar("") );
      if (inc == 90.0)
      {
         cosinc = FLT_EPSILON;
      }
      else
      {      
         inc = RAD( inc );
         cosinc = cos( inc );
      }
      phi = RAD( phi );
      r = userdble_c( inpangles, &nitems, &dfault, tofchar("GALANGLES="),
                      tofchar("") );
      if (r == 0)
      {
         return;
      }
      else    /* Start converting angles */
      {
         strcpy( mes, "" );
         for (i = 0; i < (int) r; i++)
         {
            double angle = RAD(inpangles[i]);
            double beta;            
            char   num[512];
            
            beta  = atantan( angle, 1.0/cosinc );            
            angle = beta + phi;
            angle = DEG( angle );
            sprintf( num, "%g ", angle );
            strcat( mes, num );
         } 
         wkeyf( "SKYANGLES=%s", mes );
      }
   }
}




static void getclip(  ident id,
                      char  *key,
                      int   code,
                      void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: Keyword handler for CLIP=                         */
/* This range of image values gets the full range of available*/
/* colours.                                                   */
/*------------------------------------------------------------*/
{  
   settype *set = (settype*) arg;
   fint         nitems = 2;
   fint         dfault = HIDDEN;
   fint         r;
   float        clip[2];
   
  
   r = userreal_c( clip,
                   &nitems,
                   &dfault,
                   tofchar(key),
                   tofchar(" ") ); 

   if (r == 0)
      getcliplevels( set );                       /* Set clip only for INSET= */
   if (r > 0)
      set->clip[0] = clip[0];
   if (r > 1)
      set->clip[1] = clip[1];
}



fint getcword( fchar  Setin,
               fint   profaxnum )
/*------------------------------------------------------------*/
/* PURPOSE: Find the coordinate word that corresponds to the  */
/*          profile axis.                                     */
/*                                                            */
/* The grids of all other axes are supposed to be 0.          */
/* This coordinate words enables the transformation between   */
/* grids and physical coordinates for the profile axis        */
/* (usually the velocity axis).                               */
/*------------------------------------------------------------*/
{
   int   i;
   fint  cw = 0;
   fint  setdim;


   setdim  = gdsc_ndims_c( Setin, &setlevel );
   for (i = 0; i < setdim; i++)
   {
      fint  igrid = 0;
      fint  axnr = (fint) i + 1;

      if (axnr != profaxnum)
      {
         fint  r1 = 0;
         cw = gdsc_word_c( Setin, &axnr, &igrid, &cw, &r1 );
      }
   }
   return( cw );
}




static int getheaderVunits( globaltype * g )
/*------------------------------------------------------------*/
/* PURPOSE: Read the (velocity) header units from the header  */
/*          and check if they are compatible with velocity.   */
/*------------------------------------------------------------*/ 
{
   char    cbuf[128];
   fint    r;

   r = axunit_c( g->inset.Name, &(g->slicecom.Vaxnum), g->slicecom.HeaderVunits ); /* Returns secondary axis units */
   g->slicecom.HeaderVunits.a[nelc_c(g->slicecom.HeaderVunits)] = '\0';
   if (r != 0)
   {
       anyoutf( DEBUG, "WARNING (getheaderVunits): The V axis has no units." );
       return( NO );
   }
   else
   /*--------------------------------------------------*/
   /* Note that 'HeaderVunits' above are usually the   */
   /* secondary units while 'Cunit' below are primary  */
   /* units.                                           */
   /*--------------------------------------------------*/
   {
      fint   skysys, prosys, velsys;
      fchar  Ctype, Cunit, Dunit;
      fmake( Ctype, FITSLEN );
      fmake( Cunit, FITSLEN );
      fmake( Dunit, FITSLEN );

      sprintf( cbuf, "CTYPE%d", g->slicecom.Vaxnum );
      r = 0;
      gdsd_rchar_c( g->inset.Name, tofchar(cbuf), &setlevel, Ctype, &r );
      r = axtype_c( Ctype, Cunit, Dunit, &skysys, &prosys, &velsys );
      if (r == FREQAXIS || r == VELAXIS)
      {
         anyoutf( DEBUG, "DEBUG (getheaderVunits): Axis type frequency or velocity" );
         return( YES );
      }
   }
   anyoutf( DEBUG, "DEBUG (getheaderVunits): Axis type NOT frequency or velocity" ); 
   return( NO );
}



static void getTRXunits( ident  id,
                         char  *key,
                         int    code,
                         void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: TRVUNITS= keyword handler.                        */
/* These are the units for the tilted ring velocities.        */
/*------------------------------------------------------------*/
{
   globaltype   *g=(globaltype*)arg; 
   fint         nitems = 1;
   fint         dfault = HIDDEN;
   fint         r;
   int          j;
   

   r = userchar_c( g->parcommon.TRXUnits, 
                   &nitems, 
                   &dfault,
                   tofchar(key), 
                   tofchar("") ); 

   /* Check whether the units are GRIDS or something else */
   if (r != 0)
   {
      int i;
      int len = nelc_c(g->parcommon.TRXUnits);

      g->parcommon.TRXUnits.a[len] = '\0';                       /* Close string */
      for (i = 0; i < len; i++)
      {
         g->parcommon.TRXunits[i] = toupper( g->parcommon.TRXunits[i] );
      }
   }
   
   for (j = 0; j < 3; j++)
   {
      if (g->parplot[j].exist)        /* Replot if plot exists */
         plotTRdata( g, j );                    
   }
}




static void getTRVunits( ident  id,
                         char  *key,
                         int    code,
                         void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: TRVUNITS= keyword handler.                        */
/* These are the units for the tilted ring velocities.        */
/*------------------------------------------------------------*/
{
   globaltype   *g=(globaltype*)arg; 
   fint         nitems = 1;
   fint         dfault = HIDDEN;
   fint         r;


   r = userchar_c( g->parcommon.TRVUnits, 
                   &nitems, 
                   &dfault,
                   tofchar(key), 
                   tofchar("") ); 

   /* Check whether the units are GRIDS or something else */
   if (r != 0)
   {
      int i;
      int len = nelc_c(g->parcommon.TRVUnits);

      g->parcommon.TRVUnits.a[len] = '\0';                       /* Close string */
      for (i = 0; i < len; i++)
      {
         g->parcommon.TRVunits[i] = toupper( g->parcommon.TRVunits[i] );
      }
   }
   
  
   if (g->parplot[0].exist)        /* Replot if plot exists */
      plotTRdata( g, 0 );
}




static void getVunits( ident  id,
                       char  *key,
                       int    code,
                       void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: VUNITS= keyword handler.                          */
/* This routine is either triggered by the user with the      */
/* VUNITS= keyword, or it is triggered after a call to the    */
/* 'getset' (with keyword INSET=) routine. However then the   */
/* VUNITS= keyword must be empty.                             */ 
/*------------------------------------------------------------*/
{
   globaltype   *g=(globaltype*)arg; 
   fint         nitems = 1;
   fint         dfault = HIDDEN;
   fint         r;


   r = userchar_c( g->slicecom.VUnits, 
                   &nitems, 
                   &dfault,
                   tofchar(key), 
                   tofchar("") ); 

   /* Check whether the units are GRIDS or something else */
   if (r != 0)
   {
      int i;
      int len = nelc_c(g->slicecom.VUnits);

      g->slicecom.VUnits.a[len] = '\0';                       /* Close string */
      for (i = 0; i < len; i++)
      {
         g->slicecom.Vunits[i] = toupper( g->slicecom.Vunits[i] );
      }
      if ( strncmp(g->slicecom.Vunits, "GRIDS", 5) == 0 )
      {
          g->slicecom.Vingrids = YES;                   
      }
   }
   
   if (!g->slicecom.Vingrids) 
   {
      if (!g->inset.exist)
      {
         error_status( "No set available yet", "Enter set/subsets first!", "" );
         rejectf( "VUNITS=", "No set available yet!" );
         return;
      }
      else if ( !getheaderVunits(g) )
      {
         /* No other choice than grids */
         error_status( "No suitable units in header", "", "" );
         wkeyf( "VUNITS=GRIDS" );
         g->slicecom.Vingrids = YES;
         return;
      }  
   }
   
   if (r == 0 && !g->slicecom.Vingrids)
   {
      /* Nothing entered, but we can give a default because we know */
      /* the header units of the profile axis */
      strcpy( g->slicecom.Vunits, g->slicecom.headerVunits ); 
      wkeyf( "VUNITS=%s", g->slicecom.headerVunits );      
   }                                   

   if (!g->slicecom.Vingrids) 
   {
      fint   r1, r2;
      
      r1 = grtoph_c( g->inset.Name,
                     &(g->slicecom.Vcword),
                     &(g->slicecom.Vmingr),
                     &(g->slicecom.Vminph)  );

      r2 = grtoph_c( g->inset.Name,
                     &(g->slicecom.Vcword),
                     &(g->slicecom.Vmaxgr),
                     &(g->slicecom.Vmaxph)  );
      if (r1 || r2)
      {
         anyoutf( DEBUG, "WARNING (getVunits): Could not transform to physical coordinates!" );
         /* No other choice than grids */
         wkeyf( "VUNITS=GRIDS" );
         return; 
      }
      
      r = factor_c( g->slicecom.VUnits,
                    g->slicecom.HeaderVunits,
                    &(g->slicecom.Vunits2head) );
      if (r != 0)
      {
         error_status( "Units not compatible!", "", ""  );
         rejectf( "VUNITS=", "Units not compatible!" );
         g->slicecom.Vunits2head = 0.0;
         wkeyf( "VUNITS=GRIDS" );
         return;
      }
      else
      {
         anyoutf( DEBUG, "DEBUG (getVunits): Factor from %s to %s is %g", 
                  g->slicecom.Vunits, g->slicecom.headerVunits, g->slicecom.Vunits2head );
         anyoutf( DEBUG, "DEBUG (getVunits): Physical coordinates V axis from %g to %g (%s)",
                  g->slicecom.Vminph/g->slicecom.Vunits2head, 
                  g->slicecom.Vmaxph/g->slicecom.Vunits2head,
                  g->slicecom.Vunits );     
      }
   }
   else   /* V in grids */
   {
      g->slicecom.Vunits2head = 1.0;
      g->slicecom.Vmaxph = g->slicecom.Vmaxgr;
      g->slicecom.Vminph = g->slicecom.Vmingr;      
      error_status( "NOTE:", "No transformation to physical coordinates!",
                    "Program assumes from now on all V units are grids" );
   }
   if (emptykey("TRVUNITS="))
      wkeyf("TRVUNITS=%s", g->slicecom.Vunits );
   

   /* Units changed and plot exist, then release defaults */
   if (g->slicecom.plotexist) 
   {
      wkeyf("LABSTARTY=");
      wkeyf("LABDELTAY=");
      wkeyf("LABSUBY="  );
      wkeyf("TITLEY=Velocity (%s)", g->slicecom.Vunits );      
   }
   else
   {
      if (emptykey("TITLEY="))
         wkeyf("TITLEY=Velocity (%s)", g->slicecom.Vunits );
   }
}



static int getgridspacings( settype   *set )
/*------------------------------------------------------------*/
/* PURPOSE: Return the grid spacings of the subset axes.      */
/* Routine is called by 'getset'.                             */
/*------------------------------------------------------------*/
{
   fint    subdim;
   fint    r1;
   int     n;
   char    cbuf[FITSLEN+1];


   subdim = gdsc_ndims_c( set->Name, &(set->subsets[0]) );
   for (n = 0; n < subdim; n++)
   {
      sprintf( cbuf, "CDELT%d", set->axnum[n] );
      r1 = 0;
      gdsd_rdble_c( set->Name, tofchar(cbuf), &setlevel, &set->cdelt[n], &r1 );
      if (r1 < 0)
      {
         error_status( "NOTE:", "Cannot obtain grid spacings. Zero substituted!", "" );
         anyoutf( DEBUG, "DEBUG (getgridspacings): Cannot obtain grid spacings. Zero substituted!" );
         for (n = 0; n < subdim; n++)
            set->cdelt[n] = 0.0;
         return( NO );
      }
      anyoutf( DEBUG, "DEBUG (getgridspacings): Grid spacing: %f", set->cdelt[n] );
   }
   return( YES );
}




static void getprofileproperties( globaltype *g )
/*------------------------------------------------------------*/
/* PURPOSE: Return properties of the profile axis.            */
/*                                                            */
/* Routine is called after an INSET=.                         */
/* Return the real axis number of the profile axis, the       */
/* length of this axis as given by the user, the first and    */
/* last grid values of the profile axis (first grid can be    */
/* greater than last) and the coordinate word that corresponds*/
/* to the profile axis taken at a position where the other    */
/* grids are 0.                                               */
/*------------------------------------------------------------*/
{
   fint     firstsubset;
   fint     lastsubset;
   fint     axnum, len, cword;
   fint     r1;
   double   mingrid, maxgrid;


   axnum = g->inset.axnum[ g->inset.setdim-1 ];
   cword = getcword( g->inset.Name, axnum );
   len   = g->inset.axcount[g->inset.setdim-1];
   firstsubset = g->inset.subsets[0];
   lastsubset  = g->inset.subsets[g->inset.nsubs-1];
   r1 = 0;
   mingrid = gdsc_grid_c( g->inset.Name, &axnum, &firstsubset, &r1 );
   if (r1 < 0)
   {
      error_status( "NOTE:", "Cannot find first grid spacing of this set! 0.0 substituded.", "" );
      anyoutf( DEBUG, "DEBUG (getprofileproperties): Cannot find first grid spacing of this set! 0.0 substituded." );
      g->slicecom.Vingrids = YES;
   }
   r1 = 0;
   maxgrid = gdsc_grid_c( g->inset.Name, &axnum, &lastsubset, &r1 );
   if (r1 < 0)
   {
      error_status( "NOTE:", "Cannot find 2nd grid spacing of this set! 0.0 substituded.", "" );
      anyoutf( DEBUG, "DEBUG (getprofileproperties): Cannot find 2nd grid spacing of this set! 0.0 substituded." );      
      g->slicecom.Vingrids = YES;
   }

   g->slicecom.Vaxnum   = axnum;
   g->slicecom.Vcword   = cword;
   g->slicecom.Vlen     = g->inset.nsubs;
   g->slicecom.Vmingr   = mingrid;
   g->slicecom.Vmaxgr   = maxgrid;
   g->slicecom.Vingrids = NO;


   anyoutf( DEBUG, "DEBUG (getprofileproperties): nsubs total=%d, profaxnum=%d, profcword=%d proflen=%d",
            g->inset.nsubs, g->slicecom.Vaxnum, g->slicecom.Vcword, g->slicecom.Vlen );
   anyoutf( DEBUG, "DEBUG (getprofileproperties): grid range in V: from %g to %g",
            g->slicecom.Vmingr, g->slicecom.Vmaxgr );

   /* If VUNITS= is empty and if we have to work in grids -> VUNITS=GRIDS */
   {
      fint         nitems = 1;
      fint         dfault = HIDDEN;
      fint         r;

      r = userchar_c( g->slicecom.VUnits, 
                      &nitems, 
                      &dfault,
                      tofchar("VUNITS="), 
                      tofchar("") ); 

      if (r == 0)
      {
         if (g->slicecom.Vingrids)
            wkeyf( "VUNITS=GRIDS" );
         else
            wkeyf( "VUNITS=" );            
      }
      else  wkeyf( "VUNITS=" );
   }
   g->slicecom.Xingrids = !getgridspacings( &(g->inset) );
   anyoutf( DEBUG, "DEBUG (getprofileproperties): cdelt0,1=%g %g", g->inset.cdelt[0], g->inset.cdelt[1] );   
   /* If XUNITS= is empty and if we have to work in grids -> XUNITS=GRIDS */
   {
      fint         nitems = 1;
      fint         dfault = HIDDEN;
      fint         r;

      r = userchar_c( g->slicecom.XUnits, 
                      &nitems, 
                      &dfault,
                      tofchar("XUNITS="), 
                      tofchar("") ); 

      if (r == 0)
      {
         if (g->slicecom.Xingrids)
            wkeyf( "XUNITS=GRIDS" );
         else
            wkeyf( "XUNITS=" );
      }
      else  wkeyf( "XUNITS=" );
   }   
   /*----------------------------------------*/
   /* At this moment we can set a default    */
   /* subset (in grids) for the slice        */
   /* overlay in GIDS.                       */
   /*----------------------------------------*/   
   if (!TriggerKey("OVERSUB=")) 
   {
      wkeyf( "OVERSUB=%d", (int)((g->slicecom.Vmingr+g->slicecom.Vmaxgr)/2.0) );
   }
}




static void properties( ident id,
                        char  *key,
                        int   code,
                        void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: Keyword handler for PROPERTIES=                   */
/*------------------------------------------------------------*/
{
   globaltype *g=(globaltype*)arg;  
   
   getprofileproperties( g );
}




static int getheaderXunits( globaltype * g )
/*------------------------------------------------------------*/
/* PURPOSE: Read the (spatial) header units from the header   */
/*          and check if they are compatible with DEGREE      */
/*------------------------------------------------------------*/ 
{
   fint   r;
   
   r = skyrot_c( g->inset.Name, &(g->inset.maprotation) );
   if (r != 0)
      return( NO );
   else
   { 
      int     i;
      double  fact[2];
      
      for (i = 0; i < 2; i++)
      {
         char      cbuf[FITSLEN+1];
       
         clearfchar( g->slicecom.HeaderXunits );      
         sprintf( cbuf, "CUNIT%d", g->inset.axnum[i] );
         r = 0;
         gdsd_rchar_c( g->inset.Name, tofchar(cbuf), &setlevel, g->slicecom.HeaderXunits, &r );
         g->slicecom.HeaderXunits.a[ nelc_c(g->slicecom.HeaderXunits) ] = '\0';
         anyoutf( DEBUG, "DEBUG (getheaderXunits): Subset units in header: [%s]", g->slicecom.HeaderXunits.a );
         r = factor_c( g->slicecom.HeaderXunits, tofchar("DEGREE"), &fact[i] );
         if (r != 0)              
         {
            anyoutf( DEBUG, "DEBUG (getheaderXunits): Spatial header units Not compatible with degrees!" );
            return( NO );
         }
      }
      if (fact[0] != fact[1])
      {
         anyoutf( DEBUG, "DEBUG (getheaderXunits): Unequal units in header for spatial axes!" ); 
         return( NO );         
      }
   }      
   return( YES );
}




static void getXunits( ident  id,
                       char  *key,
                       int    code,
                       void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: XUNITS= keyword handler.                          */
/* Get user units for the X-axis.                             */
/*------------------------------------------------------------*/
{
   globaltype *g=(globaltype*)arg; 
   fint       r;
   fint       dfault = HIDDEN;
   fint       nitems = 1;


   r = userchar_c( g->slicecom.XUnits, 
                   &nitems, 
                   &dfault,
                   tofchar(key), 
                   tofchar("") ); 

   /* Check whether the units are GRIDS or something else */
   if (r != 0)
   {
      int i;
      int len = nelc_c(g->slicecom.XUnits);

      g->slicecom.XUnits.a[len] = '\0';                       /* Close string */
      for (i = 0; i < len; i++)
      {
         g->slicecom.Xunits[i] = toupper( g->slicecom.Xunits[i] );
      }
      if ( strncmp(g->slicecom.Xunits, "GRIDS", 5) == 0 )
      {
          g->slicecom.Xingrids = YES;                   
      }
   }


   if (!g->slicecom.Xingrids) 
   {
      if (!g->inset.exist)
      {
         error_status( "No set available yet", "Enter set/subsets first!", "" );
         rejectf( "XUNITS=", "No set available yet!" );
         return;
      }
      else if ( !getheaderXunits(g) )
      {
         /* No other choice than grids */
         error_status( "No suitable units in set header!", 
                       "Subset not spatial or unequal header units", "" );
         wkeyf( "XUNITS=GRIDS" );
         return;
      }  
   }


   if (r == 0 && !g->slicecom.Xingrids)
   {
      /* Nothing entered, but we can give a default because we know */
      /* the header units of the spatial axes is compatible with    */
      /* degrees, so a nice default is seconds of arc.              */
      
      wkeyf( "XUNITS=ARCSEC" );
      strcpy( g->slicecom.Xunits, "ARCSEC" );
   }                                   


   if (!g->slicecom.Xingrids) 
   {
      fint   r1;
      double gr[2], ph[2];
      
      gr[0] = gr[1] = 0.0;
      r1 = grtoph_c( g->inset.Name,
                     &(g->inset.subsets[0]),
                     gr, ph );
      if (r1)
      {
         anyoutf( DEBUG, "WARNING (getXunits): Could not transform to spatial coordinates!" );
         /* No other choice than grids */
         wkeyf( "XUNITS=GRIDS" );
         return; 
      }
      
      r = factor_c( g->slicecom.XUnits,
                    g->slicecom.HeaderXunits,
                    &(g->slicecom.Xunits2head) );
      if (r != 0)
      {
         error_status( "Units not compatible!", 
                       "Units cannot be converted to degree", ""  );
         rejectf( "XUNITS=", "Units not compatible!" );
         g->slicecom.Xunits2head = 0.0;
         wkeyf( "XUNITS=GRIDS" );
         return;
      }
      else
      {
         anyoutf( DEBUG, "DEBUG (getXunits): Factor from %s to %s is %g", 
                  g->slicecom.Xunits, 
                  g->slicecom.headerXunits, 
                  g->slicecom.Xunits2head );
      }
   }
   else 
   {
      g->slicecom.Xunits2head = 0.0;
      error_status( "NOTE:", "No transformation to spatial physical coordinates!",
                    "Program assumes from now on all X units are grids" );
   }
   if (emptykey("TITLEX="))
      wkeyf("TITLEX=Offset (%s)", g->slicecom.Xunits );
   if (emptykey("TRXUNITS="))
      wkeyf("TRXUNITS=%s", g->slicecom.Xunits );
   
   /*----------------------------------------*/
   /* If units are changed and a plot exists,*/
   /* then probably we have to recalculate   */
   /* and plot again.                        */
   /*----------------------------------------*/
   if (g->slicecom.plotexist)  /* Then things are changed! */
   {  
      wkeyf("LABSTARTX=");
      wkeyf("LABDELTAX=");
      wkeyf("LABSUBX="  );   
      wkeyf("TITLEX=Offset (%s)", g->slicecom.Xunits );      
   }
}



 
static void getcontours( ident id,
                         char  *key,
                         int   code,
                         void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: CONTOURS=  keyword handler.                       */
/*------------------------------------------------------------*/
{
   globaltype *g=(globaltype*)arg;
   fint        dfault = HIDDEN;   
   fint        nitems = MAXCONTOURS;
   fint        r;
   

   r = userreal_c( g->slicecom.contours,
                   &nitems,
                   &dfault,
                   tofchar(key),
                   tofchar(" ") ); 
   g->slicecom.numcontours = r;
}




static void getconcol( ident id,
                       char  *key,
                       int   code,
                       void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: CONTOURS=  keyword handler.                       */
/*------------------------------------------------------------*/
{
   globaltype *g=(globaltype*)arg;
   fint        dfault = HIDDEN;   
   fint        nitems = 1;
   fint        r;
   fint        col = 1;
   

   r = userint_c( &col,
                  &nitems,
                  &dfault,
                  tofchar(key),
                  tofchar(" ") ); 

   col = MYMIN( col, 15 );
   col = MYMAX( col, 0 );
   g->slicecom.concol = col;
}




static void getbox( ident id,
                    char  *key,
                    int   code,
                    void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: BOX= Process user input for a box.                */
/*------------------------------------------------------------*/
{
   fint        boxopt = 0;
   fint        dfault = HIDDEN;
   fint        showdev = DEBUG;
   fint        r;
   char        dummytxt[STRLEN+1];
   fchar       Dummytxt;
   globaltype *g=(globaltype*)arg;


   if (!g->inset.exist)
   {
      rejectf( "BOX=", "You cannot define a box without a set!" );
      g->boxexist = NO;
      return;                     /* Nothing changed so return old image data */
   }

   /* Check on empty strings */
   Dummytxt.l = STRLEN;
   Dummytxt.a = dummytxt;
   r = usertext_c( Dummytxt, &dfault, tofchar(key), tofchar(" ") );
   if (!r)
   {
      char   mes1[128];
      char   mes2[128];
      int    i;

      /* Prepare the default for 'gdsbox'. With option 16 */
      /* it does not accept an empty string as input.     */

      strcpy( mes1, "BOX=" );
      for (i = 0; i < g->inset.subdim; i++)
      {
         sprintf( mes2, " %d", g->inset.flo[i] );
         strcat( mes1, mes2 );
      }
      for (i = 0; i < g->inset.subdim; i++)
      {
         sprintf( mes2, " %d", g->inset.fhi[i] );
         strcat( mes1, mes2 );
      }
      wkeyf( mes1 );
      return;
   }

   dfault = REQUEST;
   boxopt = 16;
   gdsbox_c( g->inset.blo, g->inset.bhi,
             g->inset.Name, g->inset.subsets,
             &dfault,
             tofchar(key),
             tofchar(" "),
             &showdev,
             &boxopt );

   if (boxopt == -1)
   {
      /* 'gdsbox' detected an error */
      fchar   Errtxt;
      char    errtxt[STRLEN];
      fint    l;
      fint    dfault = -1;

      Errtxt.a = errtxt; Errtxt.l = STRLEN-1; clearfchar(Errtxt);
      cancel( "UCAMESSAGE=" );
      l = usertext_c( Errtxt, &dfault, tofchar("UCAMESSAGE="), tofchar("") );
      Errtxt.a[l] = '\0';

      error_status( Errtxt.a, "Enter another box.", "" );

      g->boxexist = NO;
      return;
   }
   g->boxexist = YES;
   anyoutf( DEBUG, "DEBUG (getbox): BOX=%d %d %d %d", g->inset.blo[0], g->inset.blo[1],
            g->inset.bhi[0], g->inset.bhi[1] );

   if (!TriggerKey("GALCENTRE="))
   {
      if (0 > g->inset.blo[0] && 0 < g->inset.bhi[0] && 
          0 > g->inset.blo[1] && 0 < g->inset.bhi[1] )
      {
         wkeyf( "GALCENTRE=0 0" );   
      }
      else
      {
         wkeyf( "GALCENTRE=AC" );
      }
   }               
   clearstatus();
}




static void getgalcentre( ident  id,
                          char  *key,
                          int    code,
                          void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: INSET=, CONSET= & VELSET=  keyword handler.       */
/*------------------------------------------------------------*/
{
   globaltype *g=(globaltype*)arg;
   fint        nitems  = 1;
   fint        dfault  = HIDDEN;
   fint        r;
   
  
   g->inset.cpos[0] = g->inset.cpos[1] = 0.0;
   r = gdspos_c( g->inset.cpos,
                 &nitems,
                 &dfault,
                 tofchar(key),
                 tofchar(""),
                 g->inset.Name,
                 &(g->inset.subsets[0]) );
   
   if (g->slicecom.numslices > 0)
      calcoffsets( g );
}





static void getset( ident  id,
                    char  *key,
                    int    code,
                    void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: INSET=, CONSET= & VELSET=  keyword handler.       */
/*------------------------------------------------------------*/
{
   globaltype *g=(globaltype*)arg;     
   settype    *set = NULL;
   fint       maxaxes = MAXAXES;       /* Max num. of axes the program can deal with.*/
   fint       maxsubs = MAXSUBSETS;
   fint       class   = 1;             /* Class 1 is for applications which repeat */
   fint       showdev = DEBUG;
   fint       r1, r2;
   fint       dfault = HIDDEN+NOERRORLOOP;  /* Return immediately when error occurs */


   clearstatus();
   
   if (strcmp(key, "INSET=") == 0) 
      set = &(g->inset);
   if (strcmp(key, "CONSET=") == 0) 
      set = &(g->contourset);
   if (strcmp(key, "VELSET=") == 0) 
      set = &(g->velocityset);
   
   set->Name.a = set->buf;
   set->Name.l = FILENAMELEN;
   clearfchar( set->Name );
   set->subdim = 2;                  /* Require 2-dim subsets */
   set->nsubs = gdsinp_c(
                     set->Name,      /* Name of input set. */
                     set->subsets,   /* Array containing subsets coordinate words. */
                     &maxsubs,       /* Maximum number of subsets in 'subin'.*/
                     &dfault,        /* Default code as is USERxxx. */
                     tofchar(key),   /* Keyword prompt. */
                     tofchar(""),    /* Keyword message for the user. */
                     &showdev,       /* Device number (as in ANYOUT). */
                     set->axnum,     /* Array of size 'maxaxes' containing the axes numbers. */
                                     /* The first elements (upto the dimension of the subset) */
                                     /* contain the axes numbers of the subset, */
                                     /* the other ones contain the axes numbers */
                                     /* outside the subset ordered according to the */
                                     /* specification by the user. */
                     set->axcount,   /* Number of grids on axes in 'axnum' */
                     &maxaxes,       /* Max. number of axes. */
                                     /* the operation for each subset. */
                     &class,         /* Class 1 is for applications which repeat */
                     &set->subdim ); /* Dimensionality of the subsets for class 1 */


   if (set->nsubs <= 0)
   {
      fchar   Errtxt;
      char    errtxt[STRLEN];
      fint    dfault = -1;            /* Special default to get UCA message */
      fint    l;                                                            

      Errtxt.a = errtxt; Errtxt.l = STRLEN-1; clearfchar(Errtxt);
      l = usertext_c( Errtxt, &dfault, tofchar("UCAMESSAGE="), tofchar("") );
      Errtxt.a[l] = '\0';
      error_status( Errtxt.a,
                   "Check set name and existence on disk", "" );         
      set->exist = NO;
      return;
   }
   g->inset.Name.a[nelc_c(g->inset.Name)] = '\0'; 

   if (strcmp(key, "VELSET=") != 0)
   { 
      if (set->nsubs == 1)
      {
         error_status( "Not enough subsets", 
                       "You have to enter at least 2 subsets!", "" );
         set->exist = NO;
         return;
      }
   }

   set->setdim = gdsc_ndims_c( set->Name, &setlevel );


   /*-------------------------------*/
   /* Determine edges of this frame */
   /*-------------------------------*/
   {
      fint cwlo, cwhi;                          /* Local coordinate words */
      int  m;
      r1 = 0;
      gdsc_range_c( set->Name, &setlevel, &cwlo, &cwhi, &r1 );
      r1 = r2 = 0;
      for (m = 0; m < set->setdim; m++)
      {
         set->flo[m] = gdsc_grid_c( set->Name, &set->axnum[m], &cwlo, &r1 );
         set->fhi[m] = gdsc_grid_c( set->Name, &set->axnum[m], &cwhi, &r2 );
      }
   }
   if (strcmp(key, "INSET=") == 0)   
   {
      char mes[80];
      sprintf( mes, "Set ok. You entered %d subsets", set->nsubs );
      clearstatus();
      gui_status( mes, FIRSTLINE );
      sprintf( mes, "The V axis in your XV diagram(s) is %d pixels long",
               set->nsubs );
      gui_status( mes, SECONDLINE );
      getcliplevels( set );                       /* Set clip only for INSET= */
   }
   set->exist = YES;

   
   /*-------------------------------*/
   /* Get the units of the ampli-   */
   /* tudes for annotations. Put    */
   /* units also in gui after clip  */
   /* input field.                  */
   /*-------------------------------*/
   {
      fint    r = 0;
      fchar   Dataunits;

      Dataunits.a = set->dataunits;
      Dataunits.l = FITSLEN;
      gdsd_rchar_c( set->Name, tofchar("BUNIT"), &setlevel, Dataunits, &r );
      Dataunits.a[nelc_c(Dataunits)]  = '\0';
      if (r < 0)
         strcpy( set->dataunits, "??" );
      wkeyf("MAPUNITS=%s", set->dataunits );
      gui_units( set->dataunits ); 
   } 

   if (strcmp(key, "INSET=") == 0)
   {      
      properties( NULL, "PROPERTIES=", KEYCHANGE, g );      
      if (!TriggerKey("BOX="))
         wkeyf( "BOX=%d %d %d %d", 
                 set->flo[0], set->flo[1],
                 set->fhi[0], set->fhi[1] );
      getbox( NULL, "BOX=", KEYCHANGE, g ); 
   }    
}



static int getminmaxX( slicecommontype    *slicecom,
                       sliceplottype      *slice )
/*------------------------------------------------------------*/
/* PURPOSE: Get min., max. values of axis limits in x and y.  */
/* Input are the user defined slice characteristics of ALL    */
/* slices. Output is in physical coordinates where possible.  */
/*------------------------------------------------------------*/
{
   int       i;

  
   for (i = 0; i < slicecom->numslices; i++)
   {
      if (i == 0)
      {
         slicecom->xloall = slice[i].xlo;
         slicecom->xhiall = slice[i].xhi;
         slicecom->ndatmax = slice[i].ndat;
         slicecom->xloallgr = slice[i].xlogr;
         slicecom->xhiallgr = slice[i].xhigr;
      }
      else
      {
         double   xlo = slice[i].xlo;
         double   xhi = slice[i].xhi;
         if (xlo < slicecom->xloall)
            slicecom->xloall = xlo;
         if (xhi > slicecom->xhiall)
            slicecom->xhiall = xhi;
         if (slice[i].ndat > slicecom->ndatmax)
            slicecom->ndatmax = slice[i].ndat;
         if (slice[i].xlogr < slicecom->xloallgr)
            slicecom->xloallgr = slice[i].xlogr;
         if (slice[i].xhigr > slicecom->xhiallgr)
            slicecom->xhiallgr = slice[i].xhigr;            
      }
   }
   if (slicecom->xloall ==  slicecom->xhiall)
   {
      slicecom->xloall -= slicecom->delta;
      slicecom->xhiall += slicecom->delta;
   }
   if (slicecom->xloallgr ==  slicecom->xhiallgr)
   {
      slicecom->xloallgr -= 0.5;
      slicecom->xhiallgr += 0.5;
   }   
   anyoutf( DEBUG, "DEBUG (getminmaxX): x min, max grids = %d %d", slicecom->xloallgr, slicecom->xhiallgr );
   anyoutf( DEBUG, "DEBUG (getminmaxX): x min, max physical = %g %g", slicecom->xloall, slicecom->xhiall );
   anyoutf( DEBUG, "DEBUG (getminmaxX): Max. # sample points= %d", slicecom->ndatmax );
   return( YES );
}




static int getxvmap( sliceplottype  *slice,           /* Is an array of slices */
                     int             numslices,
                     int             xloallgr,
                     int             xhiallgr,
                     settype         inset,
                     settype         contourset,
                     settype         velocityset,
                     float         **image )
/*------------------------------------------------------------*/
/* PURPOSE: Read data values from the input set at previous   */
/*          calculated coordinates.                           */
/*------------------------------------------------------------*/
{
   int        subnr;
   int        i;
   int        sample;
   int        sli = 0;
   fint       cwlo, cwhi;
   fint       tid = 0;
   fint       imagesize = (inset.bhi[0] - inset.blo[0] + 1) *
                          (inset.bhi[1] - inset.blo[1] + 1);
   fint       pixelsread = 0;
   int        numpoints;


   numpoints = (xhiallgr - xloallgr) + 1;

   /*--------------------------------------------------*/
   /* Create space for the extracted sample data.      */
   /*--------------------------------------------------*/
   for (i = 0; i < numslices; i++)
   {
      int xvsize = numpoints * inset.nsubs;
      
      if (slice[i].image != NULL)
         free( slice[i].image );
      slice[i].image = (float *) calloc( xvsize, sizeof(float) );
      if (slice[i].image == NULL)
      {
         anyoutf( 3, "WARNING (getxvmap): [Slice %d]: cannot allocate %d bytes for XV data!",
                  i, xvsize );
         return( 0 );
      }
      /* Is a contour set specified? */
      if (contourset.exist)
      {
         if (slice[i].conimage != NULL)
            free( slice[i].conimage );  
         slice[i].conimage = (float *) calloc( xvsize, sizeof(float) );
         if (slice[i].conimage == NULL)
         {
            anyoutf( 3, "WARNING (getxvmap): [Slice %d]: cannot allocate %d bytes for contour data!",
                     i, xvsize );
            return( 0 );
         }
      }
      if (velocityset.exist)
      {
         /* Not an image! */
         if (slice[i].velpoints != NULL)
            free( slice[i].velpoints );  
         slice[i].velpoints = (float *) calloc( numpoints, sizeof(float) );
         if (slice[i].velpoints == NULL)
         {
            anyoutf( 3, "WARNING (getxvmap): [Slice %d]: cannot allocate %d bytes for velocity data!",
                     i, numpoints );
            return( 0 );
         }
      }
   }
   /*--------------------------------------------------*/
   /* Fill the xv arrays subset by subset.             */
   /*--------------------------------------------------*/
   for(subnr = 0; subnr < inset.nsubs; subnr++)
   {
      tid  = 0;
      cwlo = gdsc_fill_c( inset.Name, &(inset.subsets[subnr]), inset.blo );
      cwhi = gdsc_fill_c( inset.Name, &(inset.subsets[subnr]), inset.bhi );
           
      gdsi_read_c( inset.Name,
                   &cwlo, &cwhi,
                   &image[inset.blo[1]][inset.blo[0]],
                   &imagesize,
                   &pixelsread,
                   &tid );

      for (sli = 0; sli < numslices; sli++)
      {
         int    samplelen = slice[sli].ndat;


         for (sample = 0; sample < samplelen; sample++)
         {
            float   x, y;
            int     indx = (slice[sli].xlogr-xloallgr) + sample +
                           subnr * numpoints;            

            x = (float) slice[sli].posX[sample];
            y = (float) slice[sli].posY[sample];
            slice[sli].image[indx] = getipval( x, y,
                                               inset.blo,
                                               inset.bhi,
                                               image );
         }
      }
   }

   if (contourset.exist)
   /*--------------------------------------------------*/
   /* Fill the contour arrays subset by subset.        */
   /*--------------------------------------------------*/
   {
      for(subnr = 0; subnr < inset.nsubs; subnr++)
      {
         tid  = 0;
         cwlo = gdsc_fill_c( contourset.Name,
                             &(contourset.subsets[subnr]),
                             inset.blo );
         cwhi = gdsc_fill_c( contourset.Name,
                             &(contourset.subsets[subnr]),
                             inset.bhi );
         gdsi_read_c( contourset.Name,
                      &cwlo, &cwhi,
                      &image[inset.blo[1]][inset.blo[0]],
                      &imagesize,
                      &pixelsread,
                      &tid );

         for (sli = 0; sli < numslices; sli++)
         {
            int    slicelen = slice[sli].ndat;
            for (sample = 0; sample < slicelen; sample++)
            {
               float   x, y;
               int     indx = (slice[sli].xlogr-xloallgr) + sample +
                              subnr * numpoints;

               x = (float) slice[sli].posX[sample];
               y = (float) slice[sli].posY[sample];
               slice[sli].conimage[indx] = getipval( x, y,
                                                     inset.blo,
                                                     inset.bhi,
                                                     image );
            }
         }
      }
   }

   if (velocityset.exist)
   /*--------------------------------------------------*/
   /* Fill the velocity arrays subset by subset. The   */
   /* velocities could have originated f.i. from a     */
   /* MOMENTS set. Note that there is NO repetition in */
   /* subset direction.                                */
   /* The velocity set has only on subset.             */
   /* Each overlay velocity is put on an sample        */
   /* position and the sampling is not at a tilted     */
   /* ring radius.                                     */
   /*--------------------------------------------------*/
   {
      tid  = 0;
      cwlo = gdsc_fill_c( velocityset.Name,
                          &(velocityset.subsets[0]),
                          inset.blo );
      cwhi = gdsc_fill_c( velocityset.Name,
                          &(velocityset.subsets[0]),
                          inset.bhi );
      gdsi_read_c( velocityset.Name,
                   &cwlo, &cwhi,
                   &image[inset.blo[1]][inset.blo[0]],
                   &imagesize,
                   &pixelsread,
                   &tid );

      for (sli = 0; sli < numslices; sli++)
      {
         int    slicelen = slice[sli].ndat;
         for (sample = 0; sample < slicelen; sample++)
         {
            float   x, y;
            int     indx = sample; 
           
            x = (float) slice[sli].posX[sample];
            y = (float) slice[sli].posY[sample];
            
            slice[sli].velpoints[indx] = getipval( x, y,
                                                   inset.blo,
                                                   inset.bhi,
                                                   image );
         }
      }
   }
   return( 1 );
}



static void rotate( double x, 
                    double y,
                    double angle,                    
                    double *xr,
                    double *yr )
/*------------------------------------------------------------*/
/* PURPOSE: Rotate x,y over angle (in radians).               */
/*------------------------------------------------------------*/
{
   double SINA = sin(angle);
   double COSA = cos(angle);
   
   *xr = x * COSA - y * SINA;
   *yr = x * SINA + y * COSA;   
}



static int insidebox( double x, 
                      double y, 
                      double xlo, 
                      double ylo, 
                      double xhi, 
                      double yhi )
/*------------------------------------------------------------*/
/* PURPOSE: Return 1 if position is inside box or 0 if not.   */
/*------------------------------------------------------------*/
{
   if (x < xlo) 
      return(0);
   if (x > xhi) 
      return(0);
   if (y < ylo)
       return(0);
   if (y > yhi) 
      return(0);
   return(1);
}




static int getsamplepos( sliceplottype    *slice, 
                         slicecommontype   slicecom,
                         settype           inset,
                         bool              gidsoverlay )
/*------------------------------------------------------------*/
/* PURPOSE: Get sample positions for current slice.           */
/*                                                            */
/* Also the minimum and maximum offset values are stored.     */
/* A slice has a centre position and an angle. In spatial     */
/* systems, this angle is defined on a sphere. Samples are    */
/* taken at user given separation. A special function         */
/* calculates sample positions on a sphere. All positions are */
/* stored in arrays which are part of the slice struct.       */
/* If a transformation to physical coordinates is possible    */
/* then store xlo, xhi in DEGREE in the slice struct.         */
/*------------------------------------------------------------*/
{
   double      cpos[2];
   double      cposphys[2];
   double      physco[2];
   double      grids[2];
   double      delta;
   double      angle;
   fint        r1;
   int         i, k;
   int         imin, imax;
   int         inside;
   int         width;
   static int  coloroffset = 0;



   coloroffset++;
   cpos[0] = slice->cpos[0];                     /* Central position of slice */
   cpos[1] = slice->cpos[1];


   /* Some test output */
   anyoutf( DEBUG, "DEBUG (getsamplepos): Rotation of map: %g degrees", inset.maprotation );
   anyoutf( DEBUG, "DEBUG (getsamplepos): Angle between major axis and slice: %g degrees", slice->angle );
   anyoutf( DEBUG, "DEBUG (getsamplepos): Central position in grids: %g %g", cpos[0], cpos[1] );


   if (slicecom.Xingrids)
   {
      double    xmin, ymin, xmax, ymax;
      int       npixels;
      int       inside;
      double    xc = slice->cpos[0];
      double    yc = slice->cpos[1];
      double    x, xr, yr, xlo, xhi;
      double    delta = fabs( slicecom.delta );

      xmin = (double) inset.blo[0];
      xmax = (double) inset.bhi[0];
      ymin = (double) inset.blo[1];
      ymax = (double) inset.bhi[1];
      
      /* All calculations in pixels, angle is wrt x-axis */
      /* Get border values for upper x, then for lower x */
      /* Start sampling on horizontal line and rotate    */
      /* sample positions. All sample positions within   */
      /* the box are valid.                              */
      {
         angle = RAD( slice->angle );
         x = 0.0;
         npixels = 0;
         do
         {
            rotate( x, 0.0, angle, &xr, &yr );
            xr += xc;
            yr += yc;
            inside = insidebox( xr, yr, xmin, ymin, xmax, ymax );
            if (inside)
            {
               npixels++;
               x += delta;
            }            
         }
         while (inside);
         xhi = x - delta;         
         x = 0.0;
         /* Check the other direction for the lowest x */
         do
         {
            rotate( x, 0.0, angle, &xr, &yr );
            xr += xc;
            yr += yc;
            inside = insidebox( xr, yr, xmin, ymin, xmax, ymax );
            if (inside)
            {
               npixels++;
               x -= delta;
            }            
         }
         while (inside);
         xlo = x + delta;                   
      }

      /* We counted 0,0 twice so: */
      npixels--;

      /* Allocate memory for the positions */
      if (slice->posX != NULL)
         free( slice->posX );
      slice->posX = (double *) calloc( npixels, sizeof(double) );
      if (slice->posX == NULL)
      {
         anyoutf( 3, "WARNING (getsamplepos): Cannot allocate memory for (x-)positions array!" );
         return( 0 );
      }
      if (slice->posY != NULL)
         free( slice->posY ); 
      slice->posY = (double *) calloc( npixels, sizeof(double) );
      if (slice->posY == NULL)
      {
         free( slice->posX );
         anyoutf( 3, "WARNING (getsamplepos): Cannot allocate memory for (y-)positions array!" );
         return( 0 );
      }
      
      /* Store the sample positions between xlo and xhi */
     
      for (k = 0, x = xlo; x <= xhi; x += delta, k++)
      {
         rotate( x, 0.0, angle, &xr, &yr );
         xr += xc;
         yr += yc;
         slice->posX[k] = xr;
         slice->posY[k] = yr;
         if (gidsoverlay)
         {
            plsymbol( slice->posX[k], slice->posY[k], DOTCIRCLE );
         }         
      } 
      
      /* Administration for future plot */
      slice->xhi = slice->posX[0];                             /* Store x low */
      slice->xhi = slice->posX[k-1];                          /* Store x high */
      slice->ndat = k;
      slice->xlogr = 0;
      slice->xhigr = k - 1;
   }
   else
   {
      cposphys[0] = cposphys[1] = 0.0;
      r1 = grtoph_c( inset.Name, &inset.subsets[0], cpos, cposphys );
      anyoutf( DEBUG, "DEBUG (getsamplepos): Central position in physical coords: %g %g",
               cposphys[0], cposphys[1] );
      if (r1 != 0)
      {
         anyoutf( DEBUG, "WARNING (getsamplepos): Cannot transform grids to physical coordinates!" );
         return( 0 );
      }
      /*--------------------------------------------------*/
      /* Calculations in physical units. Delta was in     */
      /* XUNITS= The angle is defined wrt. the north in   */
      /* the direction of the east.                       */
      /*--------------------------------------------------*/
      delta = slicecom.delta * slicecom.Xunits2head; /* In fact a conv. to deg. */
      angle = slice->angle;
      /*--------------------------------------------------*/
      /* We follow the definition of a maprotation of     */
      /*'Representations of Celestial Coordinates in FITS'*/
      /* by Greisen and Calabretta. The rotation can be   */
      /* found as item CROTA in the header of a set. It   */
      /* is the angle from the +y axis to the +M (direc-  */
      /* tion of constant longitude) in the direction of  */
      /* the +x axis.                                     */
      /*--------------------------------------------------*/
      if (inset.cdelt[0] < 0.0)
         angle += inset.maprotation;
      else
         angle -= inset.maprotation;

      /*--------------------------------------------------*/
      /* The angle is defined wrt. the declination axis.  */
      /* This is also the angle needed in the routine     */
      /* that calculates new positions 'dispcoord'.       */
      /*--------------------------------------------------*/

      /*--------------------------------------------------*/
      /* Try to store the positions in an array. But      */
      /* before that, we must know how big the array will */
      /* be. Therefore try to find a maximum displacement.*/
      /* Look for the highest index which results in a    */
      /* max. displacement from the central position      */
      /* 'cposphys'. This position must be within the box */
      /* blo,bhi. Otherwise no slicedata will be          */
      /* extracted.                                       */
      /*--------------------------------------------------*/
      imax = 0;
      do
      {
         dispcoord( cposphys[0], cposphys[1],
                    ((double) imax)*delta,
                    angle,
                    inset.cdelt,
                    &physco[0], &physco[1] );
         r1 = phtogr_c( inset.Name, &(inset.subsets[0]), physco, grids );
         inside = ( grids[0] >= (double) inset.blo[0] &&
                    grids[0] <= (double) inset.bhi[0] &&
                    grids[1] >= (double) inset.blo[1] &&
                    grids[1] <= (double) inset.bhi[1] );
         if (inside)
            imax++;
      }
      while (inside);
      imax--;                                        /* We did go one too far */

      /*--------------------------------------------------*/
      /* Biggest displacement in the other direction.     */
      /*--------------------------------------------------*/
      imin = 0;
      do
      {
         dispcoord( cposphys[0], cposphys[1],
                    ((double) imin)*delta,
                    angle,
                    inset.cdelt,
                    &physco[0], &physco[1] );
         r1 = phtogr_c( inset.Name, &(inset.subsets[0]), physco, grids );
         inside = ( grids[0] >= (double) inset.blo[0] &&
                    grids[0] <= (double) inset.bhi[0] &&
                    grids[1] >= (double) inset.blo[1] &&
                    grids[1] <= (double) inset.bhi[1] );
         if (inside)
            imin--;
      }
      while (inside);
      imin++;

      width = imax - imin + 1;
      if (width == 0)
      {
         anyoutf( 3, "WARNING (getsamplepos): No slice data extracted!" );
         return( 0 );
      }
      if (width < 0)
      {
         anyoutf( 3, "WARNING (getsamplepos): No slice data. Perhaps slice center outside box!");
         return( 0 );
      }

      slice->posX = (double *) calloc( width, sizeof(double) );
      if (slice->posX == NULL)
      {
         anyoutf( 3, "WARNING (getsamplepos): Cannot allocate memory for (x-)positions array!" );
         return( 0 );
      }
      slice->posY = (double *) calloc( width, sizeof(double) );
      if (slice->posY == NULL)
      {
         free( slice->posX );
         anyoutf( 3, "WARNING (getsamplepos): Cannot allocate memory for (y-)positions array!" );
         return( 0 );
      }


      /*--------------------------------------------------*/
      /* Store the positions in the posX and posY arrays. */
      /* Note that all these positions correspond to      */
      /* grids within box blo, bhi.                       */
      /*--------------------------------------------------*/
      if (gidsoverlay)
         setcolor(RED);
      for (k = 0, i = imin; i <= imax; i++, k++)
      {
         dispcoord( cposphys[0], cposphys[1],
                    ((double) i)*delta,                     /* All in degrees */
                    angle,
                    inset.cdelt,
                    &physco[0], &physco[1] );

         r1 = phtogr_c( inset.Name, &(inset.subsets[0]), physco, grids );
         slice->posX[k] = grids[0];
         slice->posY[k] = grids[1];
         if (gidsoverlay)
         {
            if (k==(imax-imin+1)/4)
               setcolor((RED+coloroffset)%14);
            plsymbol( grids[0], grids[1], SMALLCIRCLE );
         }
      }
      slice->ndat = width;

      /* Store the minimum and maximum offset in units given by the user */
      slice->xlo = ((double) imin) * delta / slicecom.Xunits2head;
      slice->xhi = ((double) imax) * delta / slicecom.Xunits2head;
      slice->xlogr = imin;
      slice->xhigr = imax;
      anyoutf( DEBUG, "DEBUG (getsamplepos) grids lo,hi = %d %d ndat=%d", imin,imax,slice->ndat );      
      if (slice->xlo == slice->xhi)
      {
         anyoutf( 3, "WARNING (getsamplepos): No sample points: Perhaps value in SAMPLE= too big!" );
      }
   }
   return( YES );
}




static void resetTRdata( ident  id,
                         char  *key,
                         int    code,
                         void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: RESET= keyword handler.                           */
/*------------------------------------------------------------*/
{
   globaltype *g=(globaltype*)arg;
   fint        dfault = HIDDEN;   
   fint        nitems = 1;
   fint        indx;
   fint        r;
   int         i, j;   
   
   r = userint_c( &indx,
                  &nitems,
                  &dfault,
                  tofchar(key),
                  tofchar(" ") ); 
                  

   if (g->parcommon.numTRrad > 0)
   {
      if (indx <= POSANG)  /* Copy/restore just ONE array */
      {
         int   k = 2 - indx;
         for (i = 0; i < g->parplot[k].ndata; i++)
         {
            g->parplot[k].wdata[i] = g->parplot[k].sdata[i];
         }      
         plotTRdata( g, k );      
      }
      else                 /* Copy/restore ALL arrays */
      {
         for (j = 0; j < 3; j++)
         {
             for (i = 0; i < g->parplot[j].ndata; i++)    
            {
               g->parplot[j].wdata[i] = g->parplot[j].sdata[i];            
            }         
            plotTRdata( g, j );
         }
      }
   }
   
   if (g->slicecom.numslices > 0)
      putmarkersfirsttime( g );
}




static void refreshslice( ident  id,
                          char  *key,
                          int    code,
                          void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: M0/3_APPLY= keyword handler.                      */
/*------------------------------------------------------------*/
{
   globaltype   *g=(globaltype*)arg; 

   if (g->slicecom.numslices <= 0)       /* Nothing to do */
      return;
      
   putmarkersfirsttime( g ); 
         
   /* Button reset in markers.c */
}




static void refreshTR( ident  id,
                       char  *key,
                       int    code,
                       void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: M1_APPLY= keyword handler.                        */
/*------------------------------------------------------------*/
{
   globaltype   *g=(globaltype*)arg; 
   int   j;
   
  
   if (g->parcommon.numTRrad <= 0)        /* Nothing to do */
      return;
      
   for (j = 0; j < 3; j++)   
   {
      plotTRdata( g, j ); 
   }      

   /* Button reset in markers.c */
}



static void refreshplot( ident  id,
                         char  *key,
                         int    code,
                         void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: M2_APPLY= keyword handler.                        */
/*------------------------------------------------------------*/
{
   globaltype   *g=(globaltype*)arg; 


   if (g->slicecom.numslices <= 0)       /* Nothing to do */
      return; 

   wkeyf("EXTRACT=Y");   

   /* Button reset in markers.c */
}




static void extract( ident  id,
                     char  *key,
                     int    code,
                     void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: EXTRACT= keyword handler.                         */
/* Extract data for the slices.                               */
/*------------------------------------------------------------*/
{
   globaltype   *g=(globaltype*)arg;
   int          i;
   static int   oldbloX, oldbloY;   
   static int   firsttime = YES;
    
   
   if (!g->hardcopy && !UserLog(key))
      return;

   clearstatus();
   if (g->slicecom.numslices == 0)
   {
      error_status( "No slices defined!",
                    "Define slices in angle input field", "" );
      rejectf( key, "No slices defined!" );      
      return;
   }
   
   getplotpositions( g->slice, &(g->slicecom), g->hardcopy );
   for (i = 0; i < g->slicecom.numslices; i++)
   {
      /* Positions of sample points are part of the slice struct */
      getsamplepos( &(g->slice[i]),
                    g->slicecom,
                    g->inset,
                    NO );
   }
   
   /*--------------------------------------------------*/
   /* Get the min,max of all slices xlo, xhi           */
   /*--------------------------------------------------*/
   getminmaxX( &(g->slicecom), g->slice );
 

   if (subsetdata != NULL)
   {
      freefmatrix( subsetdata,                            /* Free subset data */
                   oldbloX,
                   oldbloY );
      subsetdata = NULL;     
   }
   /*--------------------------------------------------*/
   /* Fill the xv arrays for all slices but before     */
   /* that, create work space to read an entire subset */
   /* in memory.                                       */
   /*--------------------------------------------------*/
   subsetdata = fmatrix( g->inset.blo[0],   /* Allocate space for a 2-dim matrix */
                         g->inset.blo[1],
                         g->inset.bhi[0],
                         g->inset.bhi[1] );
   oldbloX = g->inset.blo[0];
   oldbloY = g->inset.blo[1];                         

                        
   getxvmap( g->slice,
             g->slicecom.numslices,
             g->slicecom.xloallgr,
             g->slicecom.xhiallgr,
             g->inset,
             g->contourset,
             g->velocityset,
             subsetdata );
   /* The xv data is now part of the slice struct */


   
   if (!g->hardcopy)   
   {
      selectplot( g->PGmainplotid );
      pgeras_c( );         
      /* Initialize coulours for the first time using the colour editor */
      if (firsttime)
      {
         wkeyf( "COLEDIT=Y" );
         GgiHandleEvents();                  
         firsttime = NO;
      }
   }
   
   setcolor( g->slicecom.labcol );        
   for (i = 0; i < g->slicecom.numslices; i++) 
   {
      setwindowandviewport( &(g->slice[i]), &(g->slicecom) );            
      plotslice( g, i );
      plotaxis( g->slice[i], &(g->slicecom), g->inset.Name );
      if (g->contourset.exist) 
         plotcontours( g, i );
      if (g->velocityset.exist)
         plotsetvelocities( g, i );
   }
   annotatesliceplot( g );
   g->slicecom.plotexist = YES;      
   if (!g->hardcopy)
   {
      GgiPlotRecord( g->mainplotGGIid, 0 );
      gui_datarange( g->inset.clip[0], g->inset.clip[1]);  /* Annotate wedge */
   }
   else
   {
      pgiden_c();
   }

   /* Check compatibility of Slice- & TR units */
   if (g->slicecom.plotexist && g->parplot[0].exist && g->parplot[1].exist && g->parplot[2].exist)
   {
      fint    r1, r2;
      r1 = factor_c( g->parcommon.TRVUnits,
                     g->slicecom.VUnits,
                     &(g->parcommon.TRVunits2head) );
      if (r1 != 0)
      {
         char   mes[128];
         sprintf( mes, "No conversion between %s and %s, i.e.:", 
                  g->parcommon.TRVunits,
                  g->slicecom.Vunits );
         error_status( "Units not compatible!", mes,
                       "tilted ring vel. units cannot be converted to sky vel.units" );
      }
      r2 = factor_c( g->parcommon.TRXUnits,
                     g->slicecom.XUnits,
                     &(g->parcommon.TRXunits2Xunits) );
      if (r2 != 0)
      {
         char   mes[128];
         sprintf( mes, "No conversion between %s and %s, i.e.:", 
                  g->parcommon.TRXunits,
                  g->slicecom.Xunits );         
         error_status( "Units not compatible!", mes, 
                       "tilted ring radii units cannot be converted to offset units" );
      }
      if (r1 == 0 && r2 == 0)
      {      
         /* Draw TR markers in slice plots */
         putmarkersfirsttime( g );
      }
   }   
   wkeyf( key );     /* Reset key */
}



static void saveslices( ident id,
                        char  *key,
                        int   code,
                        void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: SAVESLICES= keyword handler.                      */
/* Get a set (name) to and save XV maps as subsets.           */
/*------------------------------------------------------------*/
{
   globaltype    *g = (globaltype *) arg;
   fint          dfault, nitems, r;
   static int    okayed = NO;



   clearstatus();
   
   if (g->slicecom.numslices <= 0)
   {
      error_status( "No slices to save!", "", "" );
      rejectf( key, "No slices to save!" );
      okayed = NO;
      return;
   }
   
   dfault = HIDDEN;
   nitems = 1;
   r = userchar_c( g->outset.Name, &nitems, &dfault, tofchar(key), tofchar(" ") );
   g->outset.Name.a[nelc_c(g->outset.Name)] = '\0';     
   if (r == 0)
   {
      okayed = NO;
      return;
   }
   /* Check existence */
   if (!okayed)
   {
      r = 0;
      if (gds_exist_c(g->outset.Name, &r))
      {
         error_status( "Output set already exists!", "", "" ); 
         GgiPrompter( "AGAIN", "Ok to overwrite?" );
         okayed = YES;
         return;       
      }
   }
   okayed = NO;
   GgiPrompter( "ACCEPT", "" );
   r = 0;
   if (gds_exist_c(g->outset.Name, &r)) 
   {
      r = 0;
      gds_delete_c( g->outset.Name, &r );
   }
   
   /* Now we can start creating a new set for output of slices */      
   writeslices( g );

}     



static void saveTRdata( ident id,
                        char  *key,
                        int   code,
                        void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: SAVETILT= keyword handler.                        */
/* Get a file (name) to select a file to save TR results      */
/*------------------------------------------------------------*/
{
   globaltype *g = (globaltype *) arg;
   FILE       *fp;
   fchar      Dummytxt; 
   fint       dfault, nitems, r;
   static int okayed = NO;


   clearstatus();
   
   if (g->parcommon.numTRrad <= 0)
   {
      error_status( "Nothing to save!", "", "" );
      rejectf( key, "Nothing to save!" );
      okayed = NO; 
      return;
   }
   
   dfault = HIDDEN;
   nitems = 1;
   Dummytxt.l = FILENAMELEN-1;
   Dummytxt.a = g->savedatname;   
   r = userchar_c( Dummytxt, &nitems, &dfault, tofchar(key), tofchar(" ") );
   Dummytxt.a[nelc_c(Dummytxt)] = '\0';     
   if (r == 0)
   {
      okayed = NO; 
      return;
   }
   
   /* Check existence */
   if (!okayed)
   {
      fp = fopen( g->savedatname, "r" ); 
      if (fp != NULL) 
      {
         error_status( "File exists!", "", "" );
         GgiPrompter( "AGAIN", "Ok to overwrite?" );
         okayed = YES;
         fclose( fp );
         return;            
      }
   }
   okayed = NO;
   
   
   fp = fopen( g->savedatname, "w" );
   if (fp == NULL)
   {
      error_status( "Cannot open file on disk", "Check permissions", "" );   
      rejectf( key, "Cannot open file on disk!" );
      return;
   }
   GgiPrompter( "ACCEPT", "" );


   /* Do the writing to file */
   {
      int   i;
      
      fprintf( fp, "!%15s  %15s  %15s  %15s\n",
               "Radius", "Velocity", "Inclination", "Pos. angle" );

      for (i = 0; i < g->parcommon.numTRrad; i++)
      {
          fprintf( fp, " %15f  %15f  %15f  %15f\n",
                   g->parcommon.radii[i],
                   g->parplot[VELOCITY].wdata[i],
                   g->parplot[INCLINATION].wdata[i],
                   g->parplot[POSANG].wdata[i] );
      }
   }

   fclose( fp );
   {
      char  mes[512];
      sprintf( mes, "Modified Tilted Ring data saved to disk in: %s", g->savedatname );
      error_status( "", mes, "" );
   }
}





static void showhelp( ident  id,
                      char  *key,
                      int    code,
                      void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: HELP= keyword handler.                            */
/* Try to display the dc1 document with document viewer.      */
/*------------------------------------------------------------*/
{
   bool help = UserLog(key);

   if (help)
   {
      char       path[FILENAME_MAX];
      char       filename[FILENAME_MAX];
      FILE       *fp;

      strcpy( path, getenv("gip_tsk") );

      (void) sprintf( filename, "%s/%s.dc1", path, taskname );
      fp = fopen( filename, "r" );
      if (fp == NULL)
      {
         (void) sprintf( filename, "./%s.dc1", taskname );
         fp = fopen( filename, "r" );
         if (fp == NULL)
         {
            error_status( "Cannot open dc1 document for help", filename, "" );
            return;
         }
      }
      GgiShowDoc( filename );
      fclose( fp );
      return;
   }
   else
   {
      GgiShowDoc( NULL );
   }
}




static void startgui( void )
/*------------------------------------------------------------*/
/* PURPOSE: Get plot window sizes fom user and start gui.    */
/*------------------------------------------------------------*/
{
   fint       xysize[2];  
   fint       nitems  = 2;
   fint       dfault  = HIDDEN;
   fint       r;

   xysize[0] = xysize[1] = 500;
   r = userint_c( xysize,
                  &nitems,
                  &dfault,
                  tofchar("XYSIZE="),
                  tofchar("Enter x, y plot window size in pixels:   [500 500]") );
                     
   xysize[0] = MYMAX( 1, xysize[0] );
   xysize[1] = MYMAX( 1, xysize[1] );   
   
   gui( xysize[0], xysize[1] );
}




static void setgids( ident id,
                     char  *key,
                     int   code,
                     void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: GIDSSLICES= keyword handler.                      */
/*------------------------------------------------------------*/
{
   globaltype *g = (globaltype *) arg;
   fint     r, status;
   fint     subset = 0;
   fint     axnum;
   char     axname[FITSLEN+1];
   fchar    Axname;
   fint     chop = YES;
   fint     nitems, dfault;
  
 
   if (!UserLog(key))
     return;                         

   if (!g->slicecom.plotexist)
   {
      error_status( "No slices extracted", "Plot slices with PLOT button first", "" );
      wkeyf( key );     /* Reset key */  
      return;
   }
   /*--------------------------------------------------*/
   /* Display the subset for the velocity in the       */
   /* middle of the velocity axis.                     */
   /*--------------------------------------------------*/     
   axnum = g->inset.axnum[ g->inset.setdim-1 ];    
   Axname.l = FITSLEN;
   Axname.a = axname;
   getaxname_c( g->inset.Name, &axnum, &chop, Axname );
   nitems = 1;
   dfault = HIDDEN;
   subset = (int) ( (g->slicecom.Vmingr + g->slicecom.Vmaxgr)/2.0 );   
   r = userint_c( &subset, &nitems, &dfault, tofchar("OVERSUB="), tofchar("") );
   if (r)
   {
      subset = MYMAX( subset, g->slicecom.Vmingr );
      subset = MYMIN( subset, g->slicecom.Vmaxgr );
   }
   else
   {
      wkeyf( "OVERSUB=%d", subset );
   }
   wkeyf("OVERSET=%s %s %d", g->inset.Name.a,  Axname.a, subset );
   r = 0;
   subst_c( tofchar( "INSET=OVERSET=" ), &r ); 
   deputy_c( tofchar("VIEW"), &status ); 
   (void) initplot( "gids//append", 1, 1 );
   clearstatus();   
   overlayslices( g );
   pgclos_c();
   wkeyf( key );     /* Reset key */
}




static void hardcopy( ident id,
                      char  *key,
                      int   code,
                      void  *arg )
/*------------------------------------------------------------*/
/* PURPOSE: HCFILENAME= keyword handler.                      */
/*                                                            */
/* Open a hardcopy device (printer or file) and build the     */
/* plot. Close the device afterwards and return to the main   */
/* plot on screen.                                            */
/*------------------------------------------------------------*/
{
   globaltype *g = (globaltype *) arg;
   fchar      Dummytxt; 
   char       dummytxt[FILENAMELEN];
   fint       dfault, nitems, r;
   int        len;
   char       devname[FILENAMELEN+64];
   FILE       *fp;
   static int okayed = NO;
      
   
   dfault = HIDDEN;
   nitems = 1;
   Dummytxt.l = FILENAMELEN-1;   
   Dummytxt.a = dummytxt;
   clearfchar( Dummytxt );
   r = userchar_c( Dummytxt, &nitems, &dfault, tofchar(key), tofchar(" ") );
   len = nelc_c(Dummytxt);
   Dummytxt.a[len] = '\0';
   
   
   if (len == 0)
   {
      error_status( "Need a PostScript file name!", "", "" );
      rejectf( key, "Need a PS file name!" );
      okayed = NO;
      return;
   }
   strcpy( g->PSname, dummytxt );
   
   /* Check existence */
   if (!okayed)
   {
      fp = fopen( g->PSname, "r" ); 
      if (fp != NULL) 
      {
         error_status( "File exists!", "", "" );
         GgiPlotPrompter( "AGAIN", NULL, "Ok to overwrite? Press 'PLOT' for ok." );
         okayed = YES;
         fclose( fp );
         return;            
      }
   }
   okayed = NO;
      
   GgiPlotPrompter( "ACCEPT", NULL, NULL );

   Dummytxt.l = FILENAMELEN-1;   
   Dummytxt.a = dummytxt;
   clearfchar( Dummytxt );
   r = userchar_c( Dummytxt, &nitems, &dfault, tofchar("HCDEVICE="), tofchar(" ") );
   len = nelc_c(Dummytxt);
   Dummytxt.a[len] = '\0';
   sprintf( devname, "%s/%s", Dummytxt.a, g->PSname );
   anyoutf( DEBUG, "DEBUG (hardcopy): output: %s", devname );
   
   /* Start writing to the hard copy device */
   {
      int i;
      
      g->hardcopy = YES;
      g->PGhardcopyid = initplot( devname, 1, 1 );
      for (i = 0; i < 3; i++)  
      {
         plotTRdata( g, i );
      }      
      extract( NULL, "EXTRACT=", KEYCHANGE, g ); 
            
      pgclos_c();
      g->hardcopy = NO;
      g->PGhardcopyid = 0;
      selectplot( g->PGmainplotid );                        /* Back to screen */      
      getplotpositions( g->slice, &(g->slicecom), g->hardcopy );     /* Reset */      
   }
   
   /* Give an extra message that the output was written to file */
   {
      char   mes[STRLEN];
      sprintf( mes, "PostScript output written to: %s", g->PSname );
      error_status( "", mes, "" );   
   }  
}




static void initializestruct( globaltype *g )
/*------------------------------------------------------------*/
/* PURPOSE: Initialize values for current status.             */
/*------------------------------------------------------------*/
{
   int   i;
   
   g->slice = NULL;
   g->boxexist = NO;
   g->inset.exist = NO;
   g->velocityset.exist = NO;
   g->contourset.exist = NO;
   g->hardcopy = NO;
   strcpy( g->PSname, "inspectorplot.ps" );
   strcpy( g->savedatname, "inspectorTR.dat" );
   
   /* The output set is not initialized  with 'getset'! */
   g->outset.Name.a = g->outset.buf;
   g->outset.Name.l = FILENAMELEN;
   clearfchar( g->outset.Name );     
   strcpy( g->outset.Name.a, "insp_outset" );
   
   g->slicecom.Vunits2head = 1.0;        /* Conversion to header units */
   g->slicecom.VUnits.a = g->slicecom.Vunits;
   g->slicecom.VUnits.l = FITSLEN;   
   g->slicecom.HeaderVunits.a = g->slicecom.headerVunits;
   g->slicecom.HeaderVunits.l = FITSLEN;
   g->slicecom.HeaderXunits.a = g->slicecom.headerXunits;
   g->slicecom.HeaderXunits.l = FITSLEN;   
   g->slicecom.XUnits.a = g->slicecom.Xunits;
   g->slicecom.XUnits.l = FITSLEN;
   g->slicecom.numslices = 0;
   g->slicecom.plotexist = NO;
   g->parcommon.TRXUnits.a = g->parcommon.TRXunits;
   g->parcommon.TRXUnits.l = FITSLEN;      
   g->parcommon.TRVUnits.a = g->parcommon.TRVunits;
   g->parcommon.TRVUnits.l = FITSLEN;   
   g->parcommon.radii = NULL;               
   for (i = 0; i < 3; i++)
   {
      g->parplot[i].sdata = NULL;
      g->parplot[i].wdata = NULL;
      g->parplot[i].exist = NO;
      g->parplot[i].ndata = 0;
      g->parplot[i].segcount = 0;      
      g->parplot[i].xseg1 = 0.0;      
      g->parplot[i].xseg2 = 0.0;            
      g->parplot[i].yseg1 = 0.0;      
      g->parplot[i].yseg2 = 0.0;      
      g->PGparplotid[i] = 0;
   }
   strcpy( g->parplot[0].name, "VELOCITY" );
   strcpy( g->parplot[1].name, "INCLINATION" );
   strcpy( g->parplot[2].name, "POSANG" );      
   
   g->slicecom.labstartxy[0] = g->slicecom.labstartxy[1] = dblank;
   g->slicecom.labdeltaxy[0] = g->slicecom.labdeltaxy[1] = dblank;   
   g->slicecom.labsubxy[0] = g->slicecom.labsubxy[1] = -1;
   g->slicecom.numcontours = 0;
   g->slicecom.maxlablen = 0.0;
   g->PGmainplotid = 0;   
   g->PGhardcopyid = 0;      
   g->inset.cpos[0] = g->inset.cpos[1] = 0.0;
   g->gids.overlay = NO;
   g->gids.set.a = g->gids.buf;
   g->gids.set.l = FILENAMELEN;
   g->gids.subset = 0;
}




static void quit( ident  id,
                  char  *key,
                  int    code,
                  void  *arg)
/*------------------------------------------------------------*/
/* PURPOSE: QUIT= keyword handler.                            */
/*------------------------------------------------------------*/
{
   globaltype  *g = (globaltype*) arg;


   /* bool quit = UserLog(key); */
   if (1)
   {
      /* Release memory */
      if (g->slice != NULL)
         free( g->slice ); 
      cancel( key );                       /* remove QUIT= keyword */
      finis_c();                           /* terminate task */
   }
   wkeyf( key );
}




MAIN_PROGRAM_ENTRY
/*-------------------------------------------------------------------------*/
/* The macro MAIN_PROGRAM_ENTRY replaces the C-call main() to start the    */
/* main body of your GIPSY application. Variables defined as 'fchar' start */
/* with a capital.                                                         */
/*-------------------------------------------------------------------------*/
{
   globaltype   glob;
   int          i;
   
  
   init_c();                                /* Contact Hermes */

   /* Initialize global variables */
   strcpy( taskname, "inspector" );         /* Lower case is essential to find .dc1 */
   setfblank_c( &fblank );
   setdblank_c( &dblank );
   setlevel = 0;

   initializestruct( &glob );               /* Set default properties */
   wkeyf( "HCFILENAME=%s", glob.PSname );   /* Set default PS file name */ 
   wkeyf( "SAVETILT=%s", glob.savedatname );   /* Set default output data file name */ 
   wkeyf( "SAVESLICES=%s", glob.outset.Name.a );
   
  
   startgui();
   
   glob.PGmainplotid = initplot( "SLICES", 1, 1 );    
   glob.mainplotGGIid = GgiPlotField( "SLICES", 0, 0 );
        
   for (i = 0; i < 3; i++)
   {         
      glob.PGparplotid[i] = initplot( glob.parplot[i].name, 1, 1 );
      glob.parplotGGIid[i] = GgiPlotField( glob.parplot[i].name, 0, 0 );
      glob.plotcursorid[i] = ScheduleGgiPlotCursor( mouse1, glob.parplotGGIid[i], &glob );      
   }

   /* Start scheduling the functions */

   (void) ScheduleKeyevent( quit,        "QUIT=",     KEYCHANGE, &glob );
   (void) ScheduleKeyevent( showhelp,    "HELP=",     KEYCHANGE, NULL ); 
   (void) ScheduleKeyevent( getset,      "INSET=",    KEYCHANGE, &glob );
   (void) ScheduleKeyevent( getset,      "CONSET=",   KEYCHANGE, &glob );
   (void) ScheduleKeyevent( getset,      "VELSET=",   KEYCHANGE, &glob );
   (void) ScheduleKeyevent( getangles,   "SKYANGLES=",KEYCHANGE, &glob );
   (void) ScheduleKeyevent( getslicex0y0,"SLICEX0Y0=",KEYCHANGE, &glob );
   (void) ScheduleKeyevent( getbox,      "BOX=",      KEYCHANGE, &glob );
   (void) ScheduleKeyevent( getclip,     "CLIP=",     KEYCHANGE, &(glob.inset) );
   (void) ScheduleKeyevent( extract,     "EXTRACT=",  KEYCHANGE, &glob );
   (void) ScheduleKeyevent( getsamples,  "SAMPLE=",   KEYCHANGE, &glob );
   (void) ScheduleKeyevent( properties,  "PROPERTIES=",KEYCHANGE, &glob );   
   (void) ScheduleKeyevent( getVunits,   "VUNITS=",   KEYCHANGE, &glob ); 
   (void) ScheduleKeyevent( getXunits,   "XUNITS=",   KEYCHANGE, &glob );    
   (void) ScheduleKeyevent( getTRVunits, "TRVUNITS=",   KEYCHANGE, &glob ); 
   (void) ScheduleKeyevent( getTRXunits, "TRXUNITS=",   KEYCHANGE, &glob );
   (void) ScheduleKeyevent( getrows,     "MOSROWS=",     KEYCHANGE, &glob );
   (void) ScheduleKeyevent( getTRradii,  "RADII=",       KEYCHANGE, &glob );
   (void) ScheduleKeyevent( getTRdata,   "VELOCITY=",    KEYCHANGE, &glob );
   (void) ScheduleKeyevent( getTRdata,   "INCLINATION=", KEYCHANGE, &glob );
   (void) ScheduleKeyevent( getTRdata,   "POSANG=",      KEYCHANGE, &glob );
   (void) ScheduleKeyevent( hardcopy,    "HCFILENAME=",  KEYCHANGE, &glob );
   (void) ScheduleKeyevent( getdble,     "VSYS=",        KEYCHANGE, &(glob.slicecom.Vsys) );
   (void) ScheduleKeyevent( getcontours, "CONTOURS=",    KEYCHANGE, &(glob) );
   (void) ScheduleKeyevent( getconcol,   "CONCOL=",      KEYCHANGE, &(glob) );
   (void) ScheduleKeyevent( getgalcentre,"GALCENTRE=",   KEYCHANGE, &(glob) );
   (void) ScheduleKeyevent( resetTRdata, "RESET=",       KEYCHANGE, &(glob) );
   (void) ScheduleKeyevent( getint,      "M0_SYMBOL=",   KEYCHANGE, &(glob.slicecom.marker1.symbol) );
   (void) ScheduleKeyevent( getint,      "M0_LINEWIDTH=",KEYCHANGE, &(glob.slicecom.marker1.width) );
   (void) ScheduleKeyevent( getint,      "M0_COLOUR=",   KEYCHANGE, &(glob.slicecom.marker1.color) );
   (void) ScheduleKeyevent( getfloat,    "M0_SIZE=",     KEYCHANGE, &(glob.slicecom.marker1.size) );
   (void) ScheduleKeyevent( getint,      "M1_SYMBOL=",   KEYCHANGE, &(glob.parcommon.marker.symbol) );
   (void) ScheduleKeyevent( getint,      "M1_LINEWIDTH=",KEYCHANGE, &(glob.parcommon.marker.width) );
   (void) ScheduleKeyevent( getint,      "M1_COLOUR=",   KEYCHANGE, &(glob.parcommon.marker.color) );
   (void) ScheduleKeyevent( getfloat,    "M1_SIZE=",     KEYCHANGE, &(glob.parcommon.marker.size) );
   (void) ScheduleKeyevent( getint,      "M2_SYMBOL=",   KEYCHANGE, &(glob.slicecom.marker2.symbol) );
   (void) ScheduleKeyevent( getint,      "M2_LINEWIDTH=",KEYCHANGE, &(glob.slicecom.marker2.width) );
   (void) ScheduleKeyevent( getint,      "M2_COLOUR=",   KEYCHANGE, &(glob.slicecom.marker2.color) );
   (void) ScheduleKeyevent( getfloat,    "M2_SIZE=",     KEYCHANGE, &(glob.slicecom.marker2.size) );   
   (void) ScheduleKeyevent( getint,      "M3_SYMBOL=",   KEYCHANGE, &(glob.slicecom.marker3.symbol) );
   (void) ScheduleKeyevent( getint,      "M3_LINEWIDTH=",KEYCHANGE, &(glob.slicecom.marker3.width) );
   (void) ScheduleKeyevent( getint,      "M3_COLOUR=",   KEYCHANGE, &(glob.slicecom.marker3.color) );
   (void) ScheduleKeyevent( getfloat,    "M3_SIZE=",     KEYCHANGE, &(glob.slicecom.marker3.size) );      
   (void) ScheduleKeyevent( refreshslice,"M0_APPLY=",    KEYCHANGE, &(glob) );
   (void) ScheduleKeyevent( refreshTR,   "M1_APPLY=",    KEYCHANGE, &(glob) );
   (void) ScheduleKeyevent( refreshplot, "M2_APPLY=",    KEYCHANGE, &(glob) );
   (void) ScheduleKeyevent( refreshslice,"M3_APPLY=",    KEYCHANGE, &(glob) );
#if (0)
   (void) ScheduleKeyevent( getdble,     "LABSTARTX=",   KEYCHANGE, &(glob.slicecom.labstartxy[0]) );
   (void) ScheduleKeyevent( getdble,     "LABSTARTY=",   KEYCHANGE, &(glob.slicecom.labstartxy[1]) );
   (void) ScheduleKeyevent( getdble,     "LABDELTAX=",   KEYCHANGE, &(glob.slicecom.labdeltaxy[0]) );
   (void) ScheduleKeyevent( getdble,     "LABDELTAY=",   KEYCHANGE, &(glob.slicecom.labdeltaxy[1]) );
   (void) ScheduleKeyevent( getint,      "LABSUBX=",     KEYCHANGE, &(glob.slicecom.labsubxy[0]) );
   (void) ScheduleKeyevent( getint,      "LABSUBY=",     KEYCHANGE, &(glob.slicecom.labsubxy[0]) );
#endif   
   (void) ScheduleKeyevent( getint,      "LABCOL=",      KEYCHANGE, &(glob.slicecom.labcol) );
   (void) ScheduleKeyevent( getRlimits,  "RADMINMAX=",   KEYCHANGE, &(glob) );   
   (void) ScheduleKeyevent( getTRdatalimits, "VELMINMAX=",   KEYCHANGE, &(glob) );
   (void) ScheduleKeyevent( getTRdatalimits, "INCMINMAX=",   KEYCHANGE, &(glob) );
   (void) ScheduleKeyevent( getTRdatalimits, "PHIMINMAX=",   KEYCHANGE, &(glob) );      
   (void) ScheduleKeyevent( saveTRdata,  "SAVETILT=",    KEYCHANGE, &(glob) );
   (void) ScheduleKeyevent( saveslices,  "SAVESLICES=",  KEYCHANGE, &(glob) );
   (void) ScheduleKeyevent( getint,      "SEGMODE=",     KEYCHANGE, &(glob.parcommon.segmode) );
   (void) ScheduleKeyevent( setgids,     "GIDSSLICES=",  KEYCHANGE, &(glob) );
   (void) ScheduleKeyevent( convertangles, "GALANGLES=", KEYCHANGE, &(glob) );
   (void) ScheduleKeyevent( convertangles, "FIXINC=",    KEYCHANGE, &(glob) );
   (void) ScheduleKeyevent( convertangles, "FIXPHI=",    KEYCHANGE, &(glob) );

    
   TriggerKey("MONITOR="); 
   TriggerKey("INSET=");
   TriggerKey("CONSET=");   
   TriggerKey("VELSET=");   
   TriggerKey("CLIP=");
   TriggerKey("CONTOURS=");
   
   /* Marker attributes for converted TR velocities in slice plots */
   if (!TriggerKey("M0_SYMBOL="))     wkeyf( "M0_SYMBOL=%d", PLUS );
   if (!TriggerKey("M0_LINEWIDTH="))  wkeyf( "M0_LINEWIDTH=%d", 1 );
   if (!TriggerKey("M0_COLOUR="))     wkeyf( "M0_COLOUR=%d", RED );
   if (!TriggerKey("M0_SIZE="))       wkeyf( "M0_SIZE=%g", 2.0 );   
   
   /* Marker attributes for TR velocities in parameter plots */
   if (!TriggerKey("M1_SYMBOL="))     wkeyf( "M1_SYMBOL=%d", PLUS );
   if (!TriggerKey("M1_LINEWIDTH="))  wkeyf( "M1_LINEWIDTH=%d", 1 );
   if (!TriggerKey("M1_COLOUR="))     wkeyf( "M1_COLOUR=%d", YELLOW );
   if (!TriggerKey("M1_SIZE="))       wkeyf( "M1_SIZE=%g", 4.0 );
   
   /* Line of sight velocities from a set in slice plots */
   if (!TriggerKey("M2_SYMBOL="))     wkeyf( "M2_SYMBOL=%d", CROSS );
   if (!TriggerKey("M2_LINEWIDTH="))  wkeyf( "M2_LINEWIDTH=%d", 2 );
   if (!TriggerKey("M2_COLOUR="))     wkeyf( "M2_COLOUR=%d", CYAN );
   if (!TriggerKey("M2_SIZE="))       wkeyf( "M2_SIZE=%g", 2.0 );

   /* Moving markers in slice plots */
   if (!TriggerKey("M3_SYMBOL="))     wkeyf( "M3_SYMBOL=%d", PLUS  );
   if (!TriggerKey("M3_LINEWIDTH="))  wkeyf( "M3_LINEWIDTH=%d", 1 );
   if (!TriggerKey("M3_COLOUR="))     wkeyf( "M3_COLOUR=%d", YELLOW );
   if (!TriggerKey("M3_SIZE="))       wkeyf( "M3_SIZE=%g", 2.0 );
      
   if (!TriggerKey("LABCOL="))        wkeyf( "LABCOL=%d", GREEN );
   if (!TriggerKey("CONCOL="))        wkeyf( "CONCOL=%d", BLUE );
   
   if (!TriggerKey("FIXINC="))        wkeyf( "FIXINC=45" );
   if (!TriggerKey("FIXPHI="))        wkeyf( "FIXPHI=45" );   
   if (!TriggerKey("SEGMODE="))       wkeyf( "SEGMODE=%d", SEG_FACTOR );

   TriggerKey("VSYS=");            
   TriggerKey("SKYANGLES=");
   TriggerKey("SLICEX0Y0=");
   TriggerKey("SAMPLE=");   
   TriggerKey("TRXUNITS=");
   TriggerKey("TRVUNITS=");      
   TriggerKey("VUNITS=");
   TriggerKey("XUNITS=");
   TriggerKey("MOSROWS=");      
   TriggerKey("RADII=");               /* VELOCITY= etc are handled by RADII= */
   TriggerKey("RADMINMAX=");   
   
   eventmonitor("MONITOR=", TRUE);   
  
   MainLoop();

   return( EXIT_SUCCESS );
}
#<

#>            gui.c
/* gui.c -XT
                              COPYRIGHT (c) 1999
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands


Author: J.P. Terlouw
*/

#define STATUSLINES    2            /* number of status lines */
#define IMAGECOLORS   80
#define NFRAMES        3
#define BLINK1         1
#define BLINK2         2

#include "stdio.h"
#include "stddef.h"
#include "gipsyc.h"   
#include "userfio.h"
#include "ggi.h"
#include "pgplot.h"
#include "helptext.h"
#include "input.h"
#include "rotpar.h"
#include "labels.h"
#include "coledit.h"
#include "markers.h"
#include "blinker.h"
#include "gui.h"

static char *file_entries[]={"Input set, subsets and box",
                             "_LINE",
                             "Save tilted ring",
                             "Save plot",
                             "Save slices to set",
                             "_LINE",
                             "Exit", NULL};
typedef enum                {SETSPEC,
                             LINE0,
                             SAVETILT,
                             SAVEPLOT,
                             SAVESLICES,
                             LINE1,
                             EXIT} file_choices;

static char *reset_entries[]={"Position angles",
                              "Inclinations",
                              "Velocities",
                              "_LINE",
                              "ALL", NULL};

static char *seg_entries[]={"Scale factor",
                            "Horizontal line",
                            "Slope", NULL};

static ident l_status[STATUSLINES], l_error;


/* -------------------------------------------------------------------------- */
/*                                 statusbox                                  */
/* -------------------------------------------------------------------------- */
/*  statusbox() creates form containing error and status lines
 */
static ident statusbox(int width)
{
   int   i;
   ident curform, prvform, topref=NULL;  
                             
   curform = GgiForm("status", 1);
   prvform = GgiUseShell(curform);
   l_error  = GgiSetLabel(GgiLabel("error"), " ", 0);
   GgiSetPosition(l_error, 0, NULL,  0, topref);  
   topref = l_error;
   for (i=0; i<STATUSLINES; i++) {
      l_status[i] = GgiSetLabel(GgiLabel("status"), " ", 0);
      GgiSetPosition(l_status[i], 0, NULL,  -1, topref);
      topref = l_status[i];
   }
   GgiSetPosition(GgiCanvas(" ",1,1), width, NULL, 0, NULL); /* reserve space */
   (void)GgiUseShell(prvform);
   return curform;
}

/* -------------------------------------------------------------------------- */
/*                                 handle_menu                                */
/* -------------------------------------------------------------------------- */
static void handle_menu(ident id, char *key, int code, void *arg)
{
   fint  choice, nread;
   ident setinp;
            
   nread = userfint(&choice, 1, 2, key, " ");
   if (nread>0) {
      switch (choice) {
         case SETSPEC: {
            wkeyf("COMPOSE=YES");
            break;
         }
         case SAVEPLOT: {
            GgiPlotPrompter("HCFILENAME=", "HCDEVICE=",
                            "PostScript plot filename:");
            break;
         } 
         case SAVETILT: {
            GgiPrompter("SAVETILT=", H_SAVETILT);
            break;
         }
         case SAVESLICES: {
            GgiPrompter("SAVESLICES=", "Save slices to set");
            break;
         }
         case EXIT: {
            wkeyf("QUIT=YES");
            break;
         }
         default: {
            break;
         }
      }
      wkeyf(key);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 handle_seg                                 */
/* -------------------------------------------------------------------------- */
static void handle_seg(ident id, char *key, int code, void *arg)
{
   fint choice, nread;
            
   nread = userfint(&choice, 1, 2, key, " ");
   if (nread>0) GgiSetLabel((ident)arg, seg_entries[choice], 0);
}

/* ========================================================================== */
/*                                 gui                                        */
/* -------------------------------------------------------------------------- */
/*  Interface routine to initialize the graphical user interface.
 */
extern void gui(int xsize, int ysize)
{
   static char *my_resources[] = {
      "*error.foreground: Red",
#if 0
      "*white.foreground: White",
      "*red.foreground: Red",
      "*green.foreground: Green",
      "*blue.foreground: Blue",
      "*cyan.foreground: Cyan",
      "*magenta.foreground: Magenta",
      "*yellow.foreground: Yellow",
      "*orange.foreground: Orange",
      "*greenyellow.foreground: MediumSpringGreen",
      "*greencyan.foreground: SpringGreen",
      "*bluecyan.foreground: SlateBlue",
      "*bluemagenta.foreground: MediumSlateBlue",
      "*redmagenta.foreground: OrangeRed",
      "*darkgray.foreground: DimGray",
      "*lightgray.foreground: LightGray",
      "*menu_CONCOL.background: black",
      "*menu_LABCOL.background: black",
#endif
      NULL
   };

   int   barwidth=1.5*xsize;
   int   nframes;
 
   ident file, input, rotpar, labels, colors, markers, blink, help;
   ident menubar, sliplot, angplot, incplot, velplot, status,
         smenu, stext, rmenu, logo;

   GgiAutoLayout(FALSE);
   GgiPostponeRealize(TRUE);
   GgiSetResources(my_resources);

/* ----------------------- top level elements ------------------------------ */
   menubar = GgiForm("top", 1); 
   sliplot = GgiPlotField("SLICES", xsize, ysize+6);
   (void)GgiPlotColors(sliplot, 16+IMAGECOLORS);
   angplot = GgiPlotField("POSANG", xsize/2, ysize/3);
   incplot = GgiPlotField("INCLINATION", xsize/2, ysize/3);
   velplot = GgiPlotField("VELOCITY", xsize/2, ysize/3);
   smenu   = GgiSetLabel(GgiMenu("SEGMODE=", H_SEGMODE, seg_entries),
                         "SEG. MODE:", 0);
   stext   = GgiLabel(" ");
   (void)ScheduleKeyevent(handle_seg, "SEGMODE=", KEYCHANGE, stext);
   rmenu   = GgiMenu("RESET=", NULL, reset_entries);
   logo    = GgiLogo(NULL, 0, 0);
   status  = statusbox(xsize-7);

   GgiSetPosition(menubar,   0, NULL,    0, NULL);
   GgiSetPosition(sliplot,   0, NULL,    0, menubar);
   GgiSetPosition(angplot,   0, sliplot, 0, menubar);
   GgiSetPosition(incplot,   0, sliplot, 0, angplot);
   GgiSetPosition(velplot,   0, sliplot, 0, incplot);
   GgiSetPosition(status,    0, NULL,    0, velplot);
   GgiSetPosition(smenu,     0, status,  0, velplot);
   GgiSetPosition(stext,     0, smenu,   0, velplot);
   GgiSetPosition(rmenu,     0, status,  0, smenu);
   GgiSetPosition(logo, -GgiWidth(logo), velplot, 0, velplot);

/* ---------------------------- menu bar ----------------------------------- */
   (void)GgiUseShell(menubar);
   file    = GgiMenu("FILE=", H_FILE, file_entries);
   (void)ScheduleKeyevent(handle_menu, "FILE=", KEYCHANGE, NULL);
   input   = GgiButton("INPUT=", H_INPUT);
   rotpar  = GgiSetLabel(GgiButton("ROTPAR=", H_ROTPAR), "TILTED RINGS", 0);
   labels  = GgiButton("LABELS=", H_LABELS);
   colors  = GgiSetLabel(GgiButton("COLEDIT=", NULL), "COLOURS", 0);
   markers = GgiButton("MARKERS=", "Marker attributes");
   blink   = GgiButton("BLINK=", NULL);
   help    = GgiButton("HELP=",   H_HELP);

   GgiSetPosition(file,     0, NULL,    0, NULL);
   GgiSetPosition(input,    0, file,    0, NULL);
   GgiSetPosition(rotpar,   0, input,   0, NULL);
   GgiSetPosition(labels,   0, rotpar,  0, NULL);
   GgiSetPosition(colors,   0, labels,  0, NULL);
   GgiSetPosition(markers,  0, colors,  0, NULL);
   GgiSetPosition(blink,    0, markers, 0, NULL);
   GgiSetPosition(help,    barwidth-GgiWidth(help)-2, NULL, 0, NULL);
   (void)GgiUseShell(NULL);

   Input("INPUT=");
   Rotpar("ROTPAR=");
   Labels("LABELS=");
   Coledit("COLEDIT=", 17, IMAGECOLORS-1, 16);
   Markers("MARKERS=");
   Blinker("BLINK=", BLINK1, BLINK2);
    
   GgiRealize();
   (void)pgopen_c(tofchar("SLICES"));
   nframes = GgiPlotFrames(sliplot, NFRAMES);
   if (nframes!=NFRAMES) {
      errorf(2, "%d frames allocated instead of %d", nframes, NFRAMES);
   }
   pgclos_c();
}

/* ========================================================================== */
/*                                 gui_status                                 */
/* -------------------------------------------------------------------------- */
/*  Interface routine to display a status message.
 */  
void gui_status(char *message, int lineno)
{
   GgiSetLabel(l_status[lineno%STATUSLINES], message, 0);
}

/* ========================================================================== */
/*                                 gui_error                                  */
/* -------------------------------------------------------------------------- */
/*  Interface routine to display an error message.
 */
void gui_error(char *message)
{
   char buffer[512];
      
   if (*message) {
      sprintf(buffer,"Error: %s",message);
      GgiSetLabel(l_error, buffer, 0);
   } else {
      GgiSetLabel(l_error, " ", 0);
   }
}


#if defined(TESTBED)  /* ++++++++++++++++++++++++++++++++++++++++++++++++++ */
#include "init.h"
#include "finis.h"   
#include "cmain.h"
#include "eventmonitor.h"

static void setmessage(ident id, void *arg)
{
   static int mesno=0;
   char message[80];
 
   sprintf(message,"Status message %d (%o Oct, %X Hex)", mesno, mesno, mesno);
   gui_status(message,mesno);
   mesno++;
}
   
static void seterror(ident id, void *arg)
{   
   static int mesno=1;
   char message[80];

   sprintf(message,"Error message %d (%o Oct, %X Hex)", mesno, mesno, mesno);   
   mesno++;
   if (mesno%2) gui_error(message); else gui_error("");
}  

static void quit(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
   
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
      finis_c();
   }
}


static void draw(ident id, char *key, int code, void *arg)
{
   extern void pgsncr_c(fint *,float *,float *,float *,fint *);
   
   fchar text;
   char ctext[11];
   fint  nread, ci=16+IMAGECOLORS/2, lw=10;
   float x=0.1, y=0.2, ch=5.0;
   
   text.a = ctext; text.l = 10;
   nread = userftext(text, 2, key, " ");
   pgpage_c();
   pgsch_c(&ch);
   pgsci_c(&ci);
   pgslw_c(&lw);
   pgtext_c(&x, &y, text);
}
   

MAIN_PROGRAM_ENTRY
{
   init_c();
   gui(500,500);
   (void)ScheduleTimer(setmessage,  3 SECONDS, NULL);
   (void)ScheduleTimer(seterror,    5 SECONDS, NULL);
   ScheduleKeyevent(quit, "QUIT=", KEYCHANGE, NULL);
   ScheduleKeyevent(draw, "INSET=", KEYCHANGE, NULL);
   (void)pgopen_c(tofchar("SLICES"));
   eventmonitor("MONITOR=", TRUE);
   MainLoop();
}
#endif
#<

#>            utils.c
/* utils.c
                           COPYRIGHT (c) 1999
                      Kapteyn Astronomical Institute
                University of Groningen, The Netherlands
                           All Rights Reserved.
*/

#include    "inspector.h"    /* Defines a.o. the global structure to work with */
#include    "gui.h"
#include    "gds_extend.h"   /* Create or extend an axis in an output set. */ 
#include    "gds_create.h"
#include    "gdsd_rint.h"
#include    "gdsd_wchar.h"
#include    "gdsd_wdble.h"


bool UserLog(char *key)
/*------------------------------------------------------------*/
/* Purpose: Special version of userlog with default.          */
/*------------------------------------------------------------*/
{
   bool result = toflog(FALSE);
   fint fint1 = 1, fint2 = 2;

   (void) userlog_c( &result, &fint1, &fint2, tofchar(key), tofchar(" ") );
   return( tobool(result) );
}   



int emptykey( char  *key )
/*------------------------------------------------------------*/
/* Purpose: Check whether a keyword has contents or not.      */
/*------------------------------------------------------------*/
{
   fchar  Dummytxt;
   char   buf[512+1];
   fint   dfault = HIDDEN;
   fint   r;
   
   
   Dummytxt.a = buf;
   Dummytxt.l = 512;
   r = usertext_c( Dummytxt, &dfault, tofchar(key), tofchar("") );
   return( !r );
}


void hermesstatus( char *txt )
/*------------------------------------------------------------*/
/* PURPOSE: Update status in hermes status                    */                
/*------------------------------------------------------------*/                
{
   status_c( tofchar(txt) );                                                    
}



void error_status( char *errstr,
                   char *statstr1,
                   char *statstr2 )
/*------------------------------------------------------------*/
/* PURPOSE: Update status and error messages in gui.          */
/*------------------------------------------------------------*/
{
   gui_error( errstr );
   gui_status( statstr1, FIRSTLINE );
   hermesstatus( statstr1 );
   gui_status( statstr2, SECONDLINE );
}





void clearstatus( void )
/*------------------------------------------------------------*/
/* PURPOSE: Clear status and error messages in gui.           */
/*------------------------------------------------------------*/
{
   gui_error( "" );
   gui_status( "", FIRSTLINE );
   gui_status( "", SECONDLINE );
   hermesstatus("");
}



void clearfchar( fchar Str )
/*------------------------------------------------------------*/
/* PURPOSE: Set all chars. in a Fortran string to a space.    */
/*------------------------------------------------------------*/
{
   int   i,l;

   l = Str.l;
   for (i = 0; i < l; i++)
      Str.a[i] = ' ';
} 


int quadrant( double angle )
/*---------------------------------------------------------*/
/* Convert an angle to its quadrant.                       */
/*---------------------------------------------------------*/
{
   angle = fmod( angle, 360.0 );
   if (angle < 0.0)
      angle += 360.0;
   if (angle <= 90.0)  return 1;
   if (angle <= 180.0) return 2;
   if (angle <= 270.0) return 3;
   if (angle <= 360.0) return 4;
   return 1;  /* dummy */
}



double putinrangedeg( double angle )
/*------------------------------------------------------------*/
/* PURPOSE: Return angle between 0 and < 360.0                */
/*------------------------------------------------------------*/
{
   while (angle < 0.0)
      angle +=360.0;
   while (angle > 360.0)
      angle -=360.0;
   return( angle );
}



double putinrangerad( double angle )
/*------------------------------------------------------------*/
/* PURPOSE: Return angle between 0 and < 2*PI                 */
/*------------------------------------------------------------*/
{
   double twopi = 2.0*PI;
   while (angle < 0.0)
      angle += twopi;
   while (angle > twopi)
      angle -= twopi;
   return( angle );
}


double sign( double x )
/*------------------------------------------------------------*/
/* PURPOSE: Return -1 if x < 0.0 else return 1                */
/*------------------------------------------------------------*/
{
   if (x < 0.0)
      return( -1.0 );
   return( 1.0 );
}


double arctan( double y, double x )
/*------------------------------------------------------------*/
/* PURPOSE: Return arctangens between 0 and < 360.0           */
/*------------------------------------------------------------*/
{
   double r;
   r = atan2( y, x );
   if (r < 0.0)
      r += 2.0*PI;
   return( r );
}



double atantan( double   beta,
                double   y )
/*------------------------------------------------------------*/
/* PURPOSE: Return:  theta = atan[ tan(beta)/y ]              */
/*                                                            */
/* Input is an angle beta in radians and a value y. The       */
/* function atan2(tan(x)) does not preserve the sign of x.    */
/* Therefore a correction is made for the various intervals.  */
/* atan2( 89,1 )  =  89                                       */
/* atan2( 91,1 )  = -89                                       */
/* atan2( 179,1 ) = -1                                        */
/* atan2( 181,1 ) =  1                                        */
/* atan2( 269,1 ) =  89                                       */
/* atan2( 271,1 ) = -89                                       */
/*------------------------------------------------------------*/
{
   double twopi = 2.0*PI;
   double pidiv2 = PI/2.0;
   double a1 = beta;
   double a2;

   while (a1 < 0.0)
      a1 += twopi;
   while (a1 > twopi)
      a1 -= twopi;
   a2 = atan2( tan(a1), y );
   if (a1 < pidiv2)
      return( a2 );
   if (a1 == pidiv2)
      return( pidiv2 );
   if (a1 < 3.0*pidiv2)
      return( PI + a2 );
   if (a1 == 3.0*pidiv2)
      return( 3.0*pidiv2 );
   return(twopi + a2);
}



double distance( double    long1,
                 double    lat1,
                 double    long2,
                 double    lat2 )
/*------------------------------------------------------------*/
/* PURPOSE: Calculate distance in the sky between two         */
/*          positions in degrees.                             */
/*                                                            */
/* If (a1,d1) is the position in alpha.delta of position 1    */
/* and (a2,d2) is the second position, then the real distance */
/* r, is given by the formula:                                */
/*                                                            */
/* cos(r) = sin(d1)*sin(d2) + cos(d1)*cos(d2)*cos(a2-a1)      */
/*------------------------------------------------------------*/
{
   double   H, d1, d2;
   double   r;


   H  = RAD( long2-long1 );
   d1 = RAD( lat1 );
   d2 = RAD( lat2 );
   r  = acos( sin(d1)*sin(d2) + cos(d1)*cos(d2)*cos(H) );
   return( DEG(r) );
}



int dispcoord( double    longitude,
               double    latitude,
               double    disp,
               double    angle,
               double   *cdelt,
               double   *longout,
               double   *latout )
/*------------------------------------------------------------*/
/* PURPOSE: Calculate new longitude, latitude after a dis-    */
/*          placement in the sky in a direction given by an   */
/*          angle.                                            */
/* INPUT:   longitude: enter in degrees.                      */
/*          latitude:  enter in degrees.                      */
/*          disp:      the displacement in the sky entered    */
/*                     in degrees. The value can also be      */
/*                     negative to indicate the opposite      */
/*                     direction.                             */
/*          angle:     the angle wrt. a great circle of       */
/*                     constant declination entered in        */
/*                     degrees.                               */
/*          cdelt:     A negative value of cdelt[0] corres-   */
/*                     ponds to a counter-clockwise rotation  */
/*                     instead of a clockwise rotation.       */
/*                                                            */
/* Assume a triangle on a sphere with side b(=disp) connec-   */
/* ting two positions Q1=(a1,d1) and Q2=(a2,d2). If P is the  */
/* the pole of the sphere, then PQ1=c=90-d1, PQ2=a=90-d2.     */
/* Further, the angle PQ1Q2=alpha. Then the distance          */
/* between the two points Q1=(a1,d1) and Q2=(a2,d2) is:       */
/* cos(b)=cos(c)cos(a) + sin(c)sin(a)cos(beta)                */
/* ( where: beta=Q1PQ2=a2-a1 ) ==>                            */ 
/* cos(b)=cos(90-d1)cos(90-d2)+sin(90-d1)sin(90-d2)cos(a2-a1) */
/*                                                            */
/* For sampling on a slice we want to calculate the           */
/* coordinate (a2,d2) for Q2 so that Q1Q2=b. To calculate d2  */
/* use the cosine formula:                                    */
/* cos(a)=cos(b)cos(c) + sin(b)sin(c)cos(alpha)  ==>          */
/* cos(90-d2) = cos(b)cos(90-d1)+sin(b)sin(90-d1)cos(alpha)   */
/*                                                        ==> */
/* sin(d2) = cos(b)sin(d1)+sin(b)cos(d1)cos(alpha)            */
/* which gives d2.                                            */
/* For beta we have the sine formula:                         */
/*                                                            */
/*              sin(b)  = sin(a)                              */
/*              ------    ------                              */
/*           sin(beta)    sin(alpha)                          */
/*                                                            */
/* ==> sin(b)/sin(a2-a1) = sin(90-d2)/sin(alpha) so that:     */
/* sin(a2-a1) = sin(b)sin(alpha)/cos(d2).                     */
/* b,alpha and a1 are known. d2 is calculated -> a2.          */
/*------------------------------------------------------------*/
{
   double   alpha;
   double   b  = ABS(RAD(disp));
   double   a1 = RAD(longitude), a2;
   double   d1 = RAD(latitude),  d2;



   if (disp < 0.0)
      angle += 180.0;

   if (cdelt[0] < 0.0)
   {
      /*----------------------------------------*/
      /* The case where rotation is defined     */
      /* from the Y-axis to the +x axis, i.e.   */
      /* ANTI-clockwise                         */
      /*----------------------------------------*/
      alpha = RAD( 180.0 + angle );
   }
   else
   {
      /*----------------------------------------*/
      /* The case where rotation is defined     */
      /* from the Y-axis to the +x axis, i.e.   */
      /* clockwise                              */
      /*----------------------------------------*/
      alpha = RAD( 360.0 - angle );        /* Complement to PA. receding part */
   }

   d2 = asin( cos(b)*sin(d1)+cos(d1)*sin(b)*cos(alpha) );
   a2 = a1 + asin( sin(b)*sin(alpha)/cos(d2) );

   *longout = DEG(a2);
   *latout  = DEG(d2);

   return( 1 );
}



float **fmatrix( int xlo,
                 int ylo,
                 int xhi,
                 int yhi )
/*------------------------------------------------------------------*/
/* PURPOSE: Allocate space for a matrix with subscript range        */
/*          M[ylo..yhi][xlo..xhi]                                   */
/*------------------------------------------------------------------*/
{
   int      rows = yhi - ylo + 1;
   int      cols = xhi - xlo + 1;
   int      i;
   float    **m;


   /* Allocate memory for pointers to rows */
   m = (float **) malloc( rows * sizeof(float*) );
   if (!m)
      return( NULL );
   m -= ylo;                                /* Adjust subscript */

   /* Pointer to first row allocates memory for box */
   m[ylo] = (float *) malloc( rows * cols * sizeof(float) );
   if (!m[ylo])
       return( NULL );
   m[ylo] -= xlo;                           /* Adjust subscript */

   /* Set pointers to rows */
   for (i = ylo+1; i <= yhi; i++)
      m[i] = m[i-1] + cols;

   /* Return pointer to array of pointers to rows */
   return( m );
}



void freefmatrix( float **m,
                  int   xlo,
                  int   ylo )
/*------------------------------------------------------------------*/
/* PURPOSE: Free space allocated by a matrix made with function     */
/*          'fmatrix'.                                              */
/*------------------------------------------------------------------*/
{
   free( m[ylo] + xlo );
   free( m + ylo );
}




static float getimval( int       xi,
                       int       yi,
                       fint     *blo,
                       fint     *bhi,
                       float   **image )
/*------------------------------------------------------------------*/
/* PURPOSE: Get a value from 'image'. Function can only be used     */
/*          after a call to gdsi_read. The global value of 'fblank' */
/*          must be set.                                            */
/*------------------------------------------------------------------*/
{
   if (xi >= blo[0] && xi <= bhi[0] &&
       yi >= blo[1] && yi <= bhi[1] )
      return( image[yi][xi] );
   else
      return( fblank );
}



float getipval( float      x,
                float      y,
                fint      *blo,
                fint      *bhi,
                float    **image )
/*------------------------------------------------------------*/
/* PURPOSE: Calculate an image value for a non integer        */
/*          position using a bilinear interpolation between 4 */
/*          image values at neighbouring integer positions.   */
/*                                                            */
/* Given 4 values m1,m2,m3,m4 at positions:                   */
/*                                                            */
/*       m4       m3                                          */
/* (0,1) o--------o (1,1)                                     */
/*       |        |                                           */
/*       ^        |                                           */
/*    dy |        |                                           */
/*       | dx     |                                           */
/* (0,0) o---->---o (1,0)                                     */
/*      m1       m2                                           */
/*                                                            */
/* there are a number of interpolation schemes depending on   */
/* the number of blanks. If all pixels are non blank, a bi-   */
/* linear interpolation is involved. If one pixel is blank,   */
/* the interpolation is in the plane of the other (3) pixels. */
/* For two non blanks there are standard linear interpolat-   */
/* ions.                                                      */
/*------------------------------------------------------------*/
{
   float    m1, m2, m3, m4;
   float    dx, dy;
   int      xi, yi;
   int      f1, f2, f3, f4;
   int      dxi, dyi;                                     /* Neighbour pixels */


   setfblank_c( &fblank );
   xi = (int) x;
   yi = (int) y;
   /* Position outside box? */
   if (xi < blo[0] || xi > bhi[0] || yi < blo[1] || yi > bhi[1])
      return( fblank );

   dxi = dyi = 1;
   if (x < 0.0)
      dxi = -1;
   if (y < 0.0)
      dyi = -1;

   dx = ABS(x - (float) xi);
   dy = ABS(y - (float) yi);
   m1 = getimval( xi,     yi,     blo, bhi, image );
   m2 = getimval( xi+dxi, yi,     blo, bhi, image );
   m3 = getimval( xi+dxi, yi+dyi, blo, bhi, image );
   m4 = getimval( xi,     yi+dyi, blo, bhi, image );
   f1 = (m1 != fblank);
   f2 = (m2 != fblank);
   f3 = (m3 != fblank);
   f4 = (m4 != fblank);


   /* Most common situation: All pixels non blank */
   if (f1 && f2 && f3 && f4)
      return m1*(1.0-dx)*(1.0-dy) + m2*dx*(1.0-dy) + m3*dx*dy + m4*(1.0-dx)*dy;

   /* One of the pixels is a blank */
   if (!f1 && f2 && f3 && f4)
      return  (m4-m3)*(1.0-dx) + (m2-m3)*(1.0-dy) + m3;
   if (f1 && !f2 && f3 && f4)
      return  (m3-m4)*dx       + (m1-m4)*(1.0-dy) + m4;
   if (f1 && f2 && !f3 && f4)
      return  (m2-m1)*dx       + (m4-m1)*dy + m1;
   if (f1 && f2 && f3 && !f4)
      return  (m1-m2)*(1.0-dx) + (m3-m2)*dy + m2;

   /* Two non blank pixels along x or y */
   if (f1 && f2)
      return m2*dx + m1*(1-dx);

   if (f2 && f3)
      return m3*dy + m2*(1-dy);
   if (f3 && f4)
      return m3*dx + m4*(1-dx);
   if (f4 && f1)
      return m4*dy + m1*(1-dy);

   /* Two non blank pixels NOT along x or y */
   if (f1 && f3)
      return m1*(1.0-(dx+dy)/2.0) + m3*(dx+dy)/2.0;
   if (f2 && f4)
      return m2*(1.0-(1.0-dx+dy)/2.0) + m4*(1.0-dx+dy)/2.0;

   /* All the pixels, (or all but one) are blank */
   return( m1 );
}




int Vc2Vxy( double   *cpos,
            double    Vsys,
            double    radius,
            double    Vcirc,
            double    phideg,
            double    incdeg,
            double    sl_angledeg,
            double   *sl_off,
            double   *projvel,
            double   *projrad )
/*------------------------------------------------------------*/
/* PURPOSE: Convert circular velocity at radius R to line of  */
/*          sight velocity V_z(xy).                           */
/* Distinguish two situations.                                */
/* 1) A slice through 0,0 and                                 */
/* 2) A slice not through 0,0                                 */
/* 'phi' enters as an angle wrt the +Y axis. Units are degree */
/* 'radius' is the rotation curve radius in arcsec.           */
/*------------------------------------------------------------*/
{
   double   beta[2];
   double   theta[2];
   double   cosinc, sininc;
   double   sl_anglerad;
   double   phirad, incrad;
   int      i;


   if (radius == 0)
   {
      projvel[0] = projvel[1] = Vsys;
      projrad[0] = projrad[1] = 0.0;
      return( YES );
   }
   incrad      = RAD( incdeg );
   cosinc      = cos( incrad );
   sininc      = sin( incrad );
   phirad      = RAD( putinrangedeg(phideg) );
   sl_anglerad = RAD( putinrangedeg(sl_angledeg) );

   /*--------------------------------------------------*/
   /* Calculate the intersections of the slice with    */
   /* the ellipse in the sky.                          */
   /*--------------------------------------------------*/
   {
      double  sina;
      double  cosa;
      double  sinisini;
      double  cosicosi;
      double  A, B, C;
      double  p, q, r;
      double  m, b;                           /* Slice parameters */
      double  det;
      double  pidiv4 = PI / 4.0;
      double  pidiv2 = PI / 2.0;
      double  sqrdet;
      double  x[2], y[2];
      double  alpha, e;
      int     danger;


      alpha = putinrangerad( phirad + pidiv2 ); /* PA of current ring wrt +X-axis */
      sina  = sin( alpha );
      cosa  = cos( alpha );
      sl_anglerad = putinrangerad( sl_anglerad + pidiv2 );
      sinisini = sininc * sininc;
      cosicosi = cosinc * cosinc;

      A = cosa*cosa+sina*sina/cosicosi;
      B = 2.0*cosa*sina-2.0*sina*cosa/cosicosi;
      C = sina*sina+cosa*cosa/cosicosi;

      danger = ( (sl_anglerad >= 1.0*pidiv4 && sl_anglerad <= 3.0*pidiv4) ||
                 (sl_anglerad >= 5.0*pidiv4 && sl_anglerad <= 7.0*pidiv4) );
      if (!danger)
      {
         /* Intersect with a line Y = mX + b */
         m = tan( sl_anglerad );
         b = sl_off[1] - sl_off[0]*m;  /* offsets are from g. to sl. center */ 
         p = A + B*m + C*m*m;
         q = B*b + 2.0*m*b*C;
         r = C*b*b - radius*radius;
      }
      else
      {
         /* Intersect with a line X = mY + b */
         m = tan( PI/2.0 - sl_anglerad );
         b = sl_off[0] - sl_off[1]*m;
         p = A*m*m + B*m + C;
         q = B*b + 2.0*m*b*A;
         r = A*b*b - radius*radius;
      }

      det = q*q - 4.0*p*r;
      if (det < 0.0)
      {
         
         anyoutf( DEBUG, "DEBUG (vc2vxy):No intersections slice and ellipse in sky for slice" );
         anyoutf( DEBUG, "               with angle %g (deg) and offset (%g %g) user units",
                  sl_angledeg , sl_off[0], sl_off[1]  );
         anyoutf( DEBUG, "               Disk parameters: radius=%g (user units), pa=%g (deg), inc=%g (deg)",
                  radius, phideg, incdeg );
         return( NO );
      }
      sqrdet = sqrt(det);
      if (!danger)
      {
         x[0] = (-1.0*q + sqrdet) / (2.0*p);
         x[1] = (-1.0*q - sqrdet) / (2.0*p);
         y[0] = m*(x[0]) + b;
         y[1] = m*(x[1]) + b;
      }
      else
      {
         y[0] = (-1.0*q + sqrdet) / (2.0*p);
         y[1] = (-1.0*q - sqrdet) / (2.0*p);
         x[0] = m*(y[0]) + b;
         x[1] = m*(y[1]) + b;
      }

      for (i = 0; i < 2; i++)
      {
         double xt, yt;
         double Vz;

         e = arctan( y[i], x[i] );                /* This is an angle wrt the X axis */
         beta[i]  = putinrangerad( e - alpha );
         if (cosinc == 0.0)
            Vz = 0.0;
         else
         {
            theta[i] = arctan( fabs(tan(beta[i])), fabs(cosinc) );
            Vz = sign(cos(beta[i])) * Vcirc * cos(theta[i]) * sininc;
         }
         projvel[i] = Vsys + Vz;
         /*--------------------------------------------------*/
         /* Rotate slice line so that it aligns with the x-  */
         /* axis. Then the x value of the rotated x[i],y[i]  */
         /* gives size and sign(!) of the offset in the      */
         /* slice. Note that we need to move the slice       */
         /* center to the galaxy center first.               */
         /*--------------------------------------------------*/         
         xt = x[i] - sl_off[0];
         yt = y[i] - sl_off[1];
         projrad[i] = -1.0*(xt*cos(-sl_anglerad) - yt*sin(-sl_anglerad));
      }
   }
   return( YES );
}



static void copyaxis( fchar Setin,
                      int   inpaxisnum,
                      fchar Setout,
                      int   outaxisnum )
/*------------------------------------------------------------*/
/* PURPOSE: Copy CDELT, DDELT, DTYPE from source axis         */
/*          (inpaxisnum) to destination (outaxisnum).         */
/*------------------------------------------------------------*/
{
   fchar     Ctype, Cunit;
   fchar     Dtype, Dunit;
   double    cdelt, ddelt;
   double    crpix;
   double    crval, drval;
   fint      naxis;
   fint      r1;
   char      message[128];


   (void) sprintf( message, "CRPIX%d", inpaxisnum );
   r1 = 0;
   gdsd_rdble_c( Setin, tofchar(message), &setlevel, &crpix, &r1 );

   (void) sprintf( message, "NAXIS%d", inpaxisnum );
   r1 = 0;
   gdsd_rint_c( Setin, tofchar(message), &setlevel, &naxis, &r1 );

   fmake( Ctype, FITSLEN );
   (void) sprintf( message, "CTYPE%d", inpaxisnum );
   r1 = 0;
   gdsd_rchar_c( Setin, tofchar(message), &setlevel, Ctype, &r1 );

   r1 = 0;
   gds_extend_c( Setout, Ctype, &crpix, &naxis, &r1 );

   fmake( Cunit, FITSLEN );
   (void) sprintf( message, "CUNIT%d", inpaxisnum );
   r1 = 0;
   gdsd_rchar_c( Setin, tofchar(message), &setlevel, Cunit, &r1 );
   if (r1 == 0)
   {
      (void) sprintf( message, "CUNIT%d", outaxisnum );
      gdsd_wchar_c( Setout, tofchar(message), &setlevel, Cunit, &r1 );
   }

   (void) sprintf( message, "CRVAL%d", inpaxisnum );
   r1 = 0;
   gdsd_rdble_c( Setin, tofchar(message), &setlevel, &crval, &r1 );
   if (r1 == 0)
   {
      (void) sprintf( message, "CRVAL%d", outaxisnum );
      gdsd_wdble_c( Setout, tofchar(message), &setlevel, &crval, &r1 );
   }


   (void) sprintf( message, "CDELT%d", inpaxisnum );
   r1 = 0;
   gdsd_rdble_c( Setin, tofchar(message), &setlevel, &cdelt, &r1 );
   if (r1 == 0)
   {
      (void) sprintf( message, "CDELT%d", outaxisnum );
      gdsd_wdble_c( Setout, tofchar(message), &setlevel, &cdelt, &r1 );
   }

   /* Secondary info for second axis */

   fmake( Dtype, FITSLEN );
   (void) sprintf( message, "DTYPE%d", inpaxisnum );
   r1 = 0;
   gdsd_rchar_c( Setin, tofchar(message), &setlevel, Dtype, &r1 );
   if (r1 == 0)
   {
      (void) sprintf( message, "DTYPE%d", outaxisnum );
      gdsd_wchar_c( Setout, tofchar(message), &setlevel, Dtype, &r1 );
   }
   
   r1 = 0;
   fmake( Dunit, FITSLEN );
   (void) sprintf( message, "DUNIT%d", inpaxisnum );
   gdsd_rchar_c( Setin, tofchar(message), &setlevel, Dunit, &r1 );
   if (r1 == 0)
   {
      (void) sprintf( message, "DUNIT%d", outaxisnum );
      gdsd_wchar_c( Setout, tofchar(message), &setlevel, Dunit, &r1 );
   }

   (void) sprintf( message, "DRVAL%d", inpaxisnum );
   r1 = 0;
   gdsd_rdble_c( Setin, tofchar(message), &setlevel, &drval, &r1 );
   if (r1 == 0)
   {
      (void) sprintf( message, "DRVAL%d", outaxisnum );
      gdsd_wdble_c( Setout, tofchar(message), &setlevel, &drval, &r1 );
   }
   
   (void) sprintf( message, "DDELT%d", inpaxisnum );
   r1 = 0;
   gdsd_rdble_c( Setin, tofchar(message), &setlevel, &ddelt, &r1 );
   if (r1 == 0)
   {
      (void) sprintf( message, "DDELT%d", outaxisnum );
      gdsd_wdble_c( Setout, tofchar(message), &setlevel, &ddelt, &r1 );
   }
}




void writeslices( globaltype *g )
/*------------------------------------------------------------*/
/* PURPOSE: Write the XV maps to the output set.              */
/* Create a set from scratch. The output has 3 axes. The      */
/* first is an offset axis (PARAM-OFFST) in 'Xunits', the     */
/* second is the V-axis, usually velocity. The third axis is  */
/* a counter for the subsets (PARAM-SLICE).                   */
/*------------------------------------------------------------*/
{ 
   fint     r;
   char     errmes[128];
   fchar    Ctype, Bunit;
   double   crpix, cdelt, crval;
   fint     naxis;
   fint     axnum;
   int      subnr;
   fint     cwloO, cwhiO;     
   fint     tidO;
   
  
 
   gds_create_c( g->outset.Name, &r );
   if (r < 0)
   {
      sprintf( errmes, "GDS error nr %d", r );
      error_status("Cannot create this set!", errmes, "" );         
      g->outset.exist = NO;
      return;    
   }
   
   crpix = -g->slicecom.xloallgr + 1;
   naxis = g->slicecom.xhiallgr - g->slicecom.xloallgr + 1; /* Same size for all */    
   fmake( Ctype, FITSLEN );
   strcpy( Ctype.a, "PARAM-OFFSET" );
   r = 0;   
   gds_extend_c( g->outset.Name, Ctype, &crpix, &naxis, &r ); 
   r = 0;
   gdsd_wchar_c( g->outset.Name, tofchar("CUNIT1"), &setlevel,
                 g->slicecom.XUnits, &r );
   cdelt = g->slicecom.delta;
   r = 0;
   gdsd_wdble_c( g->outset.Name, tofchar("CDELT1"), &setlevel, &cdelt, &r );
   crval = 0.0;
   r = 0;
   gdsd_wdble_c( g->outset.Name, tofchar("CRVAL1"), &setlevel, &crval, &r );
      
   axnum = g->inset.axnum[ g->inset.setdim-1 ];
   copyaxis( g->inset.Name, axnum, g->outset.Name, 2 );


   /* Third axis new set */
   crpix = 0;
   naxis = g->slicecom.numslices;
   gds_extend_c( g->outset.Name, tofchar("PARAM-SLICE"), &crpix, &naxis, &r );
   r = 0;
   gdsd_wchar_c( g->outset.Name, tofchar("CUNIT3"), &setlevel,
                 tofchar("SLICENUM"), &r );
   cdelt = 1.0;
   r = 0;
   gdsd_wdble_c( g->outset.Name, tofchar("CDELT3"), &setlevel, &cdelt, &r );
   crval = 0.0;
   r = 0;
   gdsd_wdble_c( g->outset.Name, tofchar("CRVAL3"), &setlevel, &crval, &r );


   /* Get units of the data */
   r = 0;
   fmake( Bunit, FITSLEN );
   gdsd_rchar_c( g->inset.Name, tofchar("BUNIT"), &setlevel, Bunit, &r );
   if (r == 0)
      gdsd_wchar_c( g->outset.Name, tofchar("BUNIT"), &setlevel, Bunit, &r );
      
   r = 0;
   gdsc_range_c( g->outset.Name, &setlevel, &cwloO, &cwhiO, &r ) ;
   tidO = 0;   
     
   /* Write the data */    
   for(subnr = 0; subnr < g->slicecom.numslices; subnr++)
   {
      fint   pixelsdone;
      /* Note that idim is not equal to g->slicecom.ndatmax ! */
      fint   idim = g->slicecom.xhiallgr - g->slicecom.xloallgr + 1;      
      fint   numpoints = idim * g->inset.nsubs;

      gdsi_write_c( g->outset.Name,
                    &cwloO, &cwhiO,
                    g->slice[subnr].image,
                    &numpoints,
                    &pixelsdone,
                    &tidO );      
   }
}
#<

#>            colormenu.c
/* colormenu.c
                              COPYRIGHT (c) 1999
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands

Author: J.P. Terlouw
*/

#define  CLWID 100

#include "stddef.h"
#include "stdio.h"
#include "gipsyc.h"
#include "userfio.h"
#include "ggi.h"
#include "colormenu.h"

static char *colors[]={"background",
                       "foreground",
                       "red",
                       "green",
                       "blue",
                       "cyan",
                       "magenta",
                       "yellow",
                       "orange",
                       "greenyellow",
                       "greencyan",
                       "bluecyan",
                       "bluemagenta",
                       "redmagenta",
                       "darkgray",
                       "lightgray",
                       NULL}; 
                                                                                                                                                                                                               

/* -------------------------------------------------------------------------- */
/*                                 col_text                                   */
/* -------------------------------------------------------------------------- */
/* Keyword handler for setting labels in the color menus.
 */
static void col_text(ident id, char *key, int code, void *arg)
{  
   fint index;
    
   (void)userfint(&index, 1, 2, key, " ");
   GgiSetLabel((ident)arg, colors[index], CLWID);
}

/* ========================================================================== */
/*                                  ColorMenu                                 */
/* -------------------------------------------------------------------------- */
ident ColorMenu(char *key, char *label, char *help)
{
   ident result;
   char  deflab[KEYLEN];
   
   strcpy(deflab, key);
   deflab[strlen(deflab)-1] = '\0';
   result = GgiSetLabel(GgiMenu(key, help, colors), label?label:deflab, CLWID);
   (void)ScheduleKeyevent(col_text, key, KEYCHANGE, result);
   
   return result;
}
#<

#>            rotpar.c
/* rotpar.c
                              COPYRIGHT (c) 1999
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands

Author: J.P. Terlouw
*/

#define NAME "Tilted Ring parameters"
#define WIDTH 475

#include "stddef.h"
#include "stdio.h"
#include "gipsyc.h"
#include "userfio.h"
#include "ggi.h"
#include "helptext.h"
#include "rotpar.h"

#define H_TEXT "Text file name"
#define H_ROWS "Row numbers:\n\n first : last\n first :\n : last\n   :  (=all rows)"
#define H_FRC  "Radii column number"
#define H_FVC  "Velocity column number"
#define H_FIC  "Inclination column number"
#define H_FPC  "Position angle column number"
#define H_SET  "Set or subset containing table"
#define H_TAB  "Table name"
#define H_SRC  "Radii column name"
#define H_SVC  "Velocity column name"
#define H_SIC  "Inclination column name"
#define H_SPC  "Position angle column name"

static char *datanames[]={"RADII", "VELOCITY", "INCLINATION", "POSANG", NULL};

/* -------------------------------------------------------------------------- */
/*                                 filedata                                   */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for IMP_FILE=, IMP_FROWS= and xxxCOL=.
 *  Convert file, row and column information to 'standard' keywords.
 */
static void filedata(ident id, char *key, int code, void *arg)
{
   static bool initialized=FALSE;
   static char file[64]="";
   static char rows[64]="";
   fchar  data;
   int    nc;
   char   **name;
   char   *dataname=(char*)arg;
   char keybuf[KEYLEN+1];
   char col[16+1];

   data.l = 63;
   if (!initialized) {
      data.a = file;
      nc = userftext(data, 2, "IMP_FILE=", " ");
      data.a[nc] = '\0';
      data.a = rows;
      nc = userftext(data, 2, "IMP_FROWS=", " ");
      data.a[nc] = '\0';
      initialized = TRUE;
   }
   data.a = NULL;
   if (!strcmp(key,"IMP_FILE=")) {
      data.a = file;
   } else if (!strcmp(key,"IMP_FROWS=")) {
      data.a = rows;
   }
   if (data.a) {
                                        /* IMP_FILE= or IMP_FROWS= keyword seen */
      nc = userftext(data, 2, key, " ");
      data.a[nc] = '\0';
      if (!nc && data.a==rows) strcpy(rows,":");
      if (*file && *rows) {
         data.l=16;
         data.a = col;
         for (name=datanames; *name; name++) {
            sprintf(keybuf,"%s_COL=", *name);
            nc = userftext(data, 2, keybuf, " ");
            if (nc) {
               col[nc] = '\0';
               wkeyf("%s=file(%s,%s,%s)", *name, file, col, rows);
            }
         }
      }
   } else {
                                          /* xxx_COL= keyword seen */
      if (*file && *rows) {
         data.l=16; 
         data.a = col;
         nc = userftext(data, 2, key, " ");
         if (nc) {
            col[nc] = '\0';
            wkeyf("%s=file(%s,%s,%s)", dataname, file, col, rows);
         } else {
            wkeyf("%s=", dataname);
         }
      }
   }
}

/* -------------------------------------------------------------------------- */
/*                                 tabledata                                  */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for IMP_SET=, IMP_TAB=, IMP_TROWS= and xxx_TCOL=.
 *  Convert set, table, row and column information to 'standard' keywords.
 */
static void tabledata(ident id, char *key, int code, void *arg)
{
   static bool initialized=FALSE;
   static char set[64]="";
   static char table[64]="";
   static char rows[64]="";
   fchar  data;
   int    nc;
   char   **name;
   char   *dataname=(char*)arg;
   char keybuf[KEYLEN+1];
   char col[16+1];

   data.l = 63;
   if (!initialized) {
      data.a = set;
      nc = userftext(data, 2, "IMP_SET=", " ");
      data.a[nc] = '\0';
      data.a = table;
      nc = userftext(data, 2, "IMP_TAB=", " ");
      data.a[nc] = '\0';
      data.a = rows;
      nc = userftext(data, 2, "IMP_TROWS=", " ");
      data.a[nc] = '\0';
      initialized = TRUE;
   }
   data.a = NULL;
   if (!strcmp(key,"IMP_SET=")) {
      data.a = set;
   } else if (!strcmp(key,"IMP_TAB=")) {
      data.a = table;
   } else if (!strcmp(key,"IMP_TROWS=")) {
      data.a = rows;
   }
   if (data.a) {
                                  /* IMP_SET, IMP_TAB or IMP_TROWS= keyword seen */
      nc = userftext(data, 2, key, " ");
      data.a[nc] = '\0';
      if (!nc && data.a==rows) strcpy(rows,":");
      if (*set && *table && *rows) {
         data.l=16;
         data.a = col;   
         for (name=datanames; *name; name++) {
            sprintf(keybuf,"%s_TCOL=", *name);
            nc = userftext(data, 2, keybuf, " ");
            if (nc) {
               col[nc] = '\0';
               wkeyf("%s=table(%s,%s,%s,%s)",  *name, set, table, col, rows);
            }
         }
      }
   } else {
                                          /* xxx_TCOL= keyword seen */
      if (*set && *table && *rows) {
         data.l=16;
         data.a = col;
         nc = userftext(data, 2, key, " ");
         if (nc) {
            col[nc] = '\0';
            wkeyf("%s=table(%s,%s,%s,%s)", dataname, set, table, col, rows);
         } else {
            wkeyf("%s=", dataname);
         }
      }
   }
}

/* -------------------------------------------------------------------------- */
/*                                 popshell                                   */
/* -------------------------------------------------------------------------- */
/*  Keyword handler causing the shell to be popped up or down.
 */
static void popshell(ident id, char *key, int code, void *arg)
{  
   static bool state=FALSE;
   bool button=toflog(FALSE);
 
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      GgiShowShell((ident)arg, !state);
      state = !state;
      wkeyf(key);
   }
}

/* ========================================================================== */
/*                                 Rotpar                                     */
/* -------------------------------------------------------------------------- */
/*  Rotpar() creates a pop-up shell for obtaining rotation curve parameters.
 */
void Rotpar(char *key)
{
   static ident s_rotpar;
   ident units_box;
   ident s_prev, frame_box, file_box, table_box, generic_box, limits_box;
   ident closebut;
   char **name;
   char keybuf[KEYLEN+1];
    
   s_rotpar = GgiShell(NAME);
   s_prev = GgiUseShell(s_rotpar);
   frame_box = GgiForm(NAME, 2);
   closebut = GgiSetLabel(GgiButton(key, "Close window"), "CLOSE", 0);
   GgiSetPosition(frame_box, 0, NULL, 0, NULL);
   GgiSetPosition(closebut, 0, NULL, 0, frame_box);

   GgiUseShell(frame_box);
   units_box   = GgiForm(NAME, 1);
   file_box    = GgiForm(NAME, 1);
   table_box   = GgiForm(NAME, 1);
   generic_box = GgiForm(NAME, 1);
   limits_box  = GgiForm(NAME, 1);
   
   GgiSetPosition(units_box, 0, NULL, 0, NULL);
   GgiSetPosition(file_box, 0, NULL, 0, units_box);
   GgiSetPosition(table_box, 0, NULL, 0, file_box);
   GgiSetPosition(generic_box, 0, NULL, 0, table_box);
   GgiSetPosition(limits_box, 0, NULL, 0, generic_box);

   {
      ident title, vunits, xunits;
      (void)GgiUseShell(units_box);
      title = GgiLabel("Units used in Tilted Ring input\n ");
      xunits   = GgiSetLabel(GgiTextField("TRXUNITS=", H_TRXUNITS,   10),
                             "Radii units", 90);
      vunits   = GgiSetLabel(GgiTextField("TRVUNITS=",   H_TRVUNITS,   10),
                             "Velocity units", 0);
      
      GgiSetPosition(title,  0, NULL,   0, NULL);
      GgiSetPosition(xunits, 0, NULL,   0, title);
      GgiSetPosition(vunits, 0, xunits, 0, title);
      GgiSetPosition(GgiCanvas(" ",1,1), WIDTH, NULL, 0, NULL);
   }

   {
      ident title, file, rows, rcol, vcol, icol, pcol;
      (void)GgiUseShell(file_box);
      title = GgiLabel("Input from text file\n ");
      file = GgiSetLabel(GgiTextField("IMP_FILE=", H_TEXT, 40),
                             "Filename", 99);
      rcol = GgiSetLabel(GgiTextField("RADII_COL=", H_FRC, 2),
                          "Column Radii", 99);
      vcol = GgiSetLabel(GgiTextField("VELOCITY_COL=", H_FVC, 2),
                          "Vel.", 50);
      icol = GgiSetLabel(GgiTextField("INCLINATION_COL=", H_FIC, 2),
                          "Incl.", 50);
      pcol = GgiSetLabel(GgiTextField("POSANG_COL=", H_FPC, 2),
                          "Phi", 50);
      rows = GgiSetLabel(GgiTextField("IMP_FROWS=", H_ROWS, 8),
                          "Rows", 99);

      GgiSetPosition(title, 0, NULL, 0, NULL);
      GgiSetPosition(file, 0, NULL, 0, title);
      GgiSetPosition(rcol, 0, NULL, 0, file);
      GgiSetPosition(vcol, 0, rcol, 0, file);
      GgiSetPosition(icol, 0, vcol, 0, file);
      GgiSetPosition(pcol, 0, icol, 0, file);
      GgiSetPosition(rows, 0, NULL, 0, rcol);
      GgiSetPosition(GgiCanvas(" ",1,1), WIDTH, NULL, 0, NULL);

      (void)ScheduleKeyevent(filedata, "IMP_FILE=", KEYCHANGE, NULL); 
      (void)ScheduleKeyevent(filedata, "IMP_FROWS=", KEYCHANGE, NULL);
      for (name=datanames; *name; name++) {
         sprintf(keybuf, "%s_COL=", *name);
         (void)ScheduleKeyevent(filedata, keybuf, KEYCHANGE, *name);
      }
   }

   {
      ident title, set, table,  rows, rcol, vcol, icol, pcol;
      (void)GgiUseShell(table_box);
      title = GgiLabel("Input from table\n ");
      set  = GgiSetLabel(GgiTextField("IMP_SET=", H_SET, 40),
                             "Set", 99);
      table= GgiSetLabel(GgiTextField("IMP_TAB=", H_TAB, 40),
                             "Table", 99);
      rcol = GgiSetLabel(GgiTextField("RADII_TCOL=", H_SRC, 8),
                          "Column Radii", 99);
      vcol = GgiSetLabel(GgiTextField("VELOCITY_TCOL=", H_SVC, 8),
                          "Vel.", 0);
      icol = GgiSetLabel(GgiTextField("INC_TCOL=", H_SIC, 8),
                          "Incl.", 0);
      pcol = GgiSetLabel(GgiTextField("PHI_TCOL=", H_SPC, 8),
                          "Phi", 0);
      rows = GgiSetLabel(GgiTextField("IMP_TROWS=", H_ROWS, 8),
                          "Rows", 99);
       
      GgiSetPosition(title, 0, NULL, 0, NULL);
      GgiSetPosition(set,   0, NULL, 0, title);
      GgiSetPosition(table, 0, NULL, 0, set);
      GgiSetPosition(rcol,  0, NULL, 0, table);
      GgiSetPosition(vcol,  0, rcol, 0, table);
      GgiSetPosition(icol,  0, vcol, 0, table);
      GgiSetPosition(pcol,  0, icol, 0, table);
      GgiSetPosition(rows,  0, NULL, 0, rcol);
      GgiSetPosition(GgiCanvas(" ",1,1), WIDTH, NULL, 0, NULL);

      (void)ScheduleKeyevent(tabledata, "IMP_SET=",   KEYCHANGE, NULL);
      (void)ScheduleKeyevent(tabledata, "IMP_TAB=",   KEYCHANGE, NULL);
      (void)ScheduleKeyevent(tabledata, "IMP_TROWS=", KEYCHANGE, NULL);
      for (name=datanames; *name; name++) {
         sprintf(keybuf, "%s_TCOL=", *name);
         (void)ScheduleKeyevent(tabledata, keybuf, KEYCHANGE, *name);
      }
   }
   
   {
      ident title, radii, vel, inc, phi;
      (void)GgiUseShell(generic_box);
      title = GgiLabel("Generic input format\n ");
      radii = GgiSetLabel(GgiTextField("RADII=", H_RADII, 40),
                            "Radii", 99);
      vel   = GgiSetLabel(GgiTextField("VELOCITY=", H_VELOCITY, 40),
                            "Velocities", 99);
      inc   = GgiSetLabel(GgiTextField("INCLINATION=",  H_INC, 40),
                            "Inclinations", 99);
      phi   = GgiSetLabel(GgiTextField("POSANG=",  H_PHI, 40),
                            "Position angles", 99);
      GgiSetPosition(title, 0, NULL, 0, NULL);
      GgiSetPosition(radii, 0, NULL, 0, title);
      GgiSetPosition(vel,   0, NULL, 0, radii);
      GgiSetPosition(inc,   0, NULL, 0, vel);
      GgiSetPosition(phi,   0, NULL, 0, inc);
      GgiSetPosition(GgiCanvas(" ",1,1), WIDTH, NULL, 0, NULL);
   }
   
   {
      ident title, radlim, vellim, inclim, philim;
      (void)GgiUseShell(limits_box);
      title  = GgiLabel("Limits\n ");
      radlim = GgiSetLabel(GgiTextField("RADMINMAX=", H_RADMINMAX, 12),
                           "Radii min & max", 180);
      vellim = GgiSetLabel(GgiTextField("VELMINMAX=", H_VELMINMAX, 12),
                           "Velocities min & max", 180);
      inclim = GgiSetLabel(GgiTextField("INCMINMAX=", H_INCMINMAX, 12),
                           "Inclinations min & max", 180);
      philim = GgiSetLabel(GgiTextField("PHIMINMAX=", H_PHIMINMAX, 12),
                           "Position angles min & max", 180);
      GgiSetPosition(title,  0, NULL,   0, NULL);
      GgiSetPosition(radlim, 0, NULL,   0, title);
      GgiSetPosition(vellim, 0, NULL,   0, radlim);
      GgiSetPosition(inclim, 0, NULL,   0, vellim);
      GgiSetPosition(philim, 0, NULL,   0, inclim);
      GgiSetPosition(GgiCanvas(" ",1,1), WIDTH, NULL, 0, NULL);
   }
   
   (void)GgiUseShell(s_rotpar);
   (void)GgiUseShell(s_prev);
   ScheduleKeyevent(popshell, key, KEYCHANGE, s_rotpar);
}
#<

#>            input.c
/* input.c
                              COPYRIGHT (c) 1999
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands

Author: J.P. Terlouw
*/

#define NAME "Input"
#define WIDTH 589
#define HEIGHT23 125

#include "stddef.h"
#include "stdio.h"
#include "gipsyc.h"
#include "userfio.h"
#include "ggi.h"
#include "colormenu.h"
#include "helptext.h"
#include "radiogroup.h"
#include "input.h"

static ident kader2, kader3;

/* -------------------------------------------------------------------------- */
/*                                 popshell                                   */
/* -------------------------------------------------------------------------- */
/*  Keyword handler causing the shell to be popped up or down.
 */
static void popshell(ident id, char *key, int code, void *arg)
{  
   static bool state=FALSE;
   bool button=toflog(FALSE);
 
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      GgiShowShell((ident)arg, !state);
      state = !state;
      wkeyf(key);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 gui_set_units                              */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for setting units-like strings.
 */
static void gui_set_units(ident id, char *key, int code, void *arg)
{
   fchar ftext;
   char  ctext[41];
   char  ltext[43];
   int   nread;

   ftext.a = ctext; ftext.l = 40;
   nread = userftext(ftext, 2, key, " ");
   ctext[nread] = '\0';
   sprintf(ltext,"(%s)", ctext);
   GgiSetLabel((ident)arg, ltext, 0);
}

/* -------------------------------------------------------------------------- */
/*                                 switch_mode                                */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for enabling/disabling "galactic mode".
 */
static void switch_mode(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
   (void)userflog(&button, 1, 2, key, " ");
   if (button) {
      GgiActivate(kader2);
      GgiDeactivate(kader3);
   } else {
      GgiDeactivate(kader2);
      GgiActivate(kader3);
   }
}
 
/* -------------------------------------------------------------------------- */
/*                                 pop_compose                                */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for popping up "inset composer"
 */
static void pop_compose(ident id, char *key, int code, void *arg)
{
   char *setkey=(char*)arg;
   char namekey[KEYLEN];
   bool button=toflog(FALSE);
   ident composer;
   
   (void)userflog(&button, 1, 2, key, " ");
   if (button) {
      wkeyf(key);
      sprintf(namekey, "NAME_%s", setkey);
      composer = GgiInset(ggiIdent, namekey);
      if (composer) {
         (void)GgiInset(ggiDelete, composer);
      } else {
         char *message;
         char *box=NULL;
         
         if (!strcmp(setkey, "INSET=")) {
            message = "Subsets and box for slices";
            box     = "BOX=";
         } else if (!strcmp(setkey, "CONSET=")) {
            message = H_CONSET;
         } else {
            message = H_VELSET;
         }
         composer = GgiInset(ggiCreate, namekey, setkey, box, message);
         (void)GgiInset(ggiNdims, composer, 2);
      }
   }
}
 
/* ========================================================================== */
/*                                  Input                                     */
/* -------------------------------------------------------------------------- */
/*   Input() creates a pop-up shell with menus for obtaining inputed data.
 */
void  Input(char *key)
{
   static ident s_input;
   ident s_prev, frame_box, kader1, kader4;
   ident inset, box, clip, clunits, conset, contours, collab, colmenu, velset;
   ident compose, concomp, velcomp;
   ident skymode, galmode, slx0y0, fixinc, inunits, fixphi, phunits, 
         galpos, angle, galangle, anunits, ganunits, gtext, sample, saunits,
         sysvel, svunits, oversub, untlab, vunits, xunits;
   ident closebut, slices, plotbut;
   ident group;
    
   s_input = GgiShell(NAME);
   s_prev  = GgiUseShell(s_input);
   frame_box = GgiForm(NAME, 2);
   slices   = GgiSetLabel(GgiButton("GIDSSLICES=", H_GIDSSLICES), "SLICES", 0);
   plotbut  = GgiSetLabel(GgiButton("EXTRACT=", H_EXTRACT), "PLOT", 0);
   closebut = GgiSetLabel(GgiButton(key, "Close window"), "CLOSE", 0);
   GgiSetPosition(frame_box, 0, NULL, 0, NULL);
   GgiSetPosition(closebut, 0, NULL, 0, frame_box);
   GgiSetPosition(plotbut, -GgiWidth(plotbut), frame_box, 0, frame_box);
   GgiSetPosition(slices, -GgiWidth(slices)-GgiWidth(plotbut)-4, plotbut,
                          0, frame_box);
   
   GgiUseShell(frame_box);
   kader1 = GgiForm(NAME, 1);
   kader2 = GgiForm(NAME, 1);
   kader3 = GgiForm(NAME, 1);
   kader4 = GgiForm(NAME, 1);
   skymode   = GgiButton("SKYMODE=", NULL);
   GgiSetLabel(skymode, "SKY", 65);
   galmode   = GgiButton("GALMODE=", NULL);
   GgiSetLabel(galmode, "GALAXY", 65);
   
   GgiSetPosition(kader1,  0, NULL,    0, NULL);
   GgiSetPosition(kader4,  0, NULL,    0, kader1);
   GgiSetPosition(skymode, 0, NULL,   10, kader4);
   GgiSetPosition(kader2,  0, NULL,   -1, skymode);
   GgiSetPosition(galmode, 0, kader2, 10, kader4); 
   GgiSetPosition(kader3,  0, kader2, -1, galmode);
   
   group = RadioGroup("SKYGAL=");
   RadioAddKey(group, "SKYMODE=", "YES");
   RadioAddKey(group, "GALMODE=", "NO");
   (void)ScheduleKeyevent(switch_mode, "SKYGAL=", KEYCHANGE, NULL);
   (void)wkeyf("SKYMODE=YES");
   

/*
 *                        T O P   F R A M E
 */
   GgiUseShell(kader1);
   GgiSetPosition(GgiCanvas("TR", 1, 1), WIDTH, NULL, 0, NULL);
   inset    = GgiTextField("INSET=",    H_INSET,    52);
   GgiSetLabel(inset, "Inset", 85);
   box      = GgiTextField("BOX=",      H_BOX,      52);
   GgiSetLabel(box, "Box", 85);
   compose  = GgiButton("COMPOSE=", "Compose set/subsets and box");
   GgiSetLabel(compose, " \nCOMPOSE\n ", 0);
   (void)ScheduleKeyevent(pop_compose, "COMPOSE=", KEYCHANGE, "INSET=");
   galpos   = GgiTextField("GALCENTRE=", "Position of central disk", 20);
   GgiSetLabel(galpos, "Galaxy cen.", 85);
   clip     = GgiTextField("CLIP=",     H_CLIP,     20);
   GgiSetLabel(clip, "Clip", 85);
   clunits  = GgiLabel("( )");
   conset   = GgiTextField("CONSET=",   H_CONSET,   52);
   GgiSetLabel(conset, "Contour set", 85);
   concomp  = GgiButton("CONCOMPOSE=", "Compose set/subsets");
   GgiSetLabel(concomp, "COMPOSE", 0);
   (void)ScheduleKeyevent(pop_compose, "CONCOMPOSE=", KEYCHANGE, "CONSET=");
   contours = GgiTextField("CONTOURS=", H_CONTOURS, 20);
   GgiSetLabel(contours, "Contours", 85);
   collab   = GgiLabel("Contours colour:");
   colmenu  = ColorMenu("CONCOL=", ". . . . .", NULL);
   velset   = GgiTextField("VELSET=",   H_VELSET,   52);
   GgiSetLabel(velset, "Velocity set", 85);
   velcomp  = GgiButton("VELCOMPOSE=", "Compose set/subsets");
   GgiSetLabel(velcomp, "COMPOSE", 0);
   (void)ScheduleKeyevent(pop_compose, "VELCOMPOSE=", KEYCHANGE, "VELSET=");
   GgiSetPosition(inset,    0, NULL,     0, NULL);
   GgiSetPosition(box,      0, NULL,     0, inset);
   GgiSetPosition(compose,  0, inset,    0, NULL);
   GgiSetPosition(galpos,   0, NULL,     0, box);
   GgiSetPosition(clip,     0, NULL,     0, galpos);
   GgiSetPosition(clunits,  0, clip,     0, galpos);

   GgiSetPosition(conset,   0, NULL,    15, clip);
   GgiSetPosition(concomp,  0, conset,   15, clip);
   GgiSetPosition(contours, 0, NULL,     0, conset);
   GgiSetPosition(collab,  10, contours, 0, conset);
   GgiSetPosition(colmenu,  0, collab,   0, conset);

   GgiSetPosition(velset,   0, NULL,    15, contours);
   GgiSetPosition(velcomp,  0, velset,  15, contours);

/*
 *                        S E C O N D    F R A M E
 */
   GgiUseShell(kader4);
   GgiSetPosition(GgiCanvas("TR", 1, 1), WIDTH, NULL, 0, NULL);
   untlab = GgiLabel("Units used in Slice input\n ");
   xunits = GgiSetLabel(GgiTextField("XUNITS=", H_XUNITS,   10),
                        "Offset units", 90);
   vunits = GgiSetLabel(GgiTextField("VUNITS=", H_VUNITS,   10),
                        "Velocity units", 0);
   GgiSetPosition(untlab,  0, NULL,   0, NULL);
   GgiSetPosition(xunits,  0, NULL,   0, untlab);
   GgiSetPosition(vunits,  0, xunits, 0, untlab);

/*
 *                        L E F T    F R A M E
 */
   GgiUseShell(kader2);
   GgiSetPosition(GgiCanvas("TR", 1, 1), 0, NULL, HEIGHT23, NULL);
   angle    = GgiTextField("SKYANGLES=", H_ANGLE, 20);
   GgiSetLabel(angle, "Slice angles", 85);
   anunits  = GgiLabel("(deg.)");
   slx0y0   = GgiTextField("SLICEX0Y0=", H_SLICEX0Y0, 10);
   GgiSetLabel(slx0y0, "Slices centre", 85);

   GgiSetPosition(angle,    0, NULL,     0, NULL);
   GgiSetPosition(anunits,  0, angle,    0, NULL);
   GgiSetPosition(slx0y0,   0, NULL,     0, angle);


/*
 *                        R I G H T    F R A M E
 */
   GgiUseShell(kader3);
   GgiSetPosition(GgiCanvas("TR", 1, 1), 0, NULL, HEIGHT23, NULL);
   galangle  = GgiTextField("GALANGLES=", H_ANGLE, 20);
   GgiSetLabel(galangle, "Slice angles", 85);
   ganunits  = GgiLabel("(deg.)");
   gtext     = GgiLabel("Convert to sky with:");
   fixinc   = GgiTextField("FIXINC=", H_FIXINC, 10);
   GgiSetLabel(fixinc, "Inclination", 75);
   inunits  = GgiLabel("(deg.)");
   fixphi   = GgiTextField("FIXPHI=", H_FIXPHI, 10);
   GgiSetLabel(fixphi, "Pos. angle", 75);
   phunits  = GgiLabel("(deg.)");

   GgiSetPosition(galangle,    0, NULL,     0, NULL);
   GgiSetPosition(ganunits,    0, galangle, 0, NULL);
   GgiSetPosition(gtext,      40, NULL,    20, galangle);
   GgiSetPosition(fixinc,     80, NULL,     0, gtext);
   GgiSetPosition(inunits,     0, fixinc,   0, gtext);
   GgiSetPosition(fixphi,     80, NULL,     0, fixinc);
   GgiSetPosition(phunits,     0, fixphi,   0, fixinc);
/*
 *
 */
   GgiUseShell(frame_box);
   sample   = GgiTextField("SAMPLE=", H_SAMPLE, 10);
   GgiSetLabel(sample, "Sample spacing", 110);
   saunits  = GgiLabel("( )");
   sysvel      = GgiSetLabel(GgiTextField("VSYS=", H_VSYS, 10),
                              "Syst. vel.", 110);
   svunits  = GgiLabel("( )");
   oversub  = GgiTextField("OVERSUB=", H_OVERSUB, 10);
   GgiSetLabel(oversub, "Overlay on subset:", 0);
#if 0
   centre      = GgiSetLabel(GgiTextField("CENTRE=", H_CENTRE, 10),
                              "Centre pos.", 75);
#endif
   
   GgiSetPosition(sample,   0, NULL,    0, kader2);
   GgiSetPosition(saunits,  0, sample,  0, kader2);
   GgiSetPosition(sysvel,   0, NULL,    0, sample);
   GgiSetPosition(svunits,  0, sysvel,  0, sample);
   GgiSetPosition(oversub,  0, kader2,  0, kader2);
#if 0
!   GgiSetPosition(centre,   0, galpos,   0, slx0y0);
#endif

   (void)ScheduleKeyevent(gui_set_units, "MAPUNITS=", KEYCHANGE, clunits);
   (void)ScheduleKeyevent(gui_set_units, "XUNITS=", KEYCHANGE, saunits);
   (void)ScheduleKeyevent(gui_set_units, "VUNITS=", KEYCHANGE, svunits);
   (void)GgiUseShell(s_prev);
   ScheduleKeyevent(popshell, key, KEYCHANGE, s_input);
}
#<

#>            labels.c
/* labels.c
                              COPYRIGHT (c) 1999
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands

Author: J.P. Terlouw
*/

#define NAME "Plot labels"

#include "stddef.h"
#include "stdio.h"
#include "gipsyc.h"
#include "userfio.h"
#include "ggi.h"
#include "colormenu.h"
#include "helptext.h"
#include "labels.h"

                                                                                                                                                                                                               
/* -------------------------------------------------------------------------- */
/*                                 popshell                                   */
/* -------------------------------------------------------------------------- */
/*  Keyword handler causing the shell to be popped up or down.
 */
static void popshell(ident id, char *key, int code, void *arg)
{  
   static bool state=FALSE;
   bool button=toflog(FALSE);
 
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      GgiShowShell((ident)arg, !state);
      state = !state;
      wkeyf(key);
   }
}

/* ========================================================================== */
/*                                  Labels                                    */
/* -------------------------------------------------------------------------- */
/*   Labels() creates a pop-up shell for specifying plot labels.
 */
void  Labels(char *key)
{
   static ident s_labels;
   ident s_prev, frame_box;
   ident startx, starty, deltax, deltay, subx, suby, titx, tity, rows,
         coltxt, colbut;
   ident closebut, plotbut;
    
   s_labels = GgiShell(NAME);
   s_prev  = GgiUseShell(s_labels);
   frame_box = GgiForm(NAME, 2);
   closebut = GgiSetLabel(GgiButton(key, "Close window"), "CLOSE", 0);
   plotbut  = GgiSetLabel(GgiButton("EXTRACT=", H_EXTRACT), "PLOT", 0);
   GgiSetPosition(frame_box, 0, NULL, 0, NULL);
   GgiSetPosition(closebut, 0, NULL, 0, frame_box);
   GgiSetPosition(plotbut, -GgiWidth(plotbut), frame_box, 0, frame_box);
   
   GgiUseShell(frame_box);
   startx = GgiSetLabel(GgiTextField("LABSTARTX=", H_LABSTARTX, 10),
                        "Start x", 90);
   starty = GgiSetLabel(GgiTextField("LABSTARTY=", H_LABSTARTY, 10),
                        "Start y", 90);
   deltax = GgiSetLabel(GgiTextField("LABDELTAX=", H_LABDELTAX, 10),
                        "Delta x", 90);
   deltay = GgiSetLabel(GgiTextField("LABDELTAY=", H_LABDELTAY, 10),
                        "Delta y", 90);
   subx   = GgiSetLabel(GgiTextField("LABSUBX=",   H_LABSUBX,   10),
                        "Sub intv. x", 90);
   suby   = GgiSetLabel(GgiTextField("LABSUBY=",   H_LABSUBY,   10),
                         "Sub intv. y", 90);
   titx   = GgiSetLabel(GgiTextField("TITLEX=",    NULL,        10),
                        "Title x", 90);
   tity   = GgiSetLabel(GgiTextField("TITLEY=",    NULL,        10),
                         "Title y", 90);
   rows   = GgiSetLabel(GgiTextField("MOSROWS=",   H_MOSROWS,    5),
                         "# plot rows", 90);
   coltxt = GgiLabel("Labels colour:");
   colbut = ColorMenu("LABCOL=", ". . . . .", NULL);

   GgiSetPosition(startx, 0, NULL,   0, NULL);
   GgiSetPosition(starty, 0, startx, 0, NULL);
   GgiSetPosition(deltax, 0, NULL,   0, startx);
   GgiSetPosition(deltay, 0, deltax, 0, startx);
   GgiSetPosition(subx,   0, NULL,   0, deltax);
   GgiSetPosition(suby,   0, subx,   0, deltax);
   GgiSetPosition(titx,   0, NULL,   0, subx);
   GgiSetPosition(tity,   0, titx,   0, subx);
   GgiSetPosition(rows,   0, NULL,   0, titx);
   GgiSetPosition(coltxt, 0, NULL,   0, rows);
   GgiSetPosition(colbut, 0, coltxt, 0, rows);
   (void)GgiUseShell(s_prev);
   ScheduleKeyevent(popshell, key, KEYCHANGE, s_labels);
}
#<

#>            plot.c
/* plot.c


                          COPYRIGHT (c) 1996
                    Kapteyn Astronomical Institute
               University of Groningen, The Netherlands
                        All Rights Reserved.

*/

#include    "inspector.h"


#include    "gui.h"
#include    "pgplot.h"       /* All PGPLOT includes. */
#include    "pgsbci.h"
#include    "gdi_open.h" 
#include    "gdi_iinfo.h" 
#include    "gdi_frame.h" 
#include    "gdi_close.h" 


extern float    fblank;
extern double   dblank;



void setchsize( float ch )
/*-------------------------------------------------------------*/
/* PURPOSE: Set character height.                              */
/*-------------------------------------------------------------*/
{
   pgsch_c( &ch );
}



void setlinestyle( int  style )
/*-------------------------------------------------------------*/
/* PURPOSE: Set line style.                                    */
/*-------------------------------------------------------------*/
{
   fint   Style = (fint) style;
   pgsls_c( &Style );
}



void setcolor( int col )
/*-------------------------------------------------------------*/
/* PURPOSE: Set color to 'col'.                                */
/* Alternative pgsci.                                          */
/*-------------------------------------------------------------*/
{
   fint   Col = (fint) col;
   pgsci_c( &Col );
}


void setwidth( int width )
/*-------------------------------------------------------------*/
/* PURPOSE: Set line width to 'width'.                         */
/* Alternative pgslw.                                          */
/*-------------------------------------------------------------*/
{
   fint    Width = (fint) width;
   pgslw_c( &Width );
}


void setfont( int font )
/*-------------------------------------------------------------*/
/* PURPOSE: Set font to 'font'                                 */
/*-------------------------------------------------------------*/
{
   fint    Font = (fint) font;
   pgscf_c( &Font );
}


void plmove( float x, float y )
/*-------------------------------------------------------------*/
/* PURPOSE: Alternative for pgmove.                            */
/*-------------------------------------------------------------*/
{
   pgmove_c( &x, &y );
}


void pldraw( float x, float y )
/*-------------------------------------------------------------*/
/* PURPOSE: Alternative for pgdraw.                            */
/*-------------------------------------------------------------*/
{
   pgdraw_c( &x, &y );
}


void pltext( float   x,
             float   y,
             float   angle,
             float   fjust,
             char   *text )
/*-------------------------------------------------------------*/
/* PURPOSE: Alternative for pgtxt.                             */
/*-------------------------------------------------------------*/
{
   pgptxt_c( &x, &y, &angle, &fjust, tofchar(text) );
}



void plsymbol( float x, float y, int symbol )
/*-------------------------------------------------------------*/
/* PURPOSE: Alternative for pgpt.                              */
/* Is used 'extern'.                                           */
/*-------------------------------------------------------------*/
{
   fint    num = 1;
   fint    symnr = (fint) symbol;
   pgpt_c( &num, &x, &y, &symnr );
}



void plpoints( float *x, float *y, int ndat, int symbol )
/*-------------------------------------------------------------*/
/* PURPOSE: Alternative for pgpt.                              */
/*-------------------------------------------------------------*/
{
   fint    num = (fint) ndat;
   fint    symnr = (fint) symbol;

   pgpt_c( &num, x, y, &symnr );
}



void plrect( float  *box,
             int     colour )
/*-------------------------------------------------------------*/
/* PURPOSE: Alternative for pgrect.                            */
/*-------------------------------------------------------------*/
{
   fint   hollow = 2;

   pgsfs_c( &hollow );
   setcolor( colour );
   pgrect_c( &box[0], &box[2], &box[1], &box[3] );
}




void setmarker( float x, float y, fint sym )
/*-------------------------------------------------------------*/
/* PURPOSE: Set marker op x, y.                                */
/*-------------------------------------------------------------*/
{
   fint one = 1;
   pgpt_c( &one, &x, &y, &sym );
} 


void selectplot( int id )
/*------------------------------------------------------------*/
/* PURPOSE: Select plot                                       */
/*------------------------------------------------------------*/
{
   fint r = id;
   pgslct_c( &r );   
}


void clearpage( void )
/*-------------------------------------------------------------*/
/* PURPOSE: Alternative for pgpage.                            */
/* Is used 'extern'.                                           */
/*-------------------------------------------------------------*/
{
   pgpage_c();
}



void opengidsplot( void )
/*------------------------------------------------------------*/
/* PURPOSE: Initialize plot on GIDS.                          */
/*------------------------------------------------------------*/
{
   fint    pgunit;                 /* Ignored by 'pgbeg', use 0 */
   fint    nxysub[2];              /* Number of subdivisions */
   fint    r1;                     /* Return value or level */
   fint    off;


   pgunit = 0;                     /* Ignored by 'pgbeg' */
   nxysub[0] = 1;                  /* Default no subdivisions in plot */
   nxysub[1] = 1;

   r1 = pgbeg_c( &pgunit,
                 tofchar( "gids//append" ),
                 &nxysub[0],
                 &nxysub[1] );

   if (r1 != 1)
      errorf( 4, "Cannot open output device" );

   /* No NEXTPAGE= keyword */
   off = tobool( 0 );
   pgask_c( &off );
}



void closegidsplot( void )
/*------------------------------------------------------------*/
/* PURPOSE: Close pgplot for GIDS.                            */
/*------------------------------------------------------------*/
{
   pgend_c();
}



fint initplot( char *dev, 
               int  rows, 
               int  cols )
/*------------------------------------------------------------*/ 
/* PURPOSE: Initialize PGPLOT                                 */
/* We have more plot windows to process. Therefore pgbeg is   */
/* replaced by pgopen.                                        */
/* r = pgbeg_c( &unit, tofchar( dev ),&nxysub[0],&nxysub[1] );*/
/* In this stage a plot window is already opened by gui.c     */               
/*------------------------------------------------------------*/
{                                                                       
   fint      r;
   fint      ny = (fint) rows;
   fint      nx = (fint) cols;
                                                                        
   r = pgopen_c( tofchar(dev) );                                        
   if (r <= 0)  
      return( -1 );
   pgsubp_c( &nx, &ny );
   return( r );
}                                                                




void setwindowandviewport( sliceplottype     *slice,
                           slicecommontype   *slicecom )
/*------------------------------------------------------------*/
/* PURPOSE: Set viewport and window for tilted XV plot.       */
/*------------------------------------------------------------*/
{
   float   xlo, xhi, ylo, yhi;

   xlo = slice->VPxlo; xhi = slice->VPxhi;
   ylo = slice->VPylo; yhi = slice->VPyhi;

   pgsvp_c( &xlo, &xhi, &ylo, &yhi );
      
   xlo = (float) slicecom->xloall;
   xhi = (float) slicecom->xhiall;

   ylo = (float) slicecom->Vmingr;        /* Y axis window always in GRIDS !! */
   yhi = (float) slicecom->Vmaxgr;

   pgswin_c( &xlo, &xhi, &ylo, &yhi );
}




void plotslice( globaltype  *g,
                int          i )
/*------------------------------------------------------------*/
/* PURPOSE: Plot the xv maps.                                 */
/*------------------------------------------------------------*/
{
   float  trans[6];
   fint   startindexX, startindexY;
   fint   endindexX, endindexY;
   fint   idim, jdim;
   float  xlo, xhi;
   float  xlen;
   float  datmin = g->inset.clip[0];
   float  datmax = g->inset.clip[1];   
   fint   c1, c2;
   sliceplottype    *slice;
      
        
   slice = &(g->slice[i]);
 
   xlo  = (float) g->slicecom.xloall;
   xhi  = (float) g->slicecom.xhiall;   
   idim = g->slicecom.xhiallgr - g->slicecom.xloallgr + 1;   
/*   idim = slice->ndat;*/
   xlen = (float) (idim);
   
 
   /* Note that the window is previously set to fit the */
   /* greatest XV box. */
   trans[0] =  xlo - (xhi-xlo)/(xlen-1.0);      /* I = 1 -> X = xlo */
   trans[1] =  (xhi-xlo) / (xlen-1.0);          /* I = xlen -> X = xhi */
   trans[2] =  0.0;
   trans[3] =  g->slicecom.Vmingr-1.0;          /* J = 1 -> Y = Vmingr */
   trans[4] =  0.0;
   trans[5] =  1.0;                             /* J = jdim -> Y = Vmaxgr */

   jdim        = (int) (g->slicecom.Vmaxgr-g->slicecom.Vmingr+1.0);
   startindexX = 1;
   startindexY = 1;
   endindexX   = idim;
   endindexY   = jdim;

   pgqcol_c( &c1, &c2 );
   c1 = 17; 
   pgscir_c( &c1, &c2 );       /* Set the color index range */
   c1 = 16;
   pgsbci_c( &c1 );

   if (g->hardcopy) 
   {
      gui_applycol();
   }   
   pgimag_c( slice->image,     /* Array to be plotted */
             &idim,            /* First dimension of 'image' */
             &jdim,            /* Second dimension of 'image' */
             &startindexX,     /* Start of range of first index */
             &endindexX,       /* End of range of first index */
             &startindexY,     /* Start of range of second index */
             &endindexY,       /* End of range of second index */
             &datmin,
             &datmax,
             trans);           /* Transformation matrix between array grids and world coordinates */                                      
}




static int getindex( float   x,
                     float   *r,
                     float   mindelta,
                     int     n )
/*------------------------------------------------------------*/
/* PURPOSE: */
/*------------------------------------------------------------*/
{
/*   int  result = x / mindelta;*/
   int  i; 
  
   if (x < r[0] + (r[1]-r[0])/2.0) return( 0 );
   if (x > r[n-2] + (r[n-1]-r[n-2])/2.0) return( n-1 );   
   
   for (i = 1; i < n-1; i++)
   {
      if (x > r[i]-(r[i]-r[i-1])/2.0 && x < r[i]+(r[i+1]-r[i])/2.0)
         return( i );
   }
   return( -1 );
}





static void plotmarkersonslices( globaltype *g,
                                 int         currentmarker )
/*------------------------------------------------------------*/
/* PURPOSE: Put markers on slice plots.                       */
/* Moving markers are indicated with currentmarker > 0        */
/*------------------------------------------------------------*/
{
   int          i;
   fint         oldPGplotid;
   float        oldchsize;
   fint         oldcol, oldwidth;
   int          n;
   
  
   if (!g->slicecom.plotexist)
      return;
   
   n = g->parcommon.numTRrad;
   
   /*--------------------------------------------------*/
   /* If the current device is a hardcopy device,      */
   /* there is no need to restore the background plot. */
   /*--------------------------------------------------*/
   if (!g->hardcopy)      
   {   
      pgqid_c( &oldPGplotid );              /* Inquire current device identifier */
      selectplot( g->PGmainplotid );          
      GgiPlotShow( g->mainplotGGIid, 0 );   /* Recall the background plot */
   }

   pgqch_c( &oldchsize );
   pgqlw_c( &oldwidth );
   pgqci_c( &oldcol );
   for (i = 0; i < g->slicecom.numslices; i++) 
   {
      int cm = currentmarker;
      int sym = g->slicecom.marker1.symbol;

      setchsize( g->slicecom.marker1.size );
      setwidth( g->slicecom.marker1.width );   
      setcolor( g->slicecom.marker1.color );      
      setwindowandviewport( &(g->slice[i]), &(g->slicecom) );
      plpoints( g->slice[i].markerloX, g->slice[i].markerloY, n, sym );  
      plpoints( g->slice[i].markerhiX, g->slice[i].markerhiY, n, sym );        
      if (cm >= 0)  /* Plot the moving marker in the slice plots */ 
      {
         int movingsym = g->slicecom.marker3.symbol;
         setchsize( g->slicecom.marker3.size );
         setwidth( g->slicecom.marker3.width ); 
         setcolor( g->slicecom.marker3.color );
         plpoints( &(g->slice[i].markerloX[cm]), &(g->slice[i].markerloY[cm]), 1, movingsym );
         plpoints( &(g->slice[i].markerhiX[cm]), &(g->slice[i].markerhiY[cm]), 1, movingsym );
      }
   }
   setchsize( oldchsize );
   setcolor( oldcol );
   setwidth( oldwidth );
   if (!g->hardcopy)      
      selectplot( oldPGplotid ); 
}



static void updateslicemarkers( int         indx, 
                                globaltype *g )
/*------------------------------------------------------------*/
/* PURPOSE: Update markers in slices window. If a parameter   */
/* in one of the parameter plots is changed by the mouse,     */
/* then convert the new value to the corresponding slice value*/
/* and draw all markers after refreshing the background.      */
/*------------------------------------------------------------*/
{
   int      i, j;
   double   vel, phi, inc, rad;
   double   projvel[2], projvelgrid;
   double   projrad[2];

   
   /* These are the TR parameters for this radius */
   rad = (double) g->parcommon.radii[indx];
   vel = (double) g->parplot[VELOCITY].wdata[indx];
   phi = (double) g->parplot[POSANG].wdata[indx];
   inc = (double) g->parplot[INCLINATION].wdata[indx];      
   
   for (i = 0; i < g->slicecom.numslices; i++)
   {   
      float  x[2], y[2];            

      if (Vc2Vxy( g->inset.cpos, 
                  g->slicecom.Vsys, 
                  rad, vel, phi, inc,         /* Ring pars */
                  g->slice[i].angle,          
                  g->slice[i].off,            /* Slice pars */
                  projvel, projrad ) )        /* Results */
      {
         for (j = 0; j < 2; j++)
         {
            fint  r;
            /*----------------------------------------*/
            /* The function 'Vc2Vxy' modifies the     */
            /* entered velocity in any units. Before  */
            /* we can calculate a grid, we have to    */
            /* convert the velocity to the units used */
            /* by 'phtogr_c' (header units).          */
            /*----------------------------------------*/
            projvel[j] *= g->parcommon.TRVunits2head;
            r = phtogr_c( g->inset.Name, 
                          &(g->slicecom.Vcword),
                          &projvel[j], 
                          &projvelgrid );

            y[j] = (float) projvelgrid;       /* In grids */          
            /* Convert the offset in TR units to slice units */
            x[j] = (float) (projrad[j] * g->parcommon.TRXunits2Xunits); 
         }      
      }
      else
      {
         /*----------------------------------------*/
         /* No radius and velocity found! Give     */
         /* marker positions outside the current   */
         /* plot window so that they are valid     */
         /* positions but will not be plotted.     */
         /* Note; pgpt etc. do not recognize       */
         /* blanks, otherwise we used blanks in    */
         /* such situations.                       */
         /*----------------------------------------*/         
         x[0] = x[1] = g->slicecom.xloall - 1.0;
         y[0] = y[1] = g->slicecom.Vmingr - 1.0;
      }
      g->slice[i].markerloX[indx] = x[0];
      g->slice[i].markerhiX[indx] = x[1];
      g->slice[i].markerloY[indx] = y[0];
      g->slice[i].markerhiY[indx] = y[1];      
   }
}



void putmarkersfirsttime( globaltype *g )
/*------------------------------------------------------------*/
/* PURPOSE: Initialize TR markers and plot them in slice plot */
/* for the first time.                                        */
/*------------------------------------------------------------*/
{
   int   i;
   int   n;
  

   n = g->parcommon.numTRrad;
   for (i = 0; i < g->slicecom.numslices; i++)
   {
      if (g->slice[i].markerloX != NULL)   free( g->slice[i].markerloX );
      if (g->slice[i].markerhiX != NULL)   free( g->slice[i].markerhiX );
      if (g->slice[i].markerloY != NULL)   free( g->slice[i].markerloY );
      if (g->slice[i].markerhiY != NULL)   free( g->slice[i].markerhiY );
   } 
  
   /* Allocate space for marker positions for all slices */  
   for (i = 0; i < g->slicecom.numslices; i++) 
   {
      g->slice[i].markerloX = (float *) calloc( n, sizeof(float) );
      g->slice[i].markerhiX = (float *) calloc( n, sizeof(float) );
      g->slice[i].markerloY = (float *) calloc( n, sizeof(float) );
      g->slice[i].markerhiY = (float *) calloc( n, sizeof(float) );      
      if (g->slice[i].markerloX == NULL ||
          g->slice[i].markerhiX == NULL || 
          g->slice[i].markerloY == NULL ||
          g->slice[i].markerhiY == NULL )
      {
         error_status( "Cannot allocate enough memory!",
                       "No memory for the marker positions in the slice plot",
                       "Perhaps you should restart with less rings or slices" );
         return;
      }
   }
 
   for (i = 0; i < n; i++)
   {      
      updateslicemarkers( i, g );
   }
   plotmarkersonslices( g, -1 );
}



void mouse1( ident         id,
             GgiPlotInfo   info,
             void          *arg )
/*------------------------------------------------------------*/
/* PURPOSE: React to cursor actions in VELOCITY window        */
/*------------------------------------------------------------*/
{
   globaltype  *g = (globaltype*) arg;
   float        x=info->x, y=info->y;                     /* World coordinates */
   int          button=info->button, state=info->state;  
   fint         oldPGplotid;   
   static  int  indx = -1;
   int          thisplot = -1;
   float        oldchsize;
   fint         oldcol, oldwidth;   
   int          segmode;
   int          shift = NO;
   
  
        if (id == g->plotcursorid[VELOCITY])    thisplot = VELOCITY;
   else if (id == g->plotcursorid[INCLINATION]) thisplot = INCLINATION;
   else if (id == g->plotcursorid[POSANG])      thisplot = POSANG;      
   
   pgqid_c( &oldPGplotid );              /* Inquire current device identifier */
   selectplot( g->PGparplotid[thisplot] );

   pgqci_c( &oldcol );
   pgqch_c( &oldchsize ); 
   pgqlw_c( &oldwidth );

   segmode = g->parcommon.segmode;
   shift = state&ggiShift;   


   /* CONTROL BUTTON AND MOUSE BUTTON 1 */
   if (state == ggiCtrl && button&ggiButn1)
   {
      static int segmode = 0;
      segmode++;
      if (segmode == 3)
         segmode = 0;
      wkeyf( "SEGMODE=%d", segmode);
      return;
   }

   /* BUTTON 1 AND THE KEYBOARD SHIFT BUTTON ==> make segment */
   if ((button&ggiButn1) && !(state&ggiButn1) )
   {
      indx = getindex( x, g->parcommon.radii, g->parcommon.deltaradmin, g->parplot[0].ndata );
      if (shift) 
      {
         float         x1, x2, y1, y2;
         int           count = g->parplot[thisplot].segcount;

         (void) GgiPlotXor( YES );
         pgqwin_c( &x1, &x2, &y1, &y2 );
         setcolor( GREEN );

         if (count == 0)
         {
            g->parplot[thisplot].xseg1 = x;
            g->parplot[thisplot].yseg1 = y;
            plmove( x, y1 );
            pldraw( x, y2 );
            setmarker( x, y, CROSS );
         }
         else if (count == 1)
         {
            g->parplot[thisplot].xseg2 = x;
            g->parplot[thisplot].yseg2 = y;
            plmove( x, y1 );
            pldraw( x, y2 );
            setmarker( x, y, CROSS );            
         }
         else if (count == 2)       /* Remove first */
         {
            float xs = g->parplot[thisplot].xseg1;
            float ys = g->parplot[thisplot].yseg1;
            setmarker( xs, ys, CROSS );
            plmove( xs, y1 );
            pldraw( xs, y2 ); 
         }
         else if (count == 3)       /* Remove second */
         {
            float xs = g->parplot[thisplot].xseg2; 
            float ys = g->parplot[thisplot].yseg2;             
            plmove( xs, y1 );
            pldraw( xs, y2 );
            setmarker( xs, ys, CROSS );
            g->parplot[thisplot].segcount = -1; 
         }
         g->parplot[thisplot].segcount++;
         (void) GgiPlotXor( NO );       
         setcolor( oldcol );           

         if (oldPGplotid > 0)                     /* Back to starting plot */
         {
            selectplot( oldPGplotid );      
         }                
      }
   }

   /* BUTTON 3 (&SHIFT) PRESSED AND SEGMENTS ARE DEFINED */
/*    if ((button&ggiButn3) && !(state&ggiButn3) && (g->parplot[thisplot].segcount == 2) ) */
   if (!(button) && state&ggiButn3 && (g->parplot[thisplot].segcount == 2) )
   {
      int     i;
      fint    sym;
      float   slope = 0.0;
      float   fact = 1.0;

      sym = g->parcommon.marker.symbol;
      setchsize( g->parcommon.marker.size );
      setcolor( g->parcommon.marker.color ); 
      setwidth( g->parcommon.marker.width );
      (void) GgiPlotXor( YES );      
      
      if (segmode == SEG_SLOPE)   /* Line with slope */
      {
         float x1, x2, y1, y2;
         x1 = g->parplot[thisplot].xseg1; y1 = g->parplot[thisplot].yseg1;
         x2 = g->parplot[thisplot].xseg2; y2 = g->parplot[thisplot].yseg2;
         slope = (y2 - y1) / (x2 - x1);
      } 
      if (segmode == SEG_FACTOR)
      {
         if (   x >= MYMIN(g->parplot[thisplot].xseg1, g->parplot[thisplot].xseg2)
             && x <= MYMAX(g->parplot[thisplot].xseg1, g->parplot[thisplot].xseg2) )
         {
            indx = getindex( x, g->parcommon.radii, g->parcommon.deltaradmin, g->parplot[0].ndata );
            fact = y / g->parplot[thisplot].wdata[indx];
         }                  
      } 
     
      /* Start loop over all points to see which are inside segment */     
      for (i = 0; i < g->parcommon.numTRrad; i++)
      {
         float   rad = g->parcommon.radii[i];
         if (   rad >= MYMIN(g->parplot[thisplot].xseg1, g->parplot[thisplot].xseg2)
             && rad <= MYMAX(g->parplot[thisplot].xseg1, g->parplot[thisplot].xseg2) )
         {
            float  yold = g->parplot[thisplot].wdata[i];
            float  ynew = y;
            
            if (segmode == SEG_FACTOR)
            {
               ynew = g->parplot[thisplot].wdata[i] * fact;
            }
            if (segmode == SEG_CONST)
            {
               ynew = y;
            }
            if (segmode == SEG_SLOPE)
            {
               ynew = y + slope * (rad - x);
            }               
            g->parplot[thisplot].wdata[i] = ynew;
            setmarker( rad, yold, sym );   /* erase old position */
            setmarker( rad, ynew, sym );
         }        
      }      
      (void) GgiPlotXor( NO );
      setwidth( oldwidth );
      setchsize( oldchsize );
      setcolor( oldcol ); 
      if (g->slicecom.plotexist)
      {
         selectplot( g->PGmainplotid );
         for (i = 0; i < g->parcommon.numTRrad; i++)
         {
            float   rad = g->parcommon.radii[i];
            if (   rad >= MYMIN(g->parplot[thisplot].xseg1, g->parplot[thisplot].xseg2)
                && rad <= MYMAX(g->parplot[thisplot].xseg1, g->parplot[thisplot].xseg2) )
            {            
               updateslicemarkers( i, g );
            }            
         }
         plotmarkersonslices( g, -1 );         
      }      

   }
  
 
   /* A MOUSE MOVEMENT WITH MOUSE BUTTON 1 OR 2 PRESSED */
   /* BUTTON 1 WORKS ON 1 INDEX. BUTTON 2 ON ALL        */
   if (!(button) && ((state == ggiButn1) || (state == ggiButn2)) ) 
   {
      if (thisplot >= 0)
      {
         float    xi, yi;
         int      sym;
            
         if (state == ggiButn2)
             indx = getindex( x, g->parcommon.radii, g->parcommon.deltaradmin, g->parplot[0].ndata );

         
         sym = g->parcommon.marker.symbol;
         setchsize( g->parcommon.marker.size );
         setcolor( g->parcommon.marker.color ); 
         setwidth( g->parcommon.marker.width );

         (void) GgiPlotXor( YES );
         xi = g->parcommon.radii[indx];
         yi = g->parplot[thisplot].wdata[indx];
         setmarker( xi, yi, sym );         
         setmarker( xi, y, sym );
         updateslicemarkers( indx, g );
         (void) GgiPlotXor( NO );

         setwidth( oldwidth );
         setchsize( oldchsize );
         setcolor( oldcol ); 

         g->parplot[thisplot].wdata[indx] = y;
       
         if (g->slicecom.plotexist)
         {
            selectplot( g->PGmainplotid );
            plotmarkersonslices( g, indx );
         }
      }            
   }   
   (void) GgiPlotXor( NO );      
  
   /* RELEASE BUTTON 1 -> than replot all markers */
   /* in the slices plot in one colour */
   if ( ((button&ggiButn1) && (state&ggiButn1)) ||
        ((button&ggiButn2) && (state&ggiButn2)) )
   {
      if (g->slicecom.plotexist)
      {
         selectplot( g->PGmainplotid );
         plotmarkersonslices( g, -1 );
      }
   }

   if (oldPGplotid > 0)                     /* Back to starting plot */
   {
      selectplot( oldPGplotid );      
   }   

}


void getplotpositions( sliceplottype    *slice,
                       slicecommontype  *slicecom,
                       int              hardcopy )
/*------------------------------------------------------------*/
/* PURPOSE: Calculate the startpositions of the slice images. */
/*                                                            */
/* The start positions are set by the number of rows and      */
/* columns and the index number of the slice.                 */
/*------------------------------------------------------------*/
{
   int     rows, cols;
   int     i;


   cols = slicecom->cols;
   rows = slicecom->rows;

   /* If somehow necessary, calculate default */
   if (cols == 0)
   {
      double    cc;
      cc = sqrt( (double) slicecom->numslices );
      cols = (int) cc;
      if (cc - (double) cols > 0.0)
         cols++;
   }
   if (rows == 0)
   {
      double   rr;
      rr   = (double) slicecom->numslices / (double) cols;
      rows = (int) rr;
      if (rr - (double) rows > 0.0)
         rows++;
   }

   for (i = 0; i < slicecom->numslices; i++)
   {
      float   Xtot, Ytot;
      float   xlen, ylen;
      int     xi, yi;


      Xtot = 1.0;         /* Entire PGPLOT device */
      Ytot = 1.0;

      yi = i/cols;
      xi = i%cols;
      xlen = (Xtot-(XOFFSETL+XOFFSETR)) / (float) cols;
      ylen = (Ytot-(YOFFSETB+YOFFSETT)) / (float) rows;
      slice[i].VPxlo = (float)xi * xlen + XOFFSETL;
      slice[i].VPxhi = slice[i].VPxlo + xlen;
      slice[i].VPylo = (float)(rows-yi-1) * ylen + YOFFSETB;
      slice[i].VPyhi = slice[i].VPylo + ylen;
      
      if (hardcopy)
      {
         slice[i].VPxlo *= 2.0/3.0;
         slice[i].VPxhi *= 2.0/3.0;
      }
      anyoutf( DEBUG, "DEBUG (getplotpositions): vp=%g %g %g %g",
               slice[i].VPxlo, slice[i].VPxhi, slice[i].VPylo, slice[i].VPyhi );
      /*--------------------------------------------------*/
      /* Calculate at this position whether this XV plot  */
      /* gets any labeling or not.                        */
      /*--------------------------------------------------*/
      if (i%cols == 0)
         slice[i].leftpanel = YES;
      else
         slice[i].leftpanel = NO;
      if (i/cols == rows -1)
         slice[i].bottompanel = YES;
      else
         slice[i].bottompanel = NO;
   }

   /*--------------------------------------------------*/
   /* Update the contents of the mosaic array. This is */
   /* important if the initial values are equal to 0.  */
   /*--------------------------------------------------*/
   slicecom->cols = cols;
   slicecom->rows = rows;
}




static int deflabels( double   start,
                      double   end,
                      double  *labstart,
                      double  *labdelta,
                      fint    *labsub )
/*------------------------------------------------------------*/
/* PURPOSE:  If input values for start or end are blank,      */
/*           calculate default values for these variables.    */
/* Defaults must be calculated if the value of start or delta */
/* is a (d)blank or is < 0 for the subdivisions.              */
/*------------------------------------------------------------*/
{
   double    D;
   double    power;
   double    ipart;
   double    q;
   double    scaleddelta;


   if (*labstart != dblank && *labdelta != dblank && *labsub >= 0)
   {
      /* There are no defaults necessary */
      return( 1 );
   }

   D = 0.125 * (end - start);
   start += D;
   end -= D;

   if (*labdelta == dblank)
   {
      D = fabs( start - end );               /* Start value for step size */
      ipart = floor( log10(D) );             /* Bring step size in range [1,10> */
      power = pow( 10.0, ipart );
      D /= power;
      /*-----------------------------------------------------*/
      /* Range [1,2> --> 2^0 --> scaleddelta = 2^(0-1) = 0.5 */
      /* Range [2,4> --> 2^1 --> scaleddelta = 2^(1-1) = 1.0 */
      /* Range [4,8> --> 2^2 --> scaleddelta = 2^(2-1) = 2.0 */
      /* Range [8, > --> 2^3 --> scaleddelta = 2^(3-1) = 4.0 */
      /*-----------------------------------------------------*/
      q = floor( log10(D)/log10(2.0) );
      scaleddelta = pow( 2.0, q-1.0 );

      *labdelta = scaleddelta * power;            /* Bring size back in range */

      /* Determine the number of subdivisions */

      if (*labsub < 0)
      {
         /* A default must be calculated */
         if (scaleddelta == 0.5 || scaleddelta == 1.0)
            *labsub = 5;
         else
            *labsub = scaleddelta;
      }
   }
   else
   {
      if (*labsub < 0)
         *labsub = 1;
   }


   /* Calculate the start label */
   if (*labstart == dblank)
   {
      double minimum = start;
      if (end < minimum)
         minimum = end;
      ipart = ceil( minimum /(*labdelta) );
      *labstart = ipart * (*labdelta);
   }

   return( 1 );
}




static int isinside( double x,
                     double lo,
                     double hi,
                     double frac )
/*------------------------------------------------------------*/
/* PURPOSE: Is a value between two boundary values?           */
/*------------------------------------------------------------*/
{
   double D = frac * (hi - lo);

   if (x >= lo + D && x <= hi - D)
      return( YES );
   return( NO );
}



void annotatesliceplot( globaltype *g )
/*------------------------------------------------------------*/
/* PURPOSE: Titles for the plot with XV maps.                 */
/*------------------------------------------------------------*/
{
   fint        ndc = 0;
   float       xlo2, ylo2, xhi2, yhi2; 
   float       xlo1, ylo1, xhi1, yhi1;      
   char        titleXtxt[STRLEN+1];
   fchar       TitleX;      
   char        titleYtxt[STRLEN+1];
   fchar       TitleY;            
   
  
   xlo2 = XOFFSETL;
   ylo2 = YOFFSETB;
   xhi2 = 1.0 - XOFFSETR;
   yhi2 = 1.0 - YOFFSETT;  
   if (g->hardcopy)
   {
      xlo2 *= 2.0/3.0;
      xhi2 *= 2.0/3.0;
   }
   pgqvp_c( &ndc, &xlo1, &xhi1, &ylo1, &yhi1 );
   pgsvp_c( &xlo2, &xhi2, &ylo2, &yhi2 );


   /* V-axis title for leftmost plots */
   {
      float  disp  = 3.0; /*g->slicecom.maxlablen/(1.0-1.5*XOFFSET)*40.0 + 0.2;*/
      float  just  = 0.5;
      float  coord = 0.5;
      fint   dfault = HIDDEN;
      
      TitleY.l = STRLEN;
      TitleY.a = titleYtxt;    
      clearfchar(TitleY);  
      TitleY.l = usertext_c( TitleY, &dfault, tofchar("TITLEY="), tofchar(" ") );
      TitleY.a[nelc_c(TitleY)] = '\0';

      pgmtxt_c( tofchar("L"), &disp, &coord, &just, TitleY );
   }

   /* Title for bottom axis X */
   {
      fint   dfault = HIDDEN;
      float  disp = 2.5;
      float  just = 0.5;
      float  coord = 0.5;

      TitleX.l = STRLEN;
      TitleX.a = titleXtxt;
      clearfchar(TitleX);  
      TitleX.l = usertext_c( TitleX, &dfault, tofchar("TITLEX="), tofchar(" ") );
      TitleX.a[nelc_c(TitleX)] = '\0';

      pgmtxt_c( tofchar("B"), &disp, &coord, &just, TitleX );
   }
   pgsvp_c( &xlo1, &xhi1, &ylo1, &yhi1 );   
}



void plotaxis( sliceplottype     slice,
               slicecommontype  *slicecom,
               fchar             Setin )
/*------------------------------------------------------------*/
/* PURPOSE: Plot the axis labels and ticks of the XV maps.    */
/*------------------------------------------------------------*/
{
   float    ticksize;             /* Size of label ticks in char. height size */
   float    lablen;        /* Maximum text length of all labels along an axis */
   char     label[40];                               /* The label text itself */
   int      sign;         /* -1 or 1 indicates the label direction wrt. start */
   int      i;                                                     /* counter */
   float    oldchsize;


   /* Plot the frames without the labels */
   {
      float  x1, x2, y1, y2;
      fint   hollow = 2;
      float  d; 
      
      x1 = slicecom->xloall; x2 = slicecom->xhiall;
      y1 = slicecom->Vmingr; y2 = slicecom->Vmaxgr;
      d = (x2-x1)/1000.0;
      x1 += d; x2 -= d;
      d = (y2-y1)/1000.0;
      y1 += d; y2 -= d;      
      pgsfs_c( &hollow ); 
      pgrect_c( &x1, &x2, &y1, &y2 );
   }
   /* Plot the Vsys line and the 0-offset line in dotted style */
   if (!slicecom->Vingrids)
   {
      double   vsysgr, vsysph;
      fint     r;

      /* Convert velocity to velocity in header units for 'phtogr' */
      vsysph = slicecom->Vsys * slicecom->Vunits2head;
      r = phtogr_c( Setin, &(slicecom->Vcword), &(slicecom->Vsys), &vsysgr );
      setlinestyle( DASHED );
      plmove( slicecom->xloall, vsysgr );
      pldraw( slicecom->xhiall, vsysgr );
      /* Dotted axis through offset 0 on X-axis */
      plmove( 0.0, slicecom->Vmingr );
      pldraw( 0.0, slicecom->Vmaxgr );
      setlinestyle( FULL );
   }


   {
      /* Read the label keywords */
      fint   r1, r2, r3;
      fint   nitems = 1, dfault = HIDDEN;
      
      slicecom->labstartxy[1] = dblank;
      slicecom->labdeltaxy[1] = dblank;
      slicecom->labsubxy[1] = -1;
      r1 = userdble_c( &(slicecom->labstartxy[1]), &nitems, &dfault, 
                       tofchar("LABSTARTY="), tofchar("") );
      r2 = userdble_c( &(slicecom->labdeltaxy[1]), &nitems, &dfault, 
                       tofchar("LABDELTAY="), tofchar("") );
      r3 = userint_c( &(slicecom->labsubxy[1]), &nitems, &dfault, 
                       tofchar("LABSUBY="), tofchar("") );
     
            
      /*--------------------------------------------------*/
      /* If user did not supply values for the label ticks*/
      /* then calculate reasonable defaults. The reason   */
      /* 'slicecom' enters as an pointer is that its      */
      /* members 'labstartxy' etc. need to be initialized */
      /* for only one plot so that they can be stored for */
      /* future use.                                      */
      /*--------------------------------------------------*/
      deflabels( slicecom->Vminph / slicecom->Vunits2head,
                 slicecom->Vmaxph / slicecom->Vunits2head,
                 &(slicecom->labstartxy[1]),                     /* The Y-values */
                 &(slicecom->labdeltaxy[1]),
                 &(slicecom->labsubxy[1]) );

      wkeyf("LABSTARTY=%g", slicecom->labstartxy[1] );
      wkeyf("LABDELTAY=%g", slicecom->labdeltaxy[1] );
      wkeyf("LABSUBY=%d",   slicecom->labsubxy[1] );
   }

   ticksize = ((float) slicecom->cols) *
              ABS( slicecom->xhiall - slicecom->xloall ) / 80.0;

   anyoutf( DEBUG, "DEBUG (plotaxis): Y start delta sub=%f %f %d", 
            slicecom->labstartxy[1],
            slicecom->labdeltaxy[1], 
            slicecom->labsubxy[1] );

   /*-------------------Y-axis-------------------------*/
   /* Start plotting the y labels and ticks. Start at  */
   /* the starting point and work upwards (sign == 1)  */
   /* then downwards (sign == -1).                     */
   /*--------------------------------------------------*/

   pgqch_c( &oldchsize );
   setchsize( 0.8 );
   sign   = 1;
   lablen = 0.0;
   for (i = 0; i < 2; i++)
   {
      double   y, ygr;
      double   ysub, subdelt;
      fint     r1;
      int      ins, inside;

      y = slicecom->labstartxy[1];
      do
      {
         if (slicecom->Vingrids)
            ygr = y;
         else
         {
            /* Calculate step in real velocities */
            double    yhead;
            yhead = y * slicecom->Vunits2head;
            r1 = phtogr_c( Setin, &(slicecom->Vcword), &yhead, &ygr );
         }
         inside = isinside( ygr, slicecom->Vmingr, slicecom->Vmaxgr, 0.1 );
         if (inside)                                  /* Within grid limits? */
         {
            if (y == 0.0)
               strcpy( label, "0" );
            else
               sprintf( label, "%g", y );

            {
               fint     units = 5;      /* Return answer as a fraction of the current viewport size */
               float    xl, yl;
               char     lab[128];
               sprintf( lab, "%sww", label ); /* Better to add dummy characters */
               pglen_c( &units, tofchar(lab), &xl, &yl );
               lablen = MYMAX( lablen, xl );
            }

            plmove( slicecom->xloall, ygr );
            pldraw( slicecom->xloall + ticksize, ygr );    /* Plot tick inside */
            plmove( slicecom->xhiall, ygr );
            pldraw( slicecom->xhiall - ticksize, ygr );
            if (slice.leftpanel)               /* Plot labels for left panels */
            {
               /* Same displacements as PGPLOT in 'pgbox' */
               float  coord = (ygr - slicecom->Vmingr) /
                              (slicecom->Vmaxgr - slicecom->Vmingr);
               float  just = 1.0;
               float  disp = 0.7;
               pgmtxt_c( tofchar("LV"), &disp, &coord, &just, tofchar(label) );
            }
            ysub = y;
            /* Plot the minor ticks */
            subdelt = slicecom->labdeltaxy[1] / (double) slicecom->labsubxy[1];
            do
            {
               if (slicecom->Vingrids)
                  ygr = ysub;
               else
               {
                  double    yhead;
                  yhead = ysub / slicecom->Vunits2head;
                  r1 = phtogr_c( Setin, &(slicecom->Vcword), &ysub, &ygr );
               }
               ins = isinside( ygr, slicecom->Vmingr, slicecom->Vmaxgr, 0.05 );
               if (ins)
               {
                  plmove( slicecom->xloall, ygr );
                  pldraw( slicecom->xloall + ticksize/2.0, ygr );
                  plmove( slicecom->xhiall, ygr );
                  pldraw( slicecom->xhiall - ticksize/2.0, ygr );
               }
               ysub += ((double)sign) * subdelt;
            }
            while(ins);
            y += ((double)sign) * slicecom->labdeltaxy[1];
         }
      }
      while (inside);
      sign = -1;
   }
   setchsize( oldchsize );



   /*------------------Offset-axis---------------------*/
   /* Plot labels along the X-axis (spatial coords.)   */
   /*--------------------------------------------------*/
   {
      /* Read the label keywords */
      fint   r1, r2, r3;
      fint   nitems = 1, dfault = HIDDEN;
      
      slicecom->labstartxy[0] = dblank;
      slicecom->labdeltaxy[0] = dblank;
      slicecom->labsubxy[0] = -1;
      r1 = userdble_c( &(slicecom->labstartxy[0]), &nitems, &dfault, 
                       tofchar("LABSTARTX="), tofchar("") );
      r2 = userdble_c( &(slicecom->labdeltaxy[0]), &nitems, &dfault, 
                       tofchar("LABDELTAX="), tofchar("") );
      r3 = userint_c( &(slicecom->labsubxy[0]), &nitems, &dfault, 
                       tofchar("LABSUBX="), tofchar("") );


      if (slicecom->labstartxy[0] == dblank)
      {
         /* Always plot label 0 if 0 is in plot range */
         if ( isinside(0.0, slicecom->xloall, slicecom->xhiall, 0.1) )
            slicecom->labstartxy[0] = 0.0;
      }
   
  
      deflabels( slicecom->xloall, /* / slicecom->Xunits2head */
                 slicecom->xhiall, /* / slicecom->Xunits2head */
                 &(slicecom->labstartxy[0]),                      /* The X-values */
                 &(slicecom->labdeltaxy[0]),
                 &(slicecom->labsubxy[0]) );

      wkeyf("LABSTARTX=%g", slicecom->labstartxy[0] );
      wkeyf("LABDELTAX=%g", slicecom->labdeltaxy[0] );
      wkeyf("LABSUBX=%d",   slicecom->labsubxy[0] );
   }

   ticksize = ((float) slicecom->rows) *
              ABS( slicecom->Vmaxgr - slicecom->Vmingr ) / 80.0;

   anyoutf( DEBUG, "DEBUG (plotaxis): X start delta sub=%f %f %d", slicecom->labstartxy[0],
            slicecom->labdeltaxy[0], slicecom->labsubxy[0] );

   pgqch_c( &oldchsize );
   setchsize( 0.8 );

   sign = 1;
   for (i = 0; i < 2; i++)
   {
      double   x, xp;
      double   xsub, xsubp, subdelt;
      int      ins, inside;

      x = slicecom->labstartxy[0];
      do
      {
         xp = x;  /* * slicecom->Xunits2head;*/
         inside = isinside( xp, slicecom->xloall, slicecom->xhiall, 0.1 );
         if (inside)
         {
            if (x == 0.0)
               strcpy( label, "0" );
            else
               sprintf( label, "%g", x );

      	    plmove( xp, (float) slicecom->Vmingr );
            pldraw( xp, (float) slicecom->Vmingr + ticksize );
      	    plmove( xp, (float) slicecom->Vmaxgr );
            pldraw( xp, (float) slicecom->Vmaxgr - ticksize );
            if (slice.bottompanel)
            {
               float  coord = (xp - slicecom->xloall) /
                              (slicecom->xhiall - slicecom->xloall);
               float  just = 0.5;
               float  disp = 1.2;
               pgmtxt_c( tofchar("B"), &disp, &coord, &just, tofchar(label) );
            }
            xsub = x;
            subdelt = slicecom->labdeltaxy[0] / (double) slicecom->labsubxy[0];
            do
            {
               ins = isinside( xsub, slicecom->xloall, slicecom->xhiall, 0.05 );
               if (ins)
               {
                  xsubp = xsub; /* / slicecom->Xunits2labels;*/
                  plmove( xsubp, (float) slicecom->Vmingr );
                  pldraw( xsubp, (float) slicecom->Vmingr + ticksize/2.0 );
                  plmove( xsubp, (float) slicecom->Vmaxgr );
                  pldraw( xsubp, (float) slicecom->Vmaxgr - ticksize/2.0 );
               }
               xsub += ((double)sign) * subdelt;
            }
            while(ins);
            x += ((double)sign) * slicecom->labdeltaxy[0];
         }
      }
      while (inside);
      x = slicecom->labstartxy[0] - slicecom->labdeltaxy[0];
      sign = -1;
   }
   setchsize( oldchsize );

   slicecom->maxlablen = lablen;

#if (0)
   /* V-axis title for leftmost plots */
   if (slice.leftpanel)
   {
      float  just = 1.0;
      float  angle = 90.0;
      float  x1, y1;
      char   titleYtxt[STRLEN+1];
      fchar  TitleY;            
      fint   dfault = HIDDEN;
      
      TitleY.l = STRLEN;
      TitleY.a = titleYtxt;    
      clearfchar(TitleY);  
      (void) usertext_c( TitleY, &dfault, tofchar("TITLEY="), tofchar(" ") );
      TitleY.a[nelc_c(TitleY)] = '\0';


      if (slicecom->xloall > slicecom->xhiall)
         lablen *= -1.0;
      x1 = slicecom->xloall - lablen;
      y1 = (slicecom->Vmingr+ slicecom->Vmaxgr) / 2.0;
      
      pltext( x1, y1, angle, just, TitleY.a );
   }

   /* Title for bottom axis X */
   if (slice.bottompanel)
   {
      char        titleXtxt[STRLEN+1];
      fchar       TitleX;      
      fint        dfault = HIDDEN;

      TitleX.l = STRLEN;
      TitleX.a = titleXtxt;
      clearfchar(TitleX);  
      (void) usertext_c( TitleX, &dfault, tofchar("TITLEX="), tofchar(" ") );
      pglab_c( TitleX, tofchar(""), tofchar("") );
   }
#endif   
   /* Add slice angle and origin info */
   {
      float xt, yt;
      float cheight;
      char  info[40];

      pgqch_c( &oldchsize );
      setchsize( 0.8 );
      cheight = ((float) slicecom->rows) *
                ABS( slicecom->Vmaxgr - slicecom->Vmingr ) / 40.0;
      xt = slicecom->xhiall - 0.05*(slicecom->xhiall-slicecom->xloall);
      yt = slicecom->Vmaxgr - cheight;
      sprintf( info, "angle: %g\\uo\\d", slice.angle );
      pltext( xt, yt, 0.0, 1.0, info );             /* Justified to the right */

      yt -= cheight;
      sprintf( info, "cen.pos.: %g %g", slice.cpos[0], slice.cpos[1] );
      pltext( xt, yt, 0.0, 1.0, info );
      setchsize( oldchsize );
   }
}




void plotTRdata( globaltype *g,
                 int         thisplot )
/*------------------------------------------------------------*/
/* PURPOSE: Plot tilted ring velocities in velocity plot.     */
/*------------------------------------------------------------*/
{
   float    xlo, xhi, ylo, yhi; 
   float    delta;
   float    xtick = 0.0, ytick = 0.0;
   fint     nxsub = 0, nysub = 0; 
   fint     oldPGplotid ;
   int      ndat;
   float    oldchsize;
   fint     oldcol, oldwidth;


   ndat = g->parplot[thisplot].ndata; 
  
   if (g->hardcopy)
   {
      float   plotheight = (1.0-(YOFFSETB)) / 3.0;
      xlo = 0.68; xhi = 0.95; 
      ylo = YOFFSETB+thisplot*plotheight;
      yhi = ylo + plotheight - 0.05;
   }
   else
   {
      pgqid_c( &oldPGplotid );           /* Inquire current device identifier */
      selectplot( g->PGparplotid[thisplot] );
      pgeras_c( );             
      xlo = 0.15; xhi = 0.98; ylo = 0.2; yhi = 0.95;
   }
   pgsvp_c( &xlo, &xhi, &ylo, &yhi ); 
   delta = ( g->parcommon.maxrad - g->parcommon.minrad) / 10.0;
   if (delta == 0.0) 
      delta = 1.0;
   xlo = g->parcommon.minrad - delta/2.0;
   xhi = g->parcommon.maxrad + delta; 
   delta = ( g->parplot[thisplot].minmaxdat[1] - g->parplot[thisplot].minmaxdat[0]) / 4.0; 
   if (delta == 0.0)
      delta = 20.0;  
   ylo = g->parplot[thisplot].minmaxdat[0] - delta;
   yhi = g->parplot[thisplot].minmaxdat[1] + delta;
   pgswin_c( &xlo, &xhi, &ylo, &yhi ); 

   pgqci_c( &oldcol );   
   pgqch_c( &oldchsize );
   pgqlw_c( &oldwidth );

   if (g->hardcopy)
      setchsize( 0.8 );
   else
      setchsize( 2 );      
   
   setcolor( FOREGROUND );
   setwidth( 1 );

   pgbox_c( tofchar("BCNST" ), &xtick, &nxsub,
            tofchar("BCNSTV"), &ytick, &nysub );  
            
   
   /* Write the titles */
   {
      char   xtitle[64], ytitle[64];
      
      sprintf( xtitle, "Radius (%s)", g->parcommon.TRXunits );
      if (thisplot == VELOCITY)
         sprintf( ytitle, "Velocity (%s)", g->parcommon.TRVunits );
      if (thisplot == INCLINATION)
         sprintf( ytitle, "Inclination (\\uo\\d)" );
      if (thisplot == POSANG)
         sprintf( ytitle, "Pos. angle (\\uo\\d)"  );
      
      pglab_c( tofchar(xtitle), tofchar(ytitle), tofchar("") );
   }

   if (g->hardcopy)
   {
      /*--------------------------------------------------*/
      /* The hardcopy needs the entire surface as         */
      /* viewport. Compared to the small tilted ring      */
      /* plots, we need to rescale the character  sizes.  */
      /*--------------------------------------------------*/
      setchsize( g->parcommon.marker.size / 3.0 );
   } 
   else
   {
      (void) GgiPlotXor( YES );
      setchsize( g->parcommon.marker.size );
   }
      
   setcolor( g->parcommon.marker.color );
   setwidth( g->parcommon.marker.width );

   /* Plot the current values of the TR data */
   plpoints( g->parcommon.radii, 
             g->parplot[thisplot].wdata, 
             ndat, g->parcommon.marker.symbol );

   setwidth(  oldwidth );
   setchsize( oldchsize );
   setcolor(  oldcol );

   /* Reset the segments. */
   g->parplot[thisplot].segcount = 0;
   
   if (!g->hardcopy)
   {
      (void) GgiPlotXor( NO ); 
      if (oldPGplotid > 0)
         selectplot( oldPGplotid );
   }   
}



void plotcontours( globaltype  *g,
                   int          nr )
/*------------------------------------------------------------*/
/* PURPOSE: Plot the contour levels of current image.         */
/*                                                            */
/* 'nr' is the index of the current slice.                    */
/*                                                            */
/* Transformation from grids to world coordinates:            */
/* X = TR[0] + TR[1]*I + TR[2]*J                              */
/* Y = TR[3] + TR[4]*I + TR[5]*J                              */
/*                                                            */
/* No rotation or shear involved -> TR[2], TR[4] = 0:         */
/* X = TR[0] + TR[1]*I                                        */
/* Y = TR[3] + TR[5]*J                                        */
/*------------------------------------------------------------*/
{
   float  trans[6];
   fint   startindexX, startindexY;
   fint   endindexX, endindexY;
   fint   idim, jdim;
   float  xlo, xhi;
   float  xlen;
   fint   newcol, oldcol;
   fint   nc;


   nc = g->slicecom.numcontours;
   if (nc == 0)
      return;

   newcol = g->slicecom.concol;
   pgqci_c( &oldcol );
   setcolor( newcol );
   
   xlo  = (float) g->slicecom.xloall;
   xhi  = (float) g->slicecom.xhiall;
   xlen = (float) (g->slicecom.xhiallgr - g->slicecom.xloallgr+1);    
  
   /* Note that the window is previously set to fit the */
   /* greatest XV box. */

   trans[0] =  xlo - (xhi-xlo)/(xlen-1.0);      /* I = 1 -> X = xlo */
   trans[1] =  (xhi-xlo) / (xlen-1.0);          /* I = xlen -> X = xhi */
   trans[2] =  0.0;
   trans[3] =  g->slicecom.Vmingr-1.0;          /* J = 1 -> Y = Vmingr */
   trans[4] =  0.0;
   trans[5] =  1.0;                             /* J = jdim -> Y = Vmaxgr */
   idim        = g->slicecom.xhiallgr - g->slicecom.xloallgr + 1;
   jdim        = (int) (g->slicecom.Vmaxgr - g->slicecom.Vmingr+1.0);
   startindexX = 1;
   startindexY = 1;
   endindexX   = idim;
   endindexY   = jdim;


   pgconb_c( g->slice[nr].conimage,  /* Array to be plotted */
             &idim,            /* First dimension of 'image' */
             &jdim,            /* Second dimension of 'image' */
             &startindexX,     /* Start of range of first index */
             &endindexX,       /* End of range of first index */
             &startindexY,     /* Start of range of second index */
             &endindexY,       /* End of range of second index */
             g->slicecom.contours,  /* Array of contour levels */
             &nc,              /* Number of contour levels */
             trans,            /* Transformation matrix between array grids and world coordinates */
             &fblank );

   setcolor( oldcol );         /* Reset colour */
}




void plotsetvelocities( globaltype   *g,
                        int           nr )
/*------------------------------------------------------------*/
/* PURPOSE: Plot Line of sight velocity markers (from a GIPSY */
/* set) in the XV slices.                                     */
/*------------------------------------------------------------*/
{
   int   i;
   int   sym = g->slicecom.marker2.symbol;
   float oldchsize;
   fint  oldwidth, oldcol;
   

   if (g->slice[nr].velpoints == NULL)                       /* Nothing to do */
      return;

   pgqci_c( &oldcol    );
   pgqch_c( &oldchsize );
   pgqlw_c( &oldwidth  ); 
   
   setchsize( g->slicecom.marker2.size  );
   setcolor(  g->slicecom.marker2.color );
   setwidth(  g->slicecom.marker2.width );
   
   /*--------------------------------------------------*/
   /* Plot as many velocities as there are sample      */
   /* points in current slice. Note that the first     */
   /* point has offset 'xlogr' which is in grids.      */
   /*--------------------------------------------------*/
   for (i = 0; i < g->slice[nr].ndat; i++)
   {
      double  velgrid;
      double  vel;
      fint    r1;

      vel = g->slice[nr].velpoints[i];
      if (vel != fblank)
      /*----------------------------------------*/
      /* Note that these velocities came from a */
      /* GIPSY set which can contain blank      */
      /* values.                                */
      /*----------------------------------------*/
      {
         float   rad = ((float) (g->slice[nr].xlogr + i)) * g->slicecom.delta;

         vel *= g->slicecom.Vunits2head;
         r1 = phtogr_c( g->inset.Name, &(g->slicecom.Vcword), &vel, &velgrid );
         plsymbol( rad, velgrid, sym );
      }
   }
   setwidth(  oldwidth  );
   setchsize( oldchsize );
   setcolor(  oldcol    );  
}



void overlayslices( globaltype *g )
/*------------------------------------------------------------*/
/* PURPOSE: Overlay the slice lines in GIDS.                  */
/*------------------------------------------------------------*/
{
   fint     subset;
   fint     r;
   float    xtick = 0.0, ytick = 0.0;
   fint     nxsub = 0, nysub = 0; 
   float    x1, x2, y1, y2;
   fint     display_id;                     /* id of display */
   fint     display_stat;                   /* display operation status */
   int      symbol;
   int      i;
  
   display_id = gdi_open_c( tofchar(" ") );     /* open display device */
   if (display_id < 0)                          /* error opening display */
   {
      error_status( "Cannot plot slices in GIDS",
                    "No display server running!", "" );
      return;
   }
   display_stat = gdi_iinfo_c( &display_id ,        /* id of display */
                               g->gids.set ,        /* name of set */
                               &subset ,            /* subset level */
                               g->gids.blo,         /* lower left frame boundary */
                               g->gids.bhi );       /* upper right frame boundary */
  
   r = gdi_frame_c( &display_id ,    /* id of display */
                    g->gids.flo,     /* lower left frame boundary of total GIDS */
                    g->gids.fhi );   

   display_stat = gdi_close_c( &display_id );                /* close display */
   /*----------------------------------------*/
   /* Adjust window for GIDS overlays.       */
   /*----------------------------------------*/
   {
      float     Xl, Xr, Yb, Yt;

      Xl = (g->gids.blo[0]-g->gids.flo[0])/(g->gids.fhi[0]-g->gids.flo[0]);
      Xr = (g->gids.bhi[0]-g->gids.flo[0])/(g->gids.fhi[0]-g->gids.flo[0]);
      Yb = (g->gids.blo[1]-g->gids.flo[1])/(g->gids.fhi[1]-g->gids.flo[1]);
      Yt = (g->gids.bhi[1]-g->gids.flo[1])/(g->gids.fhi[1]-g->gids.flo[1]);
      /* All values are positive by definition (box fits in frame) */
      pgsvp_c(&Xl, &Xr, &Yb, &Yt);
   }
 

   x1 = (float) g->inset.blo[0];
   x2 = (float) g->inset.bhi[0];   
   y1 = (float) g->inset.blo[1];
   y2 = (float) g->inset.bhi[1];      
   pgswin_c( &x1, &x2, &y1, &y2 );
   pgbox_c( tofchar("BCNST" ), &xtick, &nxsub,
            tofchar("BCNSTV"), &ytick, &nysub ); 
   
   /* Plot galaxy position */
   setcolor( RED );
   plsymbol( g->inset.cpos[0], g->inset.cpos[1], DOTCIRCLE );       

   symbol = 2;  
   for (i = 0; i < g->slicecom.numslices; i++)
   {
      int  j;
      for (j = 0; j < g->slice[i].ndat; j++) 
      {
         setcolor((RED+i)%14);         
         plsymbol( g->slice[i].posX[j], g->slice[i].posY[j], symbol );
      }
      symbol++;
   }
}
#<

#>            coledit.c
/* coledit.c
                              COPYRIGHT (c) 1999
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands

Author: J.P. Terlouw
*/

#define NAME "Edit color map"

#include "stddef.h"
#include "stdio.h"
#include "gipsyc.h"
#include "userfio.h"
#include "ggi.h"
#include "colormenu.h"
#include "helptext.h"
#include "coledit.h"

static ident editor;
                                                                                                                                                                                                               
/* -------------------------------------------------------------------------- */
/*                                 popshell                                   */
/* -------------------------------------------------------------------------- */
/*  Keyword handler causing the shell to be popped up or down.
 */
static void popshell(ident id, char *key, int code, void *arg)
{  
   static bool state=FALSE;
   bool button=toflog(FALSE);
 
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      GgiShowShell((ident)arg, !state);
      if (!TriggerKey("CE_SLOPE1=")) wkeyf("CE_SLOPE1= 1.0");
      if (!TriggerKey("CE_SHIFT1=")) wkeyf("CE_SHIFT1= 0.0");
      state = !state;
      wkeyf(key);
   }
}

/* ========================================================================== */
/*                                  gui_applycol                              */
/* -------------------------------------------------------------------------- */
void gui_applycol(void)
{
   (void)GgiPlotColorEditor(ggiApply, editor);
}

/* ========================================================================== */
/*                                  gui_datarange                             */
/* -------------------------------------------------------------------------- */
void gui_datarange(float v1, float v2)
{
   (void)GgiPlotColorEditor(ggiLimits, editor, v1, v2);
}

/* ========================================================================== */
/*                                  gui_units                                 */
/* -------------------------------------------------------------------------- */
void gui_units(char *units)
{
   (void)GgiPlotColorEditor(ggiUnits, editor, units);
}

/* ========================================================================== */
/*                                  Coledit                                    */
/* -------------------------------------------------------------------------- */
/*   Coledit() creates a pop-up shell for editing colour map
 */
void  Coledit(char *key, int ci, int nc, int bl)
{
   static ident s_coledit;
   ident s_prev, frame_box;
   ident closebut;
    
   s_coledit = GgiShell(NAME);
   s_prev  = GgiUseShell(s_coledit);
   frame_box = GgiForm(NAME, 2);
   closebut = GgiSetLabel(GgiButton(key, "Close window"), "CLOSE", 0);
   GgiSetPosition(frame_box, 0, NULL, 0, NULL);
   GgiSetPosition(closebut, 0, NULL, 0, frame_box);
   
   GgiUseShell(frame_box);
   editor = GgiPlotColorEditor(ggiCreate, ci, nc, bl);
   (void)GgiUseShell(s_prev);
   GgiSetPosition(editor, 0, NULL, 0, NULL);
   ScheduleKeyevent(popshell, key, KEYCHANGE, s_coledit);
}
#<

#>            markers.c
/* markers.c
                              COPYRIGHT (c) 1999
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands

Author: J.P. Terlouw
*/

#define NAME "Markers"
#define KADERS 4

#include "stddef.h"
#include "stdio.h"
#include "gipsyc.h"
#include "userfio.h"
#include "ggi.h"
#include "radiogroup.h"
#include "colormenu.h"
#include "helptext.h"
#include "markers.h"

static char *widths[]={"_LINE", "1", "2", "3", "4", "5", "6", NULL};
static char *symb[]={"_LINE",
                     "dot",
                     "plus",
                     "asterisk",
                     "circle",
                     "cross",
                     "square",
                     "triangle",
                     "circle+plus",
                     "circle+dot", NULL};

static char *labels[]={"Slice plot marker attributes\n ",
                       "Tilted ring marker attributes\n ",
                       "Line-of-sight velocity markers\n ",
                       "Moving markers in XV slices\n "};

static bool changed[KADERS];

/* -------------------------------------------------------------------------- */
/*                                 popshell                                   */
/* -------------------------------------------------------------------------- */
/*  Keyword handler causing the shell to be popped up or down.
 */
static void popshell(ident id, char *key, int code, void *arg)
{  
   static bool state=FALSE;
   bool button=toflog(FALSE);
 
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      GgiShowShell((ident)arg, !state);
      state = !state;
      wkeyf(key);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 apply                                      */
/* -------------------------------------------------------------------------- */
static void apply(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
/* char message[KEYLEN];*/
   int  i;
    
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf(key);
      for (i=0; i<KADERS; i++) {
         if (changed[i]) {
            wkeyf("M%d_APPLY=", i);
            changed[i] = FALSE;
         }
      }
   }
}

/* -------------------------------------------------------------------------- */
/*                                 change                                     */
/* -------------------------------------------------------------------------- */
static void change(ident id, char *key, int code, void *arg)
{
   *(bool *)arg = TRUE;
}

/* ========================================================================== */
/*                                  Markers                                   */
/* -------------------------------------------------------------------------- */
/*   Markers() creates a pop-up shell with menus for obtaining inputed data.
 */
void  Markers(char *key)
{
   static ident s_markers;
   ident s_prev, frame_box, kader[KADERS];
   ident closebut, applybut, topref=NULL;
   int i;
    
   s_markers = GgiShell(NAME);
   s_prev  = GgiUseShell(s_markers);
   frame_box = GgiForm(NAME, 2);
   applybut  = GgiSetLabel(GgiButton("M_APPLY=", NULL), "APPLY", 0);
   ScheduleKeyevent(apply, "M_APPLY=", KEYCHANGE, changed);
   closebut = GgiSetLabel(GgiButton(key, "Close window"), "CLOSE", 0);
   GgiSetPosition(frame_box, 0, NULL, 0, NULL);
   GgiSetPosition(closebut, 0, NULL, 0, frame_box);
   GgiSetPosition(applybut, -GgiWidth(applybut), frame_box, 0, frame_box);
   
   GgiUseShell(frame_box);
   for (i=0; i<KADERS; i++) {
      kader[i] = GgiForm(NAME, 1);
      GgiSetPosition(kader[i], 0, NULL, 0, topref);
      topref = kader[i];
   }
    
   for (i=0; i<KADERS; i++) {
      ident label, width, size, color, symbol, symenu;
      char  key[KEYLEN];
      
      GgiUseShell(kader[i]);
      label = GgiLabel(labels[i]);
      sprintf(key, "M%d_LINEWIDTH=", i);
      ScheduleKeyevent(change, key, KEYCHANGE, &changed[i]);
      width = GgiSetLabel(GgiMenu(key, "Symbol line width", widths),
                          "Line width", 100);
      sprintf(key, "M%d_COLOUR=", i);
      ScheduleKeyevent(change, key, KEYCHANGE, &changed[i]);
      color = ColorMenu(key, "Colour", "Symbol colour");
      sprintf(key, "M%d_SYMBOL=", i);
      ScheduleKeyevent(change, key, KEYCHANGE, &changed[i]);
      symbol = GgiSetLabel(GgiTextField(key, "Symbol", 5),
                          "Symbol", 53);
      symenu = GgiSetLabel(GgiMenu(key, NULL, symb), "Symbol", 100);
      sprintf(key, "M%d_SIZE=", i);
      ScheduleKeyevent(change, key, KEYCHANGE, &changed[i]);
      size  = GgiSetLabel(GgiTextField(key, "Symbol size", 5),
                          "Size", 53);
      GgiSetPosition(label,  0, NULL,   0, NULL);
      GgiSetPosition(symbol, 0, NULL,   0, label);
      GgiSetPosition(symenu, 0, symbol, 0, label);
      GgiSetPosition(size,   0, NULL,   0, symbol);
      GgiSetPosition(width,  0, size,   0, symbol);
      GgiSetPosition(color,  0, size,   0, width);
   }

   (void)GgiUseShell(s_prev);
   ScheduleKeyevent(popshell, key, KEYCHANGE, s_markers);
}
#<

#>            blinker.c
/* blinker.c
                              COPYRIGHT (c) 1999
                        Kapteyn Astronomical Institute
         University of Groningen  -  9700 AV Groningen, The Netherlands

Author: J.P. Terlouw
*/

#define NAME "Blink"

#include "stddef.h"
#include "stdio.h"
#include "math.h"
#include "gipsyc.h"
#include "userfio.h"
#include "ggi.h"
#include "radiogroup.h"
#include "blinker.h"

static void logspeed(ident id, char *key, int code, void *arg);
static void realspeed(ident id, char *key, int code, void *arg);

static ident store_l, store1, store2,
             show_l,  show1,   show2,
             next, arun, fps_t, fps_g;

static ident plotter;
static int   frames[2];
static int   interval;
static fint  frame=0;
static bool  framepending=FALSE;    

/* -------------------------------------------------------------------------- */
/*                                 popshell                                   */
/* -------------------------------------------------------------------------- */
/*  Keyword handler causing the shell to be popped up or down.
 */
static void popshell(ident id, char *key, int code, void *arg)
{  
   static bool state=FALSE;
   bool button=toflog(FALSE);
 
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      GgiShowShell((ident)arg, !state);
      state = !state;
      wkeyf("SHOWRUN= SHOW0= SHOW1= %s", key);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 store                                      */
/* -------------------------------------------------------------------------- */
static void store(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
    
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      GgiPlotRecord(plotter, frames[(int)arg]);
      wkeyf("SHOW%d=YES %s", (int)arg, key);  /* select corresponding button */
   }
}

/* -------------------------------------------------------------------------- */
/*                                 show                                       */
/* -------------------------------------------------------------------------- */
static void show(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);

   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      frame = (int)arg;
      wkeyf("SHOW%d=", (frame+1)%2);                 /* reset other button */
      GgiPlotShow(plotter, frames[frame]);           /* show frame */
      framepending = FALSE;
   }
}

/* -------------------------------------------------------------------------- */
/*                                 shownext                                   */
/* -------------------------------------------------------------------------- */
static void shownext(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
 
   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button)) {
      wkeyf("SHOW%d=YES %s", (frame+1)%2, key);
   }
}   

/* -------------------------------------------------------------------------- */
/*                                 realspeed                                  */
/* -------------------------------------------------------------------------- */
/*  Keyword handler converting logarithmic to real speed.
 */
static void realspeed(ident id, char *key, int code, void *arg)
{
   float speed;
    
   (void)userfreal(&speed, 1, 2, key, " ");
   SuppressKeyevent(logspeed, (char*)arg);
   wkeyf("%s%.1f", (char*)arg, exp(speed));
   interval = 1000.0/exp(speed);
}

/* -------------------------------------------------------------------------- */
/*                                 logspeed                                   */
/* -------------------------------------------------------------------------- */
/*  Keyword handler converting real to logarithmic speed and updating
 *  the frame interval.
 */
static void logspeed(ident id, char *key, int code, void *arg)
{
   float speed;
    
   (void)userfreal(&speed, 1, 2, key, " ");
   if (speed>0.5) {
      SuppressKeyevent(realspeed, (char*)arg);
      wkeyf("%s%.1f", (char*)arg, log(speed));
      interval = 1000.0/speed;
   }
}

/* -------------------------------------------------------------------------- */
/*                                 player                                     */
/* -------------------------------------------------------------------------- */
static void player(ident id, void *arg)
{
   ident *timer=(ident *)arg;
   static int prvint=-1;

   if (!framepending) {             /* only if previous frame has been shown */
      wkeyf("SHOW%d=YES", (frame+1)%2);
      framepending = TRUE;
   }
   if (interval != prvint) {
      prvint = interval;
      Deschedule(timer);
      *timer = ScheduleTimer(player, interval, timer);
   }
}

/* -------------------------------------------------------------------------- */
/*                                 playmovie                                  */
/* -------------------------------------------------------------------------- */
/*  Keyword handler for starting and stopping the blink movie.
 */
static void playmovie(ident id, char *key, int code, void *arg)
{
   bool button=toflog(FALSE);
   static ident timer;

   (void)userflog(&button, 1, 2, key, " ");
   if (tobool(button) && !timer) {
      timer = ScheduleTimer(player, interval, &timer);
      GgiDeactivate(store1);
      GgiDeactivate(store2);
      GgiDeactivate(show1);
      GgiDeactivate(show2);
      GgiDeactivate(next);
   } else if (timer && !button) {
      Deschedule(&timer);
      GgiActivate(store1);
      GgiActivate(store2);
      GgiActivate(show1);
      GgiActivate(show2);
      GgiActivate(next);
   }
}

/* ========================================================================== */
/*                                  Blinker                                   */
/* -------------------------------------------------------------------------- */
/*   Blinker() creates a pop-up shell for recording and displaying frames.
 */
void Blinker(char *key, int f1, int f2)
{
   static ident s_blinker;
   ident s_prev, frame_box;
   ident closebut;
    
   s_blinker = GgiShell(NAME);
   s_prev  = GgiUseShell(s_blinker);
   frame_box = GgiForm(NAME, 2);
   closebut = GgiSetLabel(GgiButton(key, "Close window"), "CLOSE", 0);
   GgiSetPosition(frame_box, 0, NULL, 0, NULL);
   GgiSetPosition(closebut, 0, NULL, 0, frame_box);
   
   GgiUseShell(frame_box);
   store_l = GgiLabel("Store:");
   store1  = GgiSetLabel(GgiButton("STORE0=", NULL), "1", 0);
   store2  = GgiSetLabel(GgiButton("STORE1=", NULL), "2", 0);
   show_l  = GgiLabel("Show:");
   show1   = GgiSetLabel(GgiButton("SHOW0=", NULL), "1", 0);
   show2   = GgiSetLabel(GgiButton("SHOW1=", NULL), "2", 0);
   next    = GgiSetLabel(GgiButton("SHOWNEXT=", NULL), "Next", 0);
   arun    = GgiSetLabel(GgiButton("SHOWRUN=", NULL), "Blink", 0);
   fps_t   = GgiSetLabel(GgiTextField("SPEED=", "Speed (frames/s)", 4),
                         "Speed", 0);
   fps_g   = GgiSetLabel(GgiGauge("LSP=", NULL, 150,
                                   log(1.0), log(10.0)), " ", 1);

   GgiSetPosition(store_l,  0, NULL,    0, NULL);
   GgiSetPosition(store1,   0, store_l, 0, NULL);
   GgiSetPosition(store2,   0, store1,   0, NULL);
   GgiSetPosition(show_l,  20, store2,  0, NULL);
   GgiSetPosition(show1,    0, show_l,  0, NULL);
   GgiSetPosition(show2,    0, show1,   0, NULL);
   GgiSetPosition(next, -GgiWidth(next), arun,   0, NULL);
   GgiSetPosition(fps_t,    0, NULL,   10, store_l);
   GgiSetPosition(fps_g,    0, fps_t,  10, store_l);
   GgiSetPosition(arun,     0, fps_g,  10, store_l);
   (void)GgiUseShell(s_prev);

   ScheduleKeyevent(shownext, "SHOWNEXT=",  KEYCHANGE, NULL);
   ScheduleKeyevent(store, "STORE0=", KEYCHANGE, (void*)0);
   ScheduleKeyevent(store, "STORE1=", KEYCHANGE, (void*)1);
   ScheduleKeyevent(show, "SHOW0=", KEYCHANGE, (void*)0);
   ScheduleKeyevent(show, "SHOW1=", KEYCHANGE, (void*)1);
   ScheduleKeyevent(realspeed, "LSP=", KEYCHANGE, "SPEED=");
   ScheduleKeyevent(logspeed, "SPEED=", KEYCHANGE, "LSP=");
   ScheduleKeyevent(playmovie, "SHOWRUN=", KEYCHANGE, NULL);
   if (!TriggerKey("SPEED=")) wkeyf("SPEED=2.0");
   plotter = GgiPlotField("SLICES", 0, 0);
   frames[0] = f1; frames[1] = f2;
   
   ScheduleKeyevent(popshell, key, KEYCHANGE, s_blinker);
}
#<
