/* @(#)ast_project.c	19.1 (ES0-DMD) 02/25/03 13:53:54 */
/*===========================================================================
  Copyright (C) 1995 European Southern Observatory (ESO)
 
  This program is free software; you can redistribute it and/or 
  modify it under the terms of the GNU General Public License as 
  published by the Free Software Foundation; either version 2 of 
  the License, or (at your option) any later version.
 
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
 
  You should have received a copy of the GNU General Public 
  License along with this program; if not, write to the Free 
  Software Foundation, Inc., 675 Massachusetss Ave, Cambridge, 
  MA 02139, USA.
 
  Corresponding concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*++++++++++
.TYPE           Module
.IDENTIFICATION projection.c
.VERSION 1.0	05-Mar-1987: Creation
.VERSION 1.1	08-Sep-1988: Inclusion of several projections
.VERSION 1.2	16-Oct-1989: Change atanh -> 0.5*ln((1+x)/(1-x)), PJG
.Language       C
.AUTHOR         Francois Ochsenbein [ESO-IPG]  
.KEYWORDS       Projection of coordinates.

.ENV            Any

.COMMENTS
All spherical coordinates are assumed to be expressed in DEGREES.

\begin{TeX}

All projections defined here have the center assumed to be at
$(\ell=0, b=0)$, i.e. $x=1$.
      The following notations are used:
\begin{itemize}
\item {\tt \_PROJ\_}... specifies the type of the projection
\item $(\ell, b)$ the longitude and latitude
\item $\vec{u} = (x, y, z)$ is unit vector (cosine direction) related to
	$(\ell, b)$ by		
	$$ \left\{ \begin{array}{rcl}
                   x & = & \cos b \cos \ell \\
                   y & = & \cos b \sin \ell \\
	                   z & = & \sin b 
		   \end{array}
	   \right. 
	$$
\item	$\theta$ is the distance from center on the celestial sphere, 
	i.e. $\theta = \cos^{-1} x$
\item 	$(X, Y)$ are the projections
\item	$\rho$ is the distance from center on the projection,
	i.e. $\rho = \sqrt{X^2 + Y^2}$
\end{itemize}

\renewcommand{\arraystretch}{2.0}

The projections are:
\large

$$\begin{tabular}{|l|ll|l|}
\hline
	{\tt \_PROJ\_} & \multicolumn{2}{|c|}{Projection formulae} & Notes \\
\hline
 {\tt TAN\_}$^{1 \dagger}$ 	& $ X = \frac{y}{x}$ 
 				& $ Y = \frac{z}{x}$ 
 				& $ \rho = \tan \theta$ \\
%\hline
 {\tt TAN2\_}$^2$ 		& $ X = \frac{2y}{1+x}$ 
 				& $ Y = \frac{2z}{1+x}$ 
 				& $\rho = 2 \tan\frac{\theta}{2}$ \\
%\hline
 {\tt SIN\_$^\dagger$} 			& $ X = y $ 
 				& $ Y = z $ 
 				& $\rho = \sin \theta$ \\
%\hline
 {\tt SIN2\_$^\bullet$} 	& $ X = y \sqrt{\frac{2}{1+x}}$ 
				& $ Y = z \sqrt{\frac{2}{1+x}}$ 
				& $\rho = 2 \sin\frac{\theta}{2}$ \\
%\hline
 {\tt ARC\_}$^3$ 		& $ X = y \frac{\cos^{-1}{x}}{\sqrt{y^2+z^2}}$ 
 				& $ Y = z \frac{\cos^{-1}{x}}{\sqrt{y^2+z^2}}$ 
 				& $\rho = \theta$ \\
%\hline
 {\tt GLS\_$^{4 \bullet}$} 
		& $ 	X = \ell \cos b $
		& $	Y = b $ 
		&  {\normalsize $ 	\ell\in[-\pi,+\pi]$} \\
%\hline
 {\tt MERCATOR\_} 
		& $ 	X = \ell $
		& $ 	Y = \tanh^{-1}(\sin{b}) $
		&  {\normalsize $ 	\ell\in[-\pi,+\pi]$} \\
 {\tt AITOFF\_$^\bullet$} 
		& $	X = 2\frac{\cos b\sin\frac{\ell}{2}}{\Delta} $
		& $	Y = \frac{\sin b} {\Delta} {\qquad} $ 
		&  {\normalsize $ 	\ell\in[-\pi,+\pi]$} \\
		& \multicolumn{2}{c|}{
		$\Delta = \sqrt{\frac{1+\cos b\cos\frac{\ell}{2}}{2}}$} & \\
%\hline
\hline
\end{tabular}$$
\normalsize
\renewcommand{\arraystretch}{1.0}
{\bf Notes}: \qquad \qquad \begin{tabular}{cl}
            $\bullet$ 	& Equal area projection \\
            $\dagger$ 	& Projection restricted to $\theta \leq \frac{\pi}{2}$ \\
            1		& Gnomonic (standard) projection \\
            2		& Stereographic projection \\
            3		& Schmidt plates projection \\
            4		& Global Sinusoidal projection \\
            \end{tabular}


\bigskip

The computations of $(x , y , z)$ from the projections
$(X , Y)$ are as follows:
\renewcommand{\arraystretch}{2.0}
\large
$$\begin{tabular}{|l|lll|}
\hline
	{\tt \_PROJ\_} & \multicolumn{3}{c|}{Unit vector components} \\
\hline
 {\tt TAN\_} 		& $ x = \frac{1}{\sqrt{1+\rho^2}}$ 
			& $ y = \frac{X}{\sqrt{1+\rho^2}}$ 
			& $ z = \frac{Y}{\sqrt{1+\rho^2}}$ \\
%\hline
 {\tt TAN2\_}	& $ x = \frac{4-\rho^2}{4+\rho^2}$ 	
	 	& $ y = \frac{4X}{4+\rho^2}$ 	
	 	& $ z = \frac{4Y}{4+\rho^2}$ 	\\
%\hline
 {\tt SIN\_}		& $ x = \sqrt{1-\rho^2} $ 
			& $ y = X $ 
			& $ z = Y $ \\
%\hline
 {\tt SIN\_2} 		& $ x = 1 - \frac{\rho^2}{2}$ 
			& $ y = X \sqrt{1-\frac{\rho^2}{4}} $
			& $ z = Y \sqrt{1-\frac{\rho^2}{4}} $ \\
%\hline
 {\tt ARC\_}		& $ x = \cos \rho $
 			& $ y = X \frac{\sin \rho}{\rho}  $
 			& $ z = Y \frac{\sin \rho}{\rho}  $  \\
%\hline
 {\tt GLS\_} 
		& $ 	x = \beta \cos \frac{X}{\beta} $
		& $ 	y = \beta \sin \frac{X}{\beta} $
		& $	z = \sin Y $ \\
   & \multicolumn{3}{|c|}{$\beta = \sqrt{1-Y^2}$}\\
%\hline
 {\tt MERCATOR\_} 
		& $ 	x = \frac{\cos X}{\cosh Y}$
		& $ 	y = \frac{\sin X}{\cosh Y}$
		& $ 	z = \tanh Y $ \\
 {\tt AITOFF\_} 	
    	& $ x = \frac{1 - \Delta^2(\frac{X^2}{2} + Y^2)}{\sqrt{1-Y^2\Delta^2}} $
    	& $ y = \frac{X\Delta(2\Delta^2-1)}{\sqrt{1-Y^2\Delta^2}}$
    	& $ z = Y \Delta $  \\
   & \multicolumn{3}{|c|}{$\Delta = \sqrt{1-\frac{X^2}{16}-\frac{Y^2}{4}}$}\\
\hline
\end{tabular}$$
\normalsize
\renewcommand{\arraystretch}{1.0}


To compute projected coordinates around 
	any $o_0 = (\ell_0, b_0)$ center:
\begin{enumerate}
\item 
	determine first the rotation matrix $R$
	with {\em tr\_oR}($o_0$, $R$)

\item	for each position $o$, 
	apply successively 
	
	{\em tr\_ou}($o$, $u$), {\em tr\_uu}($u$, $u$, $R$),
	and {\em tr\_up}({\em method}, $u$, $p$). 
	
	Conversions from projections are similarly obtained
	with successive applications of 
	
	{\em tr\_pu}({\em method}, $p$, $u$), 
	{\em tr\_uu1}($u$, $u$, $R$), and
	{\em tr\_uo}($u$, $o$).
\end{enumerate}

\end{TeX}

-----------------------------------------------------------------------*/
 

#include <astro.h>
#include <trigo.h>
#include <osdefos.h>	
#include <ok.h>

#define DEBUG	0		/* Debugging option			*/

                               /* the following #define help to understand  */
#define lon          o[0] 
#define lat          o[1]
#define x            u[0]
#define y            u[1]
#define z            u[2]
#define X            p[0]
#define Y            p[1]

/*============================================================================*/

int tr_op (c, o , p)
/*+++		
.PURPOSE   Compute projection (center is at coordinate (0,0))
.RETURNS  OK / NOK (Bad projection system)
.METHOD	 Use unit vectors for projections.
----------*/
	int	c;	/* IN: The projection system	*/
	double o[2]; 	/* IN: Polar angles to project */
	double p[2]; 	/* OUT: Projections in specified system */
{
	double us[3];
	double cosb, l2, den;
	int    ret;

  tr_ou(o, us);
  return(tr_up(c, us, p));
}

/*============================================================================*/
int tr_po ( c, p , o )   
/*+++		
.PURPOSE  Derive equatorial coordinates from a projection 
	centered at position (0,0).
.METHOD   unit vectors are used.
.RETURNS  NOK ifor bad projection system or point outside limits,
	OK otherwise
---*/
	int	c;	/* IN: The projection system	*/
	double p[2]; 	/* IN: Projections in specified system	*/
	double o[2]; 	/* OUT: Polar angles         */
{ 
	double 	us[3];
	int	ret;

  ret = tr_pu(c, p, us);
  if (ret)	tr_uo(us, o);
  return(ret);
}

/*============================================================================*/
/*+++			tr_pu
.PURPOSE  Computes Unit vector from a position in projection
		centered at position (0,0).
.RETURNS  OK if anything works,
	NOK if the point is outside the limits, 
	or if the projection is unknown.
---*/
int tr_pu ( c, p , u )  
	int	c;	/* IN: The projection system	*/
	double p[2]; 	/* IN: Projection in `c' system */
	double u[3]; 	/* OUT: unit vector          */
{
	double 	r,t;
	int	ret;

  ret = OK;

  switch(c)
  { default:
	ret = NOK;
  	break;

    case _PROJ_AITOFF_:		/* Limit is ellipse with axises 
    				 * a = 2 * sqrt(2) ,  b = sqrt(2)  */
                           		/*     Compute dir l/2, b  */
	r = X*X/8.e0 + Y*Y/2.e0; 	/* 1 - cos b . cos l/2 */
	if (r > 1.e0)  			/* Test outside domain */
     		{ ret = NOK; break; }
	x = 1.e0 - r ;   		/*     cos b . cos l/2 */
	t = sqrt(1.e0 - r/2.e0) ;   	/* sqrt(( 1 + cos b . cos l/2)/2) */
	y = X * t / 2.e0;
	z = Y * t ;
					/* From (l/2,b) to (l,b)   	*/
	r = hypot ( x, y ) ;		/* cos b			*/
	if (r)	
	{	t = x;
		x = (t*t - y*y) /r;
		y = 2.e0 * t * y/r;
	}
	break;

    case _PROJ_GLS_:		/* Limit is |Y| <= pi/2 */
	z = sin(Y);
	r = 1 - z*z;		/* cos(b) ** 2	*/
	if (r < 0.0e0)
		{ ret = NOK; break; }
	r = sqrt(r);		/* cos b	*/
	if (r)	t = X/r;	/* Longitude	*/
	else	t = 0.e0;	/* For poles	*/
	x = r * cos(t);
	y = r * sin(t);
	break;

    case _PROJ_MERCATOR_:
	z = tanh(Y), r = 1.e0/cosh(Y);
	x = r * cos(X);
	y = r * sin(X);
	break;

    case _PROJ_TAN_:		/* No limit			*/
	x = 1.e0 / sqrt(1.e0 + X*X + Y*Y);
	y = X * x;
	z = Y * x;
	break;

    case _PROJ_TAN2_:		/* No limit			*/
	r = (X*X + Y*Y)/4.e0;
	t = 1.e0 + r;
	x = (1.e0 - r)/t;
	y = X / t;
	z = Y / t;
	break;

    case _PROJ_ARC_:		/* Limit is circle, radius PI	*/
	r = hypot(X, Y);
	if (r > PI)	
		{ ret = NOK; break; }
	t = sinc(r);
	x = cos(r);
	y = t * X;
	z = t * Y;
	break;

    case _PROJ_SIN_:		/* Limit is circle, radius 1	*/
	t = 1.e0 - X*X - Y*Y;
	if (t < 0.e0)
		{ ret = NOK; break; }
	x = sqrt(t);
	y = X;
	z = Y;
	break;

    case _PROJ_SIN2_:		/* Limit is circle, radius 2	*/
	r = (X*X + Y*Y)/4.e0;
	if (r > 1.e0)
		{ ret = NOK; break; }
	t = sqrt(1.e0 - r);
	x = 1.e0 - 2.e0 * r;
	y = t * X;
	z = t * Y;
	break;
  }

  if (ret == NOK)	x = 0.e0, y = 0.e0, z = 0.e0;

  return(ret);
}

/*============================================================================*/
/*+++			tr_up
.PURPOSE Compute projections from unit vector.
\begin{TeX}
The center of the projection corresponds to $\vec{u} = (1, 0, 0)$.
\end{TeX}
.RETURNS - NOK if u == 0 (not a unit vector)
         - non-zero value otherwise
---*/
int tr_up(c, u , p )   
	int	c;	/* IN: The projection system	*/
	double u[3]; 	/* IN: Unit vector */
	double p[2]; 	/* OUT: Projections */
{
	double  r, den;
	int	ret;	/* Status */

#if DEBUG
  printf("tr_up: proj.%d -- direction=%13.9f,%13.9f,%13.9f\n",c,x,y,z);
#endif
  ret = OK;

  r = hypot(x,y);			/* r = cos b	*/
  if ( (r == 0.e0) && (z == 0.e0))
  	{ ret = NOK; goto FIN; }

  switch(c)
  { default:
  	ret = NOK;
  	break;

    case _PROJ_AITOFF_:
	den = sqrt (r*(r+x)/2.e0); 		/*   cos b . cos l/2 */
	X = sqrt(2.*r*(r-x));
	den = sqrt ((1.e0 + den)/2.e0); 
	X   = X / den;
	Y   = z / den;
      	if (y < 0.e0)	X = -X;
	break;

    case _PROJ_GLS_:
    	Y = asin(z);				/* sin b 	*/
	if (r)	X = atan2(y,x) * r;		/* l cos b 	*/
	else	X = 0.e0;
	break;

    case _PROJ_MERCATOR_:
	if (r)	X = atan2(y,x), Y = 0.5 * log((1.0+z)/(1.0-z));
	else	ret = NOK;
	break;

    case _PROJ_TAN_:
	if (x > 0.e0)	X = y/x, Y = z/x; 
	else		ret = NOK;
	break;

    case _PROJ_TAN2_:
    	den = (1.e0 + x)/2.e0;
    	if (den > 0.e0)	X = y/den, Y = z/den;
    	else		ret = NOK;
    	break;

    case _PROJ_ARC_:
	if (x <= -1.e0)			/* Distance of 180 degrees */
		X = PI, Y = 0.e0;
	else				/* Arccos(x) = Arcsin(r)   */
	{	r = hypot(y,z);
		if (x > 0.e0)	den = asinc(r);
		else		den = acos(x)/r;
		X = y * den;
		Y = z * den;
	}
    	break;

    case _PROJ_SIN_:
	if (x >= 0.e0)	X = y, Y = z; 
	else		ret = NOK;
	break;

    case _PROJ_SIN2_:	/* Always possible */
	den = sqrt((1.e0 + x)/2.e0);
	if (den)	X = y / den, Y = z / den;
	else		X = 2.e0, Y = 0.e0;	/* For x = -1 */
	break;
  }

  FIN:
  return(ret);
}


