/**---------------------------------------------------------------------------
 ** 
 ** pos.c -- Miscellaneous position functions
 ** 
 ** Author          : Pierre Jaccard
 ** Created On      : 1999/07/14 21:14:25
 ** Last Modified By: Pierre Jaccard
 ** Last Modified On: 1999/09/21 11:19:44
 ** Update Count    : 13
 ** Directory       : /pcdata1/jaccard/codas3c/gfi/src/libs/misc/
 ** Version         : 0.0
 ** Status          : Unknown
 ** ---------------------------------------------------------------------- ** 
 ** DESCRIPTION: 
 ** 
 **    This file provides functions for handling geographical positions that
 **    come in addition to those defined in the original CODAS files.
 ** 
 ** ---------------------------------------------------------------------- ** 
 ** REVISIONS: 
 ** ---------------------------------------------------------------------- ** 
 ** CHANGES: 
 **------------------------------------------------------------------------**/

#include "pos.h"

/* ------------------------------------------------------------------
	Calculate distance in meters from start to stop positions.

	1998/06/06 Pierre Jaccard
	For small position differences, the resulting displacement can vary from one 
	architecture to the other, because the position difference can be +/- 1
	hundredth of second biased. Using this in further calculations may lead to
	different results.

  RETURNS: the difference in meters between both positions.
	------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
DOUBLE posdiff_meter(
                     /* Start longitude */
                     DMSH_POSITION_TYPE *lon1, 
                     /* Start latitude */
                     DMSH_POSITION_TYPE *lat1,
                     /* Stop longitude */
                     DMSH_POSITION_TYPE *lon2, 
                     /* Stop latitude */
                     DMSH_POSITION_TYPE *lat2)
#else
DOUBLE posdiff_meter(lon1, lat1, lon2, lat2)
     DMSH_POSITION_TYPE *lon1;
     DMSH_POSITION_TYPE *lat1;
     DMSH_POSITION_TYPE *lon2;
     DMSH_POSITION_TYPE *lat2;
#endif
{

	DOUBLE avg_lat, dlat, dlon, dx, dy;

	/* Average latitude */
	avg_lat = ((DOUBLE) (POSHUN(lat1)
											 + POSHUN(lat2)))/(2.0*360000.0);

	/* Latitude and longitude differences */
	dlat = ((DOUBLE) POSDIF(lat1, lat2))/360000.0;
	dlon = ((DOUBLE) POSDIF(lon1, lon2))/360000.0;
	
	/* Idem, in meters */
	dx = (DOUBLE) dlon_to_meters(dlon, avg_lat);
	dy = (DOUBLE) dlat_to_meters(dlat, avg_lat);
	
	return((DOUBLE) sqrt(dx*dx + dy*dy));

}

/* ------------------------------------------------------------------
   As posdiff_meter(), but argumetns are given in decimal degrees. This
   function uses posdiff_meter(), so remarks for the latter are also valid for 
   this one.
   
   RETURNS: the difference in meters between both positions.
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
DOUBLE posdiff_meter_d(/* Start longitude */
                       double lon1, 
                       /* Start latitude */
                       double lat1, 
                       /* Stop longitude */
                       double lon2, 
                       /* Stop latitude */
                       double lat2)
#else
DOUBLE posdiff_meter_d(lon1, lat1, lon2, lat2)
     double lon1;
     double lat1;
     double lon2;
     double lat2;
#endif
{

	DMSH_POSITION_TYPE dmsh_lat1, dmsh_lon1, dmsh_lat2, dmsh_lon2;
	LONG hun_lat1, hun_lon1, hun_lat2, hun_lon2;
	double d;

	/* Convert positions in hundredth of seconds */
	hun_lat1 = (LONG) (lat1 * 360000.0);
	hun_lat2 = (LONG) (lat2 * 360000.0);
	hun_lon1 = (LONG) (lon1 * 360000.0);
	hun_lon2 = (LONG) (lon2 * 360000.0);

	/* Convert positions in DMSH_POSITION_TYPE */
	HUNPOS(&dmsh_lat1, &hun_lat1);
	HUNPOS(&dmsh_lon1, &hun_lon1);
	HUNPOS(&dmsh_lat2, &hun_lat2);
	HUNPOS(&dmsh_lon2, &hun_lon2);

	/* Calculate distance in meters */
	d = posdiff_meter(&dmsh_lon1, &dmsh_lat1, &dmsh_lon2, &dmsh_lat2);

	return(d);

}

/* ------------------------------------------------------------------
   Calculate direction in decimal degrees (heading) between two positions.

   RETURNS: the direction from the start to the stop position in degrees. 
	------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
DOUBLE posdiff_direction(/* Start longitude */
                         DMSH_POSITION_TYPE *lon1, 
                         /* Start latitude */
                         DMSH_POSITION_TYPE *lat1,
                         /* Stop longitude */
                         DMSH_POSITION_TYPE *lon2, 
                         /* Stop latitude */
                         DMSH_POSITION_TYPE *lat2)
#else
DOUBLE posdiff_direction(lon1, lat1, lon2, lat2)
     DMSH_POSITION_TYPE *lon1;
     DMSH_POSITION_TYPE *lat1;
     DMSH_POSITION_TYPE *lon2;
     DMSH_POSITION_TYPE *lat2;
#endif
{

	DOUBLE avg_lat, dlat, dlon, dx, dy, hdg;

	/* Average latitude */
	avg_lat = ((DOUBLE) (POSHUN(lat1)
											 + POSHUN(lat2)))/(2.0*360000.0);

	/* Latitude and longitude differences */
	dlat = ((DOUBLE) POSDIF(lat1, lat2))/360000.0;
	dlon = ((DOUBLE) POSDIF(lon1, lon2))/360000.0;
	
	/* Idem, in meters */
	dx = (DOUBLE) dlon_to_meters(dlon, avg_lat);
	dy = (DOUBLE) dlat_to_meters(dlat, avg_lat);
	
	if(dx != 0.0){
		hdg = atan(dy/dx) * 180.0 / M_PI;
		hdg = (dx < 0.0) ? hdg + 180.0 : hdg;
		hdg = to360(90.0 - hdg);
	}
	else
		hdg = BADDOUBLE;

	return(hdg);

}

/* ------------------------------------------------------------------
	 This function is similar to posdiff_direction(), but input arguments are
   given decimal degrees.

   RETURNS: the direction from the start to the stop position in degrees. 
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
DOUBLE posdiff_direction_d(/* Start longitude */
                           double lon1, 
                           /* Start latitude */
                           double lat1, 
                           /* Stop longitude */
                           double lon2, 
                           /* Stop latitude */
                           double lat2)
#else
DOUBLE posdiff_direction_d(lon1, lat1, lon2, lat2)
     double lon1;
     double lat1;
     double lon2;
     double lat2;
#endif
{

	DMSH_POSITION_TYPE dmsh_lat1, dmsh_lon1, dmsh_lat2, dmsh_lon2;
	LONG hun_lat1, hun_lon1, hun_lat2, hun_lon2;
	double d;

	/* Convert positions in hundredth of seconds */
	hun_lat1 = (LONG) (lat1 * 360000.0);
	hun_lat2 = (LONG) (lat2 * 360000.0);
	hun_lon1 = (LONG) (lon1 * 360000.0);
	hun_lon2 = (LONG) (lon2 * 360000.0);

	/* Convert positions in DMSH_POSITION_TYPE */
	HUNPOS(&dmsh_lat1, &hun_lat1);
	HUNPOS(&dmsh_lon1, &hun_lon1);
	HUNPOS(&dmsh_lat2, &hun_lat2);
	HUNPOS(&dmsh_lon2, &hun_lon2);

	/* Calculate distance in meters */
	d = posdiff_direction(&dmsh_lon1, &dmsh_lat1, &dmsh_lon2, &dmsh_lat2);

	return(d);

}

/* ------------------------------------------------------------------
	 From file posn.c, no position is allowed and is coded with BADSHORT in each
	 DMSH structure field. Bad values here are therefore different, and
	 correspond to the same as those used for writting bad position. The value
	 of lat controls if the position is a latitude or a longitude.

   RETURNS: 0
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int dmsh_to_bad(/* Position structure */
                DMSH_POSITION_TYPE *pos, 
                /* True if position is a latitude */
                int lat)
#else 
int dmsh_to_bad(pos, lat)
     DMSH_POSITION_TYPE *pos;
     int lat;
#endif
{

	if(lat)
		pos->degree  =  99;
	else
		pos->degree  = 999;
	pos->minute    =  99;
	pos->second    =  99;
	pos->hundredth =  99;

	return(0);

}

/* ------------------------------------------------------------------
	 Given (i,j) indexes to a rectangular grid, this function fills the
	 corresponding latitude and longitudes using an inverse stereographic
	 projection. Because the geographical grid is not a rectangular grid, all
	 grid points have to be converted. Hence, lat and lon must point to 

	    (nx * ny)

	 arrays.

	 RETURNS: lat and lon filled with latitudes and longitudes in decimal
            degrees. Longitude values range from 0 to 360. 
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int inv_polar_stereo_proj(/* x coordinate of North pole */
                          double xp, 
                          /* y coordinate of North pole */
                          double yp, 
                          /* number of grid units between North pole and
                             Equator */
                          double an, 
                          /* angle of rotation between y-axis and 0 longitude,
                             or longitude being parallel to y-axis (+ east, -
                             west) */ 
                          double fi, 
													/* vector of x points */
                          int *x, 
                          /* vector of y points */
                          int *y, 
                          /* number of x points */
                          int nx, 
                          /* number of y points */
                          int ny, 
													/* matrix of longitudes */
                          double *lon, 
													/* matrix of latitudes */
                          double *lat)
#else
int inv_polar_stereo_proj(xp, yp, an, fi, x, y, nx, ny, lon, lat)
     double xp;
     double yp; 
     double an; 
     double fi; 
     int *x; 
     int *y; 
     int nx;
     int ny;
     double *lon;
     double *lat;
#endif
{

	double dx, dy, ds, glat, glon;
	int i, j;

	for(i=0; i<nx; i++){
		dx = x[i]-xp;
		for(j=0; j<ny; j++){
			dy = yp - y[j];
			ds = sqrt(dx*dx + dy*dy);
			glat = 90.0 - 2.0*to_deg(atan(ds/an));
			glon = 0.0;
			if(ds > 1.0E-10)
				glon = fi + to_deg(atan2(dx,dy));
			/* Set longitude from 0 to 360 */
			glon = to360(glon);
			/* Keep values */
			*(lat + i*ny + j) = glat;
			*(lon + i*ny + j) = glon;
		}
	}

	return(0);

}

/* ------------------------------------------------------------------
	 Set a position to its minimal value. The value of code determines whether
   it is a latitude or longtude, as well as the longitude format. Available
   macros are LONGITUDE_180_CODE, LONGITUDE_360_CODE and LATITUDE_CODE.

   RETURNS: 0
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int dmsh_to_min(/* Position structure */
                DMSH_POSITION_TYPE *p, 
                /* Code for determining the minimal value */
                int code)
#else
int dmsh_to_min(p, code)
     DMSH_POSITION_TYPE *p;
     int code;
#endif
{

	LONG hun;

	hun = (code == LONGITUDE_180_CODE) ? (-180*360000) : BADLONG;
	hun = (code == LONGITUDE_360_CODE) ?           (0) : BADLONG;
	hun = (code == LATITUDE_CODE)      ?  (-90*360000) : BADLONG; 
	if(hun == BADLONG) return(-1);
	
	HUNPOS(p, &hun);
	
	return(0);

}

/* ------------------------------------------------------------------
	 Set a position to its maximal value. The value of code determines whether
   it is a latitude or longtude, as well as the longitude format. Available
   macros are LONGITUDE_180_CODE, LONGITUDE_360_CODE and LATITUDE_CODE.

   RETURNS: 0
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int dmsh_to_max(/* Position structure */
                DMSH_POSITION_TYPE *p, 
                /* Code for determining the minimal value */
                int code)
#else
int dmsh_to_max(p, code)
     DMSH_POSITION_TYPE *p;
     int code;
#endif
{

	LONG hun;

	hun = (code == LONGITUDE_180_CODE) ? (180*360000) : BADLONG;
	hun = (code == LONGITUDE_360_CODE) ? (360*360000) : BADLONG;
	hun = (code == LATITUDE_CODE) ?  (90*360000) : BADLONG; 
	if(hun == BADLONG) return(-1);
	
	HUNPOS(p, &hun);
	
	return(0);

}

/* ------------------------------------------------------------------
	 Copies the content of DMSH position from one to another position
   structure. 

   RETURNS: 0
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int copy_dmsh_position(/* Destination */
                       DMSH_POSITION_TYPE *dst, 
                       /* Source */
                       DMSH_POSITION_TYPE *src)
#else
int copy_dmsh_position(dst, src)
     DMSH_POSITION_TYPE *dst;
     DMSH_POSITION_TYPE *src;
#endif
{

	dst->degree    = src->degree;
	dst->minute    = src->minute;
	dst->second    = src->second;
	dst->hundredth = src->hundredth;

	return(0);

}

/* ------------------------------------------------------------------
	 Tests if a position is within a range. The valuse of code determines if
   positions are latitudes or longitudes, as well as the longitude format to
   use. Avaialable macros are LONGITUDE_180_CODE, LONGITUDE_360_CODE,
   LATITUDE_CODE.

   RETURNS: true if the position is within the given range.
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int position_inside_range(/* Start position structure */
                          DMSH_POSITION_TYPE *p1, 
                          /* Stop position structure */
                          DMSH_POSITION_TYPE *p2,
													/* Test position structure */
                          DMSH_POSITION_TYPE *p, 
                          /* Code determining comparisons */
                          int code)
#else
int position_inside_range()
     DMSH_POSITION_TYPE *p1; 
     DMSH_POSITION_TYPE *p2;
     DMSH_POSITION_TYPE *p;
     int code;
#endif
{

	LONG h1, h2, h, dh1, dh2;

	h1 = POSHUN(p1);
	h2 = POSHUN(p2);
	h  = POSHUN(p);
	
	if(code < LONGITUDE_180_CODE){
		if(h1 > 18000) h1 -= 360000;
		if(h2 > 18000) h2 -= 360000;
		if(h  > 18000) h  -= 360000;
	}
	if(code == LATITUDE_CODE){
		if(h1 < 0) h1 += 360000;
		if(h2 < 0) h2 += 360000;
		if(h  < 0) h  += 360000;
	}		
	
	dh1 = h - h1;
	dh2 = h2 - h;

	return((dh1 > 0) && (dh2 > 0));

}

/* ------------------------------------------------------------------
	 Write a DMSH position into a string. The value of code controls printing
	 of longitudes and latitudes. Available macros for this parameter are
   LONGITUDE_180_CODE, LONGITUDE_360_CODE and LATITUDE_CODE. The value of
   <fmt> controls the output format. Available macros for this parameter are
   D_FORMAT_CODE, SD_FORMAT_CODE, DM_FORMAT_CODE and DMS_FORMAT_CODE.

	 The functions reading and writting positions should use the same format, so
	 that a value written by the one can be read by the other. The following
	 formats have been made available:

	       dd mm' dd.hh" S, 
				 dd mm.hhhh' S,
				 dd.hhhhhh S and
				Sdd.hhhhhh

   with S in {N,S,Y,+,-} for latitudes, where S='Y' stands for bad, and
				
	       ddd mm' dd.hh" S,
				 ddd mm.hhhh' S,
				 ddd.hhhhhh S and
				Sddd.hhhhhh
				
   with S in {E,W,X,+,-} for longitudes, where S='X' stands for bad. In
   addition, digits for bad positions are all '9'.

	 RETURNS: -1 on error, 0 if success and +1 if the given position is bad. 
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int dmsh_to_str(/* Pointer to string for formated position */
                char *str, 
                /* Pointer to position structure */
                DMSH_POSITION_TYPE *pos, 
                /* Code determining position format */
                int code, 
                /* Format for converting position */
                int fmt)
#else
int dmsh_to_str(str, pos, code, fmt)
     char *str;
     DMSH_POSITION_TYPE *pos;
     int code;
     int fmt;
#endif
{

	DMSH_POSITION_TYPE p;
	LONG hun;
	DOUBLE d;
	char hemi;
	int  error, lat;
	
	/* Initializations */
	hemi = (code == LATITUDE_CODE) ? 'Y' : 'X';
	lat  = (code == LATITUDE_CODE) ?  1  :  0 ;
	copy_dmsh_position(&p, pos);
	hun = POSHUN(&p);

	/* Make a first check of position and fill it bad */
	error = hun_to_dmsh(&p, hun, hemi, code);
	if(error < 0) return(error);
	
	/* Check for correct hemisphere character */
	if(!error){
		hun  = POSHUN(&p);
		if(lat)
			hemi = (hun < 0) ? 'S' : 'N';
		else
		hemi = (hun < 0) ? 'W' : 'E';
	}
		
	/* Create formatted string */
  strcpy(str, "");
	switch(fmt)
		{
		case SD_FORMAT_CODE:
			d = ((DOUBLE) hun)/360000.0;
			switch(hemi)
				{
				case 'X':
					strcat(str, " 999.999999");
					break;
				case 'Y':
					strcat(str, " 99.999999");
					break;
				case 'N': case 'S':
					sprintf(str, "%+09.6f",  d);
					break;
				case 'W': case 'E':
					sprintf(str, "%+010.6f", d);
					break;
				default:
					error = -1;
					break;
				}
			break;
		case D_FORMAT_CODE:
			d = abs_val(((DOUBLE) hun)/360000.0);
			switch(hemi)
				{
				case 'X':
					strcat(str, "999.999999 X");
					break;
				case 'Y':
					strcat(str, "99.999999 Y");
					break;
				case 'N': case 'S':
					sprintf(str, "%09.6f %c",  d, hemi);
					break;
				case 'W': case 'E':
					sprintf(str, "%010.6f %c", d, hemi);
					break;
				default:
					error = -1;
					break;
				}
			break;
		case DM_FORMAT_CODE:
			d = abs_val(((DOUBLE) (6000*pos->minute + 
														 100*pos->second + 
														 pos->hundredth)) / 6000.0);
			switch(hemi)
				{
				case 'X':
					strcat(str, "999 99.9999' X");
					break;
				case 'Y':
					strcat(str, "99 99.9999' Y");
					break;
				case 'N': case 'S':
					sprintf(str, "%02hd %07.4f' %c", abs_val(pos->degree), d, hemi); 
					break;
				case 'W': case 'E':
					sprintf(str, "%03hd %07.4f' %c", abs_val(pos->degree), d, hemi); 
					break;
				default:
					error = -1;
					break;
				}
			break;
		case DMS_FORMAT_CODE:
			d = abs_val(((DOUBLE) (100*pos->second + 
														 pos->hundredth)) / 100.0);
			switch(hemi)
				{
				case 'X':
					strcat(str, "999 99' 99.99\" X");
					break;
				case 'Y':
					strcat(str, "99 99' 99.99\" Y");
					break;
				case 'N': case 'S':
					sprintf(str, "%02hd %02hd' %05.2f\" %c", 
									abs_val(pos->degree), abs_val(pos->minute), d, hemi); 
					break;
				case 'W': case 'E':
					sprintf(str, "%03hd %02hd' %05.2f\" %c", 
									abs_val(pos->degree), abs_val(pos->minute), d, hemi); 
					break;
				default:
					error = -1;
					break;
				}
			break;
		default:
			error = -1;
			break;
		}
	if(error) return(error);

	if((hemi == 'X') || (hemi == 'Y')) return(1);
	
	return(0);

}

/* ------------------------------------------------------------------
	 Reads a DMSH position from a string, according to the formats described
	 in function dmsh_to_str(). The value of code defines if one expects to read
   a latitude or longitude, as well as the position range on output (see also
   function dmsh_to_str()).

	 RETURNS: -1 on error, 0 if success and +1 if the position is bad. 
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int str_to_dmsh(/* Pointer to position structure */
                DMSH_POSITION_TYPE *pos, 
                /* Pointer to string containing position */
                char *str, 
                /* Code determining position type */
                int code)
#else
int str_to_dmsh(pos, str, code)
     DMSH_POSITION_TYPE *pos;
     char *str;
     int code;
#endif
{

	DMSH_POSITION_TYPE p;
	LONG hun;
	char hemi;
	DOUBLE d;
  int ierr = 0;

	/* Initializations */
	hemi = (code == LATITUDE_CODE) ? 'Y' : 'X';

	/* Try the different formats */
	if(sscanf(str, "%hd %hd' %lf\" %c", 
						&(p.degree), &(p.minute), &d, &hemi) == 4){
    hun = p.degree*360000 + p.minute*6000 + (LONG) (d*100.0);
    if(hun < BADLONG)
      goto got_it;
  }
  
  if(sscanf(str, "%hd %lf' %c", &(p.degree), &d, &hemi) == 3){
    hun = p.degree*360000 + (LONG) (d*6000.0);
    if(hun  < BADLONG)
      goto got_it;
	}

  if(sscanf(str, "%lf %c", &d, &hemi) == 2){
    hun = (LONG) (d*360000.0);
    if(hun  < BADLONG)
      goto got_it;
	}
  
  if(sscanf(str, "%lf", &d) == 1){
    hun = (LONG) (d*360000.0);
    if(code == LATITUDE_CODE)
      hemi = (hun < 0) ? 'S' : 'N';
    else
      hemi = (hun < 0) ? 'W' : 'E';
    if(hun  < BADLONG)
      goto got_it;
	}

  return(-1);
 
 got_it:

  ierr = hun_to_dmsh(pos, hun, hemi, code);
  error_found(ierr < 0, "hun_to_dmsh()\n");
  
  return(ierr);

}

/* ------------------------------------------------------------------
	 Read a DMSH position from a file, according to the described in function
   dmsh_to_str(). The value of code defines if one expects a latitude or a
   longitude.

	 RETURNS: -1 on error or EOF, 0 if success, +1 if the position is bad. 
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int get_dmsh_position(
                      /* File descriptor */
                      FILE *fp, 
                      /* Pointer to position structure */
                      DMSH_POSITION_TYPE *pos, 
                      /* Code to determine type of position */
                      int code)
#else
int get_dmsh_position(fp, pos, code)
     FILE *fp;
     DMSH_POSITION_TYPE *pos;
     int code;
#endif
{

	DMSH_POSITION_TYPE p;
	LONG hun, ofs;
	DOUBLE d;
	char hemi;
  int ierr = 0;

	/* Initrializations */
	hemi = (code == LATITUDE_CODE) ? 'Y' : 'X';
	ofs = ftell(fp);
	hun = BADLONG;

	/* Try the different formats */
	if(fscanf(fp, "%hd %hd' %lf\" %c", 
						&(p.degree), &(p.minute), &d, &hemi) == 4){
    hun = p.degree*360000 + p.minute*6000 + (LONG) (d*100.0);
    if(hun < BADLONG)
      goto got_it;
  }

  if(error_found(fseek(fp, ofs, SEEK_SET), "fseek()\n"))
    return(-1);
  
  if(fscanf(fp, "%hd %lf' %c", &(p.degree), &d, &hemi) == 3){
    hun = p.degree*360000 + (LONG) (d*6000.0);
    if(hun  < BADLONG)
      goto got_it;
	}

  if(error_found(fseek(fp, ofs, SEEK_SET), "fseek()\n"))
    return(-1);

  if(fscanf(fp, "%lf %c", &d, &hemi) == 2){
    hun = (LONG) (d*360000.0);
    if(hun  < BADLONG)
      goto got_it;
	}

  if(error_found(fseek(fp, ofs, SEEK_SET), "fseek()\n"))
    return(-1);

  if(fscanf(fp, "%lf", &d) == 1){
    hun = (LONG) (d*360000.0);
    if(code == LATITUDE_CODE)
      hemi = (hun < 0) ? 'S' : 'N';
    else
      hemi = (hun < 0) ? 'W' : 'E';
    if(hun  < BADLONG)
      goto got_it;
	}

  return(-1);

 got_it:

  ierr = hun_to_dmsh(pos, hun, hemi, code);
  error_found(ierr < 0, "hun_to_dmsh()\n");
    
	return(ierr);

}

/* ------------------------------------------------------------------
	 Given a position in hundredth of seconds, an hemisphere character and a
	 code for expected longitude or latitude, see description in function
   dmsh_to_str(), this function fills the corresponding DMSH structure, or set
   it to bad.

	 RETURNS: -1 on error, 0 if success and +1 if the position is bad.
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int hun_to_dmsh(/* Pointer to position structure */
                DMSH_POSITION_TYPE *pos, 
                /* Hundredths of second */
                LONG hun, 
                /* Hemisphere */
                char hemi, 
                /* Code for expected position type */
                int code)
#else
int hun_to_dmsh(pos, hun, hemi, code)
     DMSH_POSITION_TYPE *pos;
     LONG hun;
     char hemi; 
     int code;
#endif
{

	int lat;

	/* Initializations */
	lat = (code == LATITUDE_CODE) ? 1 : 0;

	/* Check hemisphere character */
	if(lat && (hemi != 'N') && (hemi != 'S') && (hemi != 'Y'))
		return(-1);
	if(!lat && (hemi != 'W') && (hemi != 'E') && (hemi != 'X'))
		return(-1);

  /* Set the correct sign. NOTE: on input, hun can already have the correct
     sign.
  */
  if(lat)
    hun = (hemi == 'S') ? -abs_val(hun) : abs_val(hun);
  else
    hun = (hemi == 'W') ? -abs_val(hun) : abs_val(hun);
    
	/* Check for bad data */
	HUNPOS(pos, &hun);
	if(invalid_position(pos, lat)){
		dmsh_to_bad(pos, lat);
		return(1);
	}
	
	/* Modify longitude */
	if(code == LONGITUDE_360_CODE)
		hun = (LONG) ((to360(((DOUBLE) hun)/360000.0)) * 360000.0);
	if(code == LONGITUDE_180_CODE)
		hun = (LONG) ((to180(((DOUBLE) hun)/360000.0)) * 360000.0);

	/* Final conversion */
	HUNPOS(pos, &hun);
  
	return(0);

}

/* ------------------------------------------------------------------
	 Given an array of four DMSH position structures, this function writes a 
	 string with DMSH position range as

	    lat1 lon1  to  lat2 lon2

			  1    0         3    2     <-- Array indexes

   where the position formats are the same as those described in function
   dmsh_to_str(). Longitude values range from -180 to +180. The value of fmt
   controls the output format (see dmsh_to_str()). The array indexes described
   above allow to pass a 2-elements DMSH_LL array also.

	 RETURNS: -1 on error or the nhumber of bad positions on input.
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int position_range_to_str(/* Pointer to string */
                          char *str, 
                          /* Pointer to array of positioons */
                          DMSH_POSITION_TYPE *pos, 
                          /* Code for formatting positions */
                          int fmt)
#else
int position_range_to_str(str, pos, fmt)
     char *str;
     DMSH_POSITION_TYPE *pos;
     int fmt;
#endif
{

	char buff[25];
	int  error, n=0;

	/* Initializations */
	strcpy(str, "");

	error = dmsh_to_str(buff, pos+1, +1, fmt); 
	if(error < 0) return(error);
	n += (error > 0) ? 1 : 0;
	strcat(str, buff);
	strcat(str, " ");

	error = dmsh_to_str(buff, pos+0, -1, fmt); 
	if(error < 0) return(error);
	n += (error > 0) ? 1 : 0;
	strcat(str, buff);

	strcat(str, "  to  ");

	error = dmsh_to_str(buff, pos+3, +1, fmt); 
	if(error < 0) return(error);
	n += (error > 0) ? 1 : 0;
	strcat(str, buff);
	strcat(str, " ");

	error = dmsh_to_str(buff, pos+2, -1, fmt); 
	if(error < 0) return(error);
	n += (error > 0) ? 1 : 0;
	strcat(str, buff);

	return(n);

}

/* ------------------------------------------------------------------
	 Write a DMSH position into a file. See function dmsh_to_str() for the
	 different parameters.

	 RETURNS: -1 on error, 0 if success and +1 the position is bad on input.
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int write_dmsh_position(/* File descriptor */
                        FILE * fp, 
                        /* pointer to positon structure */
                        DMSH_POSITION_TYPE *pos, 
                        /* Code for expected position type */
                        int code, 
                        /* Format for printing the position */
                        int fmt)
#else
int write_dmsh_position(fp, pos, code, fmt)
     FILE * fp;
     DMSH_POSITION_TYPE *pos;
     int code; 
     int fmt;
#endif
{

	char buff[25];
	int  error;

	error = dmsh_to_str(buff, pos, code, fmt);
	if(error < 0) return(error);
	
	fprintf(fp, "%s", buff);

	return(error);

}

/* ------------------------------------------------------------------
	 Get a DMSH position range from a file. Data must be written in the same
	 order as those writen by function position_range_to_str(), and follow one
	 of the format described above. Output format of longitudes is from 0 to
	 360. 

	 RETURNS: -1 on error or number of bad positions read.
	 ------------------------------------------------------------------ */
#if PROTOTYPE_ALLOWED
int get_position_range(/* File descriptor */
                       FILE *fp, 
                       /* Pointer to start position */
                       DMSH_POSITION_TYPE *start, 
                       /* Pointer to stop position */
											 DMSH_POSITION_TYPE *end)
#else
int get_position_range()
#endif
{

	int error, n=0;

	error = get_dmsh_position(fp, start+1, 1);
	if(error<0) return(error);
	n += (invalid_position(start+1, 1)) ? 1 : 0;

	error = get_dmsh_position(fp, start+0, 0);
	if(error<0) return(error);
	n += (invalid_position(start+0, 0)) ? 1 : 0;

	if(fscanf(fp, " to ") != 0) return(-1);

	error = get_dmsh_position(fp, end+1, 1);
	if(error<0) return(error);
	n += (invalid_position(end+1, 1)) ? 1 : 0;

	error = get_dmsh_position(fp, end+0, 0);
	if(error<0) return(error);
	n += (invalid_position(end+0, 0)) ? 1 : 0;

	return(n);

}

/* ------------------------------------------------------------------
	 This function is similar to function dmsh_to_str(), except that the input
   position is given in decimal degree.

   RETURN: -1 on error, 0 if success or +1 if input position is bad
	 ------------------------------------------------------------------ */

#if PROTOTYPE_ALLOWED
int degree_to_str(/* Pointer to string for formatted position */
                  char *str, 
                  /* Position */
                  double deg, 
                  /* Code for expected position type */
                  int code, 
                  /* Format for printing the position */
                  int fmt)
#else
int degree_to_str(str, deg, code, fmt)
     char *str;
     double deg;
     int code;
     int fmt;
#endif
{

	DMSH_POSITION_TYPE pos;
	LONG hun;
	int lat, nbad;
	char hemi;

	/* Initializations */
	lat = (code == LATITUDE_CODE) ? 1 : 0;
	hemi = (lat) ? 'Y' : 'X';

	/* Convert data in DMSH structure */
	hun = (LONG) (deg*360000.0);
	nbad = hun_to_dmsh(&pos, hun, hemi, code);
	if(error_found(nbad < 0, "hun_to_dmsh()\n")) return(nbad);
	
	/* Convert position to string */
	dmsh_to_str(str, &pos, code, fmt);
	
	return(nbad);

}
