

function GPSCoordinateSet(coordStr)
{
   if (altitude == null)
	altitude = 0;

   var lat = "";
   var lon = "";
   var sortKey = 0;
   var geolog;
   var altitude;   
   var rectX;
   var rectY;
   var rectZ;
   var hemi;
   var zone;
   var UTMx;
   var UTMy;

   // parse out each part
   this.altitude = altitude;

   var coordStr =  coordStr.replace("\r\n",  ' ');
   coordStr =  coordStr.replace("\n",  ' ');
   coordStr =  coordStr.replace("\r",  ' ');
   coordStr =  coordStr.replace("&deg;",  '');

//	alert("\"coordStr\"");

   var matches = new Array();


// GPS coordinates
   matches = coordStr.match(/([NWSE\-]?)([^0-9NWSE]{0,3}[0-9]{1,3}[^0-9]{1,4}[0-9]{1,2}[^0-9]{1,3}[0-9]{1,3})[^a-z0-9\-]*([NWSE\-]?)([^0-9NWSE]{0,3}[0-9]{1,3}[^0-9]{1,3}[0-9]{1,2}[^0-9]{1,3}[0-9]{1,3})[^NWSE]*([NWSE]?)/im);

   if (!matches)
   {
//    alert("did not match GPS regex<BR>\n");

      // decimal coordinates
      matches = coordStr.match(/([NWSE\-]?[^0-9]*[0-9]{1,3}\.[0-9]{2,}[^a-z0-9\-]+)([NWSE\-]?[^0-9]*[0-9]{1,3}\.[0-9]{2,})/i);

      lat = matches[1];
      lon = matches[2];

//      alert(lat + " " + lon);
	
   }
   else
   {
      latDir1 = matches[1];
      latDir2 = matches[3];
      lonDir1 = matches[3];
      lonDir2 = matches[5];
      lat = matches[2];
      lon = matches[4];	

//		var_dump(matches);


	  // we either use "-", "" or NWSE, can't use both
 	  if (latDir2.match("/\-/") && latdir1.match("/[NWSE]/i"))
	{
			latDir1 = "";
		}

		// we want 1 and 1 or 2 and 2 or blank and 1 or 1 and blank
		if (latDir1 != "" && lonDir1 != "")
		{
		  lat = latDir1 + lat;
		  lon = lonDir1 + lon;
		}
		else if (latDir2 != "" && lonDir2 != "")
		{
		  lat = latDir2 + lat;
		  lon = lonDir2 + lon;
		}
		else if (latDir1 == "" && lonDir1 != "")
		{
		  lat = "N" + lat;
		  lon = lonDir1 + lon;
		}
		else if (latDir1 != "" && lonDir1 == "")
		{
		  lat = latDir1 + lat;
		  lon = "E" + lon;
		}
		else
		{
		  lat = "N" + lat;
		  lon = "E" + lon;
		}

	}

       this.lat = new GPSCoordinate(lat, "lat");
       this.lon = new GPSCoordinate(lon, "lon");

//       alert("coords " + this.lat.toDec() + " " + this.lon.toDec() + "<BR>\n");
   
       var UTMArr = dec2utm(this.lat.toDec(), this.lon.toDec());

//       alert(UTMArr);

       this.hemi  = UTMArr[0];
       this.zone  = UTMArr[1];
       this.UTMx  = UTMArr[2];
       this.UTMy  = UTMArr[3];

       this.calcRect();
   }

function deg2rad(deg)
{
    return (deg / 180.0 * Math.PI)
}

GPSCoordinateSet.prototype.calcRect = GPSCoordinateSet_calcRect;
function GPSCoordinateSet_calcRect()
{
//   alert("Lat: " + (this.lat.toDecString()));
       latdec = deg2rad(this.lat.toDecString());
       londec = deg2rad(this.lon.toDecString());
//   alert("dec: latdec londec\n");
    
   er = 6378137.0;
   f = 1.0/298.257223560;
   ee =  2.0 * f - f * f;
   h = this.altitude;
//   alert("Alt: h\n");

   b = er / Math.sqrt(1 - ee * Math.sin(latdec) * Math.sin(latdec));
   d = (b + h) * Math.cos(latdec);
   this.rectX = d * Math.cos(londec);
   this.rectY = d * Math.sin(londec);
   this.rectZ = (b *(1 - ee) + h) * Math.sin(latdec);
}

// -------------------------------------------------------------------------
// METHOD:  CLatLon::VincentyDistance()
/*! 
   \brief  Calculates the distance and forward and reverse azimuths between 
           this point and P using the Vincenty method.

   \author fizzymagic
   \date   9/6/2003

   \return  [double] - Distance between this point and P in meters.

   \param P [CLatLon&] - Point to which to compute distance.
   \param pForwardAzimuth [double *] - Pointer to parameter to receive 
                                       forward azimuth in degrees.
   \param pReverseAzimuth [double *] - Pointer to parameter to receive
                                       reverse azimuth in degrees.


   This method computes a high-accuracy distance between this point and P
   using the Vincenty method and the WGS84 ellipsoid.  It also calculates 
   and returns the forward and reverse azimuths.
*/
// -------------------------------------------------------------------------

GPSCoordinateSet.prototype.vincentyDistance = GPSCoordinateSet_vincentyDistance;
function GPSCoordinateSet_vincentyDistance(cset, units)
{
  if (units == null) units = "m";

  return this.distance(cset, units);
}

GPSCoordinateSet.prototype.distance = GPSCoordinateSet_distance;
function GPSCoordinateSet_distance(cset, units)
{
   if (units == null) units = "m";

   result['forward'] = 0;
   result['reverse'] = 0;
   result['distance'] = 0;

   if (cset.toGPSString() == this.toGPSString())
   {
	return result;
   }

// Degrees to radians conversion.
   Deg2Rad = 1.74532925199433E-02;

   EPSILON = 5.e-14;

   m_ellipsoidA = 6378137.00;
   m_ellipsoidInv = 298.257223563;

   // Check to see if either latitude is 90 degrees exactly.
   // In that case, the distance is a closed-form expression!
   
   thislat = this.getLat();
   thislon = this.getLong();

   plat = cset.getLat();
   plon = cset.getLong();
   dLat1  = Deg2Rad * thislat.toDec();
   dLat2  = Deg2Rad * plat.toDec();
   dLong1 = Deg2Rad * thislon.toDec();
   dLong2 = Deg2Rad * plon.toDec();

/*
   var_dump(thislat.toDec());
   var_dump(thislon.toDec());
   var_dump(plat.toDec());
   var_dump(plon.toDec());
*/

   a0 = m_ellipsoidA;
   flat = 1.0 / m_ellipsoidInv;
   r = 1.0 - flat;
   b0 = a0 * r;

   tanu1 = r * tan(dLat1);
   tanu2 = r * tan(dLat2);

   dtmp = Math.atan(tanu1);

   if (Math.abs(thislat.toDec()) >= 90.0)
   {
	dtmp = dLat1;
   }

   cosu1 = Math.cos(dtmp);
   sinu1 = Math.sin(dtmp);

   dtmp = Math.atan(tanu2);
   if (Math.abs(plat.toDec()) >= 90.0) 
   {
      dtmp = dLat2;
   }

   cosu2 = Math.cos(dtmp);
   sinu2 = Math.sin(dtmp);

   omega = dLong2 - dLong1;
   lambda = omega;

   do {
      testlambda = lambda;
      ss1 = cosu2 * Math.sin(lambda);
      ss2 = cosu1 * sinu2 - sinu1 * cosu2 * Math.cos(lambda);
      ss = Math.sqrt(ss1 * ss1 + ss2 * ss2);
      cs = sinu1 * sinu2 + cosu1 * cosu2 * Math.cos(lambda);
      tansigma = ss / cs;
      sinalpha = cosu1 * cosu2 * Math.sin(lambda) / ss;
      dtmp = Math.asin(sinalpha);
      cosalpha = Math.cos(dtmp);
      cosalpha2 = cosalpha * cosalpha; 
      c2sm = cs - 2.0*sinu1*sinu2/cosalpha2;
      c = flat/16.0 * cosalpha2*(4.0 + flat*(4.0 - 3.0*cosalpha2));
      lambda = omega + (1.0 - c)*flat*sinalpha*(Math.asin(ss) + c*ss*(c2sm + c*cs*(-1.0 + 2.0*c2sm*c2sm)));
      dDeltaLambda = Math.abs(testlambda - lambda);
   } while (dDeltaLambda > EPSILON);

   u2 = cosalpha2 * (a0*a0 - b0*b0)/(b0*b0);
   a = 1.0 + (u2 / 16384.0) * (4096.0 + u2 * (-768.0 + u2 * (320.0 - 175.0 * u2)));
   b = (u2 / 1024.0) * (256.0 + u2 * (-128.0 + u2 * (74.0 - 47.0 * u2)));

   dsigma = b * ss * (c2sm + (b / 4.0) * (cs * (-1.0 + 2.0 * c2sm*c2sm) 
                 - (b / 6.0) * c2sm * (-3.0 + 4.0 * ss*ss) * (-3.0 + 4.0 * c2sm*c2sm)));

   s = b0 * a * (Math.asin(ss) - dsigma);

   alpha12 = Math.atan2(cosu2 * Math.sin(lambda), (cosu1 * sinu2 - sinu1 * cosu2 * Math.cos(lambda)))/Deg2Rad;
   alpha21 = Math.atan2(cosu1 * Math.sin(lambda), (-sinu1 * cosu2 + cosu1 * sinu2 * Math.cos(lambda)))/Deg2Rad;

   result['distance'] = this.convertUnits(s, units);

   result['forward'] = (alpha12 + 360.0) % 360.0;
   result['reverse'] = (alpha21 + 180.0) % 360.0;

   return result;
}

GPSCoordinateSet.prototype.convertUnits = GPSCoordinateSet_convertUnits;
function GPSCoordinateSet_convertUnits(s, units)
{

   units = units.toUpperCase();

   if (units == "K")
	return s / 1000.00;
   else if (units == "NM")
	return s / 1852.0; 
   else 
	return s / 1609.344; 
}

GPSCoordinateSet.prototype.distance3D = GPSCoordinateSet_distance3D;
   function GPSCoordinateSet_distance3D(cset, units)
   {
      if (units == null) units = "m";

	xd = cset.getRectX() - this.getRectX();
	yd = cset.getRectY() - this.getRectY();
	zd = cset.getRectZ() - this.getRectZ();

	return this.convertUnits(Math.sqrt(xd*xd + yd*yd + zd*zd), units);
   }

GPSCoordinateSet.prototype.toRectString = GPSCoordinateSet_toRectString;
   function GPSCoordinateSet_toRectString()
   {
      return this.rectX + ", " + this.rectY + ", " + this.rectZ;
   }

GPSCoordinateSet.prototype.getRectX = GPSCoordinateSet_getRectX;
   function GPSCoordinateSet_getRectX()
   {
      return this.rectX;
   }
GPSCoordinateSet.prototype.getRectY = GPSCoordinateSet_getRectY;
   function GPSCoordinateSet_getRectY()
   {
      return this.rectY;
   }
GPSCoordinateSet.prototype.getRectZ = GPSCoordinateSet_getRectZ;
   function GPSCoordinateSet_getRectZ()
   {
      return this.rectZ;
   }

GPSCoordinateSet.prototype.gcDistance = GPSCoordinateSet_gcDistance;
   function GPSCoordinateSet_gcDistance(cset, units)
   {
//	alert("calculating distance from " + gettype(cset) + " to this<BR>");

	lt1 = cset.getLat();
	ln1 = cset.getLong();
	
	lon1 = ln1.toDecString();
	lat1 = lt1.toDecString();

	lt2 = this.lat;
	ln2 = this.lon;

	lon2 = ln2.toDecString();
	lat2 = lt2.toDecString();

//	alert("calculating distance from lat1, lon1 to lat2, lon2<BR>");

	theta = lon1 - lon2; 
 	dist = Math.sin(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) +Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(deg2rad(theta)); 
 	dist = Math.acos(dist); 
 	dist = rad2deg(dist); 
 	miles = dist * 60 * 1.1515; 
	unit = unit.toUpperCase(); 

        if (unit == "K") { 
        	return (miles * 1.609344); 
 	} else if (unit == "N") { 
        	return (miles * 0.8684); 
	} else { 
        	return miles; 
       	} 
   }

GPSCoordinateSet.prototype.compareBySortKey = GPSCoordinateSet_compareBySortKey;
   function GPSCoordinateSet_compareBySortKey(a, b)
   { 
//	alert("comparing " + a.getSortKey() + " " + b.getSortKey() + "<BR>");

        if (a.getSortKey() == b.getSortKey()) {
          return 0;
        }

        return (a.getSortKey() < b.getSortKey()) ? -1 : 1;
   }

GPSCoordinateSet.prototype.getLat = GPSCoordinateSet_getLat;
   function GPSCoordinateSet_getLat()
   {
	return this.lat;
   }

GPSCoordinateSet.prototype.getZone = GPSCoordinateSet_getZone;
   function GPSCoordinateSet_getZone()
   {
	return this.zone;
   }

GPSCoordinateSet.prototype.getUTMx = GPSCoordinateSet_getUTMx;
   function GPSCoordinateSet_getUTMx()
   {
	return this.UTMx;
   }

GPSCoordinateSet.prototype.getUTMy = GPSCoordinateSet_getUTMy;
   function GPSCoordinateSet_getUTMy()
   {
	return this.UTMy;
   }

GPSCoordinateSet.prototype.getHemi = GPSCoordinateSet_getHemi;
   function GPSCoordinateSet_getHemi()
   {
	return this.hemi;
   }

GPSCoordinateSet.prototype.getSortKey = GPSCoordinateSet_getSortKey;
   function GPSCoordinateSet_getSortKey()
   {
	return this.sortKey;
   }

GPSCoordinateSet.prototype.setSortKey = GPSCoordinateSet_setSortKey;
   function GPSCoordinateSet_setSortKey(key)
   {
	return this.sortKey = key;
   }

GPSCoordinateSet.prototype.addGPS = GPSCoordinateSet_addGPS;
   function GPSCoordinateSet_addGPS(latDeg, latMin, lonDeg, lonMin)
   {
	this.lat.addGPS(latDeg, latMin);
	this.lon.addGPS(lonDeg, lonMin);
        this.calcRect();
   }

GPSCoordinateSet.prototype.getLong = GPSCoordinateSet_getLong;
   function GPSCoordinateSet_getLong()
   {
	return this.lon;
   }

GPSCoordinateSet.prototype.isValid = GPSCoordinateSet_isValid;
   function GPSCoordinateSet_isValid()
   {
	return ((this.lat.toDec() + this.lon.toDec()) != 0);
   }

  GPSCoordinateSet.prototype.toGPSString = GPSCoordinateSet_toGPSString;

   function GPSCoordinateSet_toGPSString()
   {
	return this.lat.toGPSString() + "  " + this.lon.toGPSString();
   }

GPSCoordinateSet.prototype.toDecString = GPSCoordinateSet_toDecString;
   function GPSCoordinateSet_toDecString()
   {
	return this.lat.toDecString() + "  " + this.lon.toDecString();

   }


   GPSCoordinateSet.prototype.toUTMString = GPSCoordinateSet_toUTMString;
   function GPSCoordinateSet_toUTMString()
   {
	return this.hemi + this.zone + " " + Math.round(this.UTMx) + " " + Math.round(this.UTMy);
   }


