function degToRad(degs) {
  return (Math.PI * (degs/180));
}

function radToDeg(rads) {
  return (180 * (rads/Math.PI));
}

function Point3D() {
  var x, y, z;
  
  this.setFromLatLngAlt = 
    function(lat, lng, alt) {
    
// convert to radians    
      var latRads = degToRad(lat);
      var lngRads = degToRad(lng);
      
      var xy = new Array(2);
      
// convert to UTM      
      LatLonToUTMXY(latRads, lngRads, UtmZone, xy);
      
// XY contains UTM E at xy[0]
      var utmE = xy[0];
      
// utmE is our -Y
      this.y = UtmEToY(utmE);      
      
// XY contains UTM N at xy[1]
      var utmN = xy[1];
      
// utmN is our +X
      this.x = UtmNToX(utmN);       
      
// z is altitude
      this.z = alt;
    }
  
  this.distanceTo = 
    function(point) {
      var dx = this.x - point.x;
      var dy = this.y - point.y;
      var dz = this.z - point.z;
    
      return (Math.sqrt(dx*dx + dy*dy + dz*dz));
    } 
    
  this.extend =
    function(heading,roll,distance) {
      this.z += distance * Math.sin(roll);
      var dp = distance * Math.cos(roll);
      this.x += dp * Math.cos(heading);
      this.y += dp * Math.sin(heading);
    }
    
  this.extendFromPoint = 
    function(point,heading,tilt,distance) {

      this.z = point.z + distance * Math.sin(tilt);
      var dp = distance * Math.cos(tilt);
      this.x = point.x + dp * Math.sin(heading);
      this.y = point.y - dp * Math.cos(heading);
    }  
    
  this.show = 
    function(txt) {
      log(txt+'=> x:' + this.x + ' y:' + this.y + ' z:' + this.z);
    }   
   
  this.setFromGLatLngAndAlt = 
    function(latLng,alt) {
      var lat = latLng.lat();
      var lng = latLng.lng();
      
      this.setFromLatLngAlt(lat,lng,alt);
    } 
    
  this.extendFromBaseToTarget =
    function(base,target,length) {
    
// find the distance from the target to the base
      var dx = target.x - base.x;
      var dy = target.y - base.y;
      var dz = target.z - base.z;
      
      var d = Math.sqrt(dx*dx + dy*dy + dz*dz);
      
// find the orientation to point at this target
      var heading = Math.atan2(dy, dx);

// find the projected distance and the tilt
      var dp = Math.sqrt(dx*dx + dy*dy);
      var tilt = Math.atan2(dz,dp);   
//log('heading = ' + radToDeg(heading) + ' tilt = ' + radToDeg(tilt));      
      
      dp = length * Math.cos(tilt);
//      this.x = base.x + dp * Math.sin(heading);
//      this.y = base.y - dp * Math.cos(heading);
      this.x = base.x + dp * Math.cos(heading);
      this.y = base.y + dp * Math.sin(heading);

      this.z = base.z + length * Math.sin(tilt);
    }   
    
  this.toGLatLng =
    function() {
    
// our x is UTM N
      var utmN = XToUtmN(this.x);
      
// our -y is UTM E              
      var utmE = YToUtmE(this.y);
      
// convert to lat lng
      var latLng = new Array(2);

      UTMXYToLatLon(utmE, utmN, UtmZone, false, latLng);
      
      var lat = radToDeg(latLng[0]);
      var lng = radToDeg(latLng[1]);
      
      var result = new GLatLng(lat,lng);
      return result;
    }  
    
  this.interpolate = 
    function(pt1, pt2, frac) {
      this.x = pt1.x + frac * (pt2.x - pt1.x);
      this.y = pt1.y + frac * (pt2.y - pt1.y);
      this.z = pt1.z + frac * (pt2.z - pt1.z);
    }  
}

function GeoPoint() {
  this.lat = 0;
  this.lng = 0;
  this.alt = 0;
  
  this.setFromXY = 
    function(x, y) {
      var utmN = XToUtmN(x);
      var utmE = YToUtmE(y);
      
// convert to lat/lng
      var latLng = new Array(2);

      UTMXYToLatLon(utmE, utmN, UtmZone, false, latLng);
      
      this.lat = radToDeg(latLng[0]);
      this.lng = radToDeg(latLng[1]);
    }
}    

//*****************************************************************************
// Converts P1,P2 into a vector
//      P1o----->P2
//*****************************************************************************
function vector(p1,p2) {
  var result = new Point3D();
 
  result.x = p2.x - p1.x;
  result.y = p2.y - p1.y;
  result.z = p2.z - p1.z;
  
  return result;
}  

//*****************************************************************************
// Returns the dot product of two vectors
//*****************************************************************************
function dotProduct(v1,v2) {
  var result = (v1.x*v2.x) + (v1.y * v2.y) + (v1.z * v2.z);
  return result;
}  

//*****************************************************************************
// Returns the magnitude of V. ( |V| )
//*****************************************************************************
function magnitude(v) {
  var result = Math.sqrt( (v.x*v.x) + (v.y*v.y) + (v.z*v.z) );
  return result;
}

//*****************************************************************************
// Returns the angle between P1,P2,P3          P1
//                                            /
//                                           /
//                                         P2 ----P3
//*****************************************************************************
function angleBetween3DPoints(p1,p2,p3) {

// find the vectors
  p1 = vector(p2,p1);
  p3 = vector(p2,p3);
  
  var result = dotProduct(p1,p3) / (magnitude(p1) * magnitude(p3));

// clip the ArcCos to the valid range - sometimes it goes beyond +1/-1 due to
// round off error
  if (result > 1) return(0)
  else if (result < -1) return (Math.PI)
  else return (Math.acos(result));
}  


