///Current version
///ID:          $Id: Frame.cs,v 1.1.1.1 2006/11/14 02:56:46 gupchup Exp $
///Revision:    $Revision: 1.1.1.1 $
///Date:        $Date: 2006/11/14 02:56:46 $

using System;
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;

namespace Geom
{

	/// <summary>
	/// Summary description for Frame.
	/// </summary>
	/// <summary>
	/// Frame private class carries the transformation of ra/dec to nu/mu and xy.
	/// This is specific to a particular survey. 
	/// </summary>
	public class Frame
	{

		public static string Revision
		{
			get 
			{
				return "$Revision: 1.1.1.1 $";
			}
		}
		
		public static float		FrameWidth		= 2048.0F;						// pixel
		public static float		FrameHeight		= 1489.0F;						// pixel
		public static double	FrameHalfDiag	= 8.4F;							// arcMinute
		public static float		ClipX			= 32.0F;						// clip from the edge
		public static float		ClipY			= 64.0F;						// clip from the edge
		public static int		PixelsPerDegree	= 9088;						    // pixel
		public static double	PixelsPerArcMin	= PixelsPerDegree /60.0;		// 151.466667 = 9088 pixels per degree
		public static double	plateRadiusArcMin	= 89.4;						// arcMinute
		public static double	plateRadiusDeg		= 1.49;						// degrees
		
		public double ra,dec;			// point (ra, dec) in J2000 degrees
		public double mu,nu;			// point (ra, dec) in J2000 degrees
		public double row, col;			// the row and column number in the frame
		private float x,y;				// pixel offsets from upper left corner 
										// corresponding to (ra,dec), scaled 
		public double scale;			// image scale 
		private double a,b,c,d,e,f,node,incl;	// affine tranformation of the stripe to ra,dec
		public string info;				// information about the field for debugging
		public string fieldid;			// string representation of the fieldid
		public Matrix m;
		public Image tile;

		private static double D2R = Math.PI/180.0;		// degrees to radians
		
		public Frame()
		{}

		public Frame(SqlDataReader reader, float sc, bool drawFrames)
		{
			a = Convert.ToDouble(reader[0]);	// a
			b = Convert.ToDouble(reader[1]);	// b 
			c = Convert.ToDouble(reader[2]);	// c
			d = Convert.ToDouble(reader[3]);	// d
			e = Convert.ToDouble(reader[4]);	// e
			f = Convert.ToDouble(reader[5]);	// f
			node  = Convert.ToDouble(reader[6]);	// node
			incl  = Convert.ToDouble(reader[7]);	// inclination
			fieldid = Convert.ToString(reader[10]);	// fieldid
			info  = Convert.ToString(reader[11]);	// info
			scale = sc;								// scale
			tile  = null;
			if (drawFrames) 
				tile = Image.FromStream(new MemoryStream((Byte[]) reader[12]));
		}

		/// <summary>
		/// The copy method copies the values of the coord parameter into the current coordinate object
		/// </summary>
		/// <param name=coord>			
		public void copy( Frame c) 
		{
			this.a			= c.a;
			this.b			= c.b;
			this.c			= c.c;
			this.d			= c.d;
			this.e			= c.e;
			this.f			= c.f;
			this.node		= c.node;
			this.incl		= c.incl;
			this.scale		= c.scale;
			this.info		= c.info;
			this.m			= c.m;
			this.tile		= c.tile;
		}

		/// <summary>
		/// EqtoScreen goes from equatorial coordinates to xy
		/// Sets ra, dec and consequently xOffset and yOffset of 
		/// Coord according to the astrometric transformation.
		/// </summary>
		/// <param name="ra">right ascension degrees in J2000</param>
		/// <param name="dec">declination degrees in J2000</param>
		public PointF EqToFrame(double pRa, double pDec) 
		{   
			//-----------------
			// save the angles
			//-----------------
			ra	= pRa;
			dec	= pDec;

			//---------------------------
			// convert angles to radians
			//---------------------------
			double inclR = incl*D2R;
			double raR	 = (pRa-node) *D2R;
			double decR	 = pDec*D2R;

			//---------------------
			// first go to (mu,nu)
			//---------------------
			double gx	=  Math.Cos(raR) * Math.Cos(decR);
			double gy	=  Math.Sin(raR) * Math.Cos(decR);
			double gz	=  Math.Sin(decR); 	

			//-----------------------------------
			// reverse rotation by incl around x
			//-----------------------------------
			double qx	=  gx;
			double qy	=  gy*Math.Cos(inclR) + gz * Math.Sin(inclR);
			double qz	= -gy*Math.Sin(inclR) + gz * Math.Cos(inclR);

			//----------------
			// compute mu, nu
			//----------------
			mu	=  Math.Atan2(qy,qx)/D2R + node;
			nu	=  Math.Asin(qz)/D2R;

			//-----------------------------------------
			// adjust for wraparound of the difference
			//-----------------------------------------
			double dmu	=  mu - a;
			if (dmu<-180)  dmu += 360;
			if (dmu> 180)  dmu -=360; 
	
			//------------------------
			// set up the determinant
			//------------------------
			double det	 = b * f - e * c;
			double col   = ( (nu-d)*b - dmu*e )/det;
			double row   = ( dmu*f - (nu-d)*c )/det;
				
			//---------------------------
			// compute pixel coordinates
			//---------------------------
			x	= (float)(col/scale);
			y	= (float)(row/scale);

			PointF p = new PointF(x,y);
			return p; 
		} 

		/// <summary>
		/// FrameToEq goes from Frame x,y coordinates and sets ra and dec
		/// </summary>
		/// <param name="x">pixels across (scaled for zoom level)</param>
		/// <param name="y">pixels down (scaled for zoom level)</param>
		public PointEq FrameToEq(float x, float y)  
		{
			//----------------------------------------
			// x,y need to be in original pixel scale
			//----------------------------------------
			double col = x;
			double row = y;
    
			//-------------------------------------
			// Great circle coordinates in degrees
			//-------------------------------------
			mu = (a +  b*row  +  c*col);
			nu = (d +  e*row  +  f*col);

			//------------------------------------
			// convert to radians, then to ra,dec
			//------------------------------------
			double muR = (mu - node) * D2R;
			double nuR = nu   * D2R;
			double xg  = Math.Cos(muR) * Math.Cos(nuR);
			double yg  = Math.Sin(muR) * Math.Cos(nuR);
			double zg  = Math.Sin(nuR);

			// compute unit vector
			double inR = incl * D2R;
			double qx   = xg;
			double qy   = yg * Math.Cos(inR) - zg*Math.Sin(inR);
			double qz   = yg * Math.Sin(inR) + zg*Math.Cos(inR);

			// compute ra, dec
			double pRa  = Math.Atan2(qy,qx) / D2R  +  node;
			double pDec = Math.Asin(qz) / D2R;
			if (pRa>360) pRa -= 360;
			if (pRa<0)   pRa += 360;

			ra	= pRa;
			dec	= pDec;

			return new PointEq(pRa,pDec);
		}

		/// <summary>
		/// GetFrameSQL returns the SQL string required to fetch Frame
		/// </summary>
		/// <param name="raCenter">right ascension of center point</param>
		/// <param name="decCenter">declination of center point</param>
		/// <param name="fradius">frame radius in arcminutes</param>
		/// <param name="zoom">integer zoom factor (0 is unscaled)</param>
		public static String GetFrameSQL(double raCenter, double decCenter, 
			double fradius, int zoom, bool drawFrames )  
		{
			int zoom10x = (zoom>3)? 30: zoom * 10;		// for DB lookup, clipped at 30
			String sQ = "";

			sQ +=	 "select f.a, f.b, f.c, f.d, f.e, f.f"
					+", f.node, f.incl, f.ra, f.dec " 
					+", f.fieldID, dbo.fSDSS(f.fieldID) ";

			//------------------------------------------------
			// only read the JPG bytes if we draw the frames
			//------------------------------------------------
			if (drawFrames) sQ += ", f.img \n";

			sQ += "from  dbo.fGetNearbyFrameEq ("
				+raCenter.ToString("F6")+","+decCenter.ToString("F6")+","
				+fradius.ToString("F6")+","+zoom10x.ToString()+") as n"
				+", frame f \n"
				+"where f.fieldID = n.fieldID and zoom ="
				+zoom10x.ToString();

			return (String) sQ;
		}
		
		/// <summary>
		/// FieldGeometry. Returns the four corners of the field in unscaled pixel 
		/// coordinates with optional clipping
		///  </summary>
		public static PointF[]	FieldGeometry(bool clip)
		{
			//-------------------------------------------------------------------
			// compute the pixel coordinates of corners with optional clipping
			//-------------------------------------------------------------------
			int clipit	= (clip?1:0);
			float fX1	= (float) (ClipX * clipit);
			float fY1	= (float) (ClipY * clipit);
			float fX2	= (float) (FrameWidth  - ClipX * clipit);
			float fY2	= (float) (FrameHeight - ClipY * clipit);

			//------------------------------------
			// create array for the four corners
			//------------------------------------
			PointF[] p = new PointF[4];

			p[0] = new PointF(fX1, fY1);
			p[1] = new PointF(fX2, fY1);
			p[2] = new PointF(fX2, fY2);
			p[3] = new PointF(fX1, fY2);

			return p;
		}

	} // end of Frame Class

	/// <summary>
	/// The SdssConstants class holds all constants related to SDSS.
	/// </summary>


}
/* Revision History

        $Log: Frame.cs,v $
        Revision 1.1.1.1  2006/11/14 02:56:46  gupchup
        no message

        Revision 1.1.1.1  2006/04/25 16:07:23  gupchup
        Code base that merges original imgcutout code and WCS frame format imgcutout code

        Revision 1.3  2003/04/08 17:12:00  nieto
        Trying to include projection.cs class in project

        Revision 1.2  2003/03/25 23:18:33  nieto
        testing draw fields with their own coord		
*/
