///////////////////////////////////////////////////////////////////////////////
// loadgpx.4.js
//
// Javascript object to load GPX-format GPS data into Google Maps.
// 
// Copyright (C) 2009 Heiko Will (http://trekking-photos.net)
// Based on the great work of Kaz Okuda (http://notions.okuda.ca)
//
// 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// If you use this script or have any questions please leave a comment
// at http://notions.okuda.ca/geotagging/projects-im-working-on/gpx-viewer/
// A link to the GPL license can also be found there.
//
///////////////////////////////////////////////////////////////////////////////
 function gtbExternal() { }


  var Dparser = new Array();
  var gtc = new GLargeMapControl();
  var gmc = new GMapTypeControl();
  var gsc = new GScaleControl();
		
				
  function LoadGPXFileIntoGoogleMap(filename,delta, id)
	{
	  var request = GXmlHttp.create();
	  request.open("GET", filename, true);
	  request.onreadystatechange = function()
	  {
	    if (request.readyState == 4)
	    {
	      Dparser[id] = new GPXParser(request.responseXML, id);
	      Dparser[id].SetTrackColour("red");		// Set the track line colour
	      Dparser[id].SetTrackWidth(3);			// Set the track line width
	      Dparser[id].SetMinTrackPointDelta(delta);		// Set the minimum distance between track points
	      Dparser[id].CenterAndZoom(request.responseXML, G_PHYSICAL_MAP);	// Center and Zoom the map over all the points.
	      Dparser[id].AddTrackpointsToMap();			// Add the trackpoints
	      Dparser[id].draw();
	      Dparser[id].AddWaypointsToMap();			// Add the waypoints
      }
	  }
	  request.send(null);
	}
			
	function DGPX_switch(id,url,width) {
	  var CanvasElem = document.getElementById("canvas"+id);
	  var elem = document.getElementById("gmap"+id);
	  var blink = document.getElementById("blink"+id);
	  var slink = document.getElementById("slink"+id);
	  var gcap = document.getElementById("gcap"+id);
	  var delta = 5;
	  var marker=0;
	  if (slink.style.display!="none"){
	    CanvasElem.src = "/w/extensions/DisplayGPX/gpxHeightImage.php?width="+width+"&file="+url;
	    slink.style.display = "none";
	    blink.style.display = "block"
	    elem.style.width= width+"px";
	    elem.style.height= ((width|0)*(3/4))+"px";
	    gcap.style.width= ((width|0)+0)+"px";
	   // CanvasElem.style.width = width+"px";
	    marker=0;
	  } else {
	    blink.style.display = "none";
	    slink.style.display = "block"
	    elem.style.width="870px";
	    elem.style.height="670px";
	    gcap.style.width="878px";
	    CanvasElem.src = "/extensions/DisplayGPX/gpxHeightImage.php?width=870&file="+url;
//	    CanvasElem.style.width = "870px";
	    marker=1;
	  }
	  if (!Dparser[id]) LoadGPXFileIntoGoogleMap(url,delta,id);
	    else{
        Dparser[id].draw();
	      if (marker){
    	    Dparser[id].map.addControl(gmc);
    	    Dparser[id].map.addControl(gtc);
    	    Dparser[id].map.addControl(gsc);
  	  } else {
  	    if (Dparser[id].map) {
          Dparser[id].map.removeControl(gmc);
    	    Dparser[id].map.removeControl(gtc);
    	    Dparser[id].map.removeControl(gsc);
  	    }
      }
      }
    }

var me = Array();

// fix for JS Buggy Garbage Collector
JB_addEvent(window,"onunload",function(){
  me.statistics=null;
  me.tracks=null;
  me.elevationProfile=null;
  me.Icon=null;
  me.jg=null;
  me.hline=null;
  me.eleMarker=null;
  me=null;
});

// AddEvent function from
// 2009 Jürgen Berkemeier
// www.j-berkemeier.de
function JB_addEvent(oTarget, sType, fpDest) {
  var oOldEvent = oTarget[sType];
  if (typeof oOldEvent != "function") {
    oTarget[sType] = fpDest;
  } else {
    oTarget[sType] = function(e) {
      oOldEvent(e);
      fpDest(e);
    }
  }
} // addEvent

function GPXParser(xmlDoc, id)
{
  me[id] = this;
  this.id = id;
  this.xmlDoc = xmlDoc;
  this.trackcolour = "#ff0000"; // red
  this.trackwidth = 2;
  this.mintrackpointdelta = 50;
  this.tracks = new Array();
  this.elevationProfile = new Array();
  this.statistics = new Array();
  this.DebugElem = document.getElementById("debug"+id);
  this.CanvasElem = document.getElementById("canvas"+id);
  
  //this.name =  new Array();
  
  this.Icon = new GIcon();
  this.Icon.image = "http://outdoorseiten.net/w/extensions/DisplayGPX/icon.png";
  this.Icon.iconSize = new GSize(11, 11);
  this.Icon.iconAnchor = new GPoint(6, 6);
  this.Icon.infoWindowAnchor = new GPoint(6, 6);
  this.eleMarker = new GMarker(new GLatLng(45,45),this.Icon);
}

// Set the colour of the track line segements.
GPXParser.prototype.SetTrackColour = function(colour)
{
	this.trackcolour = colour;
}

// Set the strokewidth of the track line segements
GPXParser.prototype.SetTrackWidth = function(width)
{
	this.trackwidth = width;
}

// Set the minimum distance between trackpoints.
// Used to cull unneeded trackpoints from map.
GPXParser.prototype.SetMinTrackPointDelta = function(delta)
{
	this.mintrackpointdelta = delta;
}

GPXParser.prototype.CreateMarker = function(point)
{
	var lon = parseFloat(point.getAttribute("lon"));
	var lat = parseFloat(point.getAttribute("lat"));
	var html = "<div style=\"padding-bottom:20px;\" class=\"DGPX_Waypoint\">";
	var s = "";
	var l = "";
	
  /* Get name of the Waipoint */
  if (point.getElementsByTagName("name").length > 0) {
		s = point.getElementsByTagName("name").item(0).childNodes[0].nodeValue;
		html += "<h1>"+stripHTML(s)+"</h1>";
	} else {
    html+= "<h1>Waypoint</h1>";
  }
  
  /* Get Description of Waypoint */
  if (point.getElementsByTagName("desc").length > 0) {
    s = point.getElementsByTagName("desc").item(0).childNodes[0].nodeValue;
		html += "<div class=\"DGPX_WP_Description\">"+stripHTML(s)+"</div>";
	} 
	
	/* Get Comment on Waypoint */
  if (point.getElementsByTagName("cmt").length > 0) {
    l = point.getElementsByTagName("cmt").item(0).childNodes[0].nodeValue;
		if (s!=l) 
      html += "<div class=\"DGPX_WP_Comment\">"+stripHTML(s)+"</div>";
	} 
	
  /* Get Link on Waypoint */
  	
  if (point.getElementsByTagName("link").length > 0) {
    s = point.getElementsByTagName("link").item(0).getAttribute("href");
    html += "<span class=\"DGPX_WP_Link\"><a href=\""+stripHTML(s)+"\">"+stripHTML(s)+"</a></span> ";
  } 
	  
  /* Get Elevation of Waypoint */
  if (point.getElementsByTagName("ele").length > 0) {
    s = point.getElementsByTagName("ele").item(0).childNodes[0].nodeValue;
    html += "<span class=\"DGPX_WP_Elevation\"><strong>Elevation: </strong>"+stripHTML(s)+"m</span> <br>";
  } 
  html += "<span class=\"DGPX_WP_LonLat\"><strong>Longitude: </strong>"+point.getAttribute("lon")+"° </span> <br>";
  html += "<span class=\"DGPX_WP_LonLat\"><strong>Latitude: </strong>"+point.getAttribute("lat")+"° </span> <br>";
  html+="</div>";
	  
  var marker;  
  if (point.getElementsByTagName("sym").length > 0) {
    s = point.getElementsByTagName("sym").item(0).childNodes[0].nodeValue;
     if (s=="Lodge") {
        if (!this.Logde) {
          this.Lodge = new GIcon();
          this.Lodge.image = "http://www.trekking-photos.net/wp-content/plugins/gpx-filter/House.png";
          this.Lodge.iconSize = new GSize(16, 16);
          this.Lodge.iconAnchor = new GPoint(8, 8);
          this.Lodge.infoWindowAnchor = new GPoint(0, 0);
        }
        marker = new GMarker(new GLatLng(lat,lon),this.Lodge);
     }
     else if (s=="Parking Area") {
        if (!this.Parking) {
          this.Parking = new GIcon();
          this.Parking.image = "http://www.trekking-photos.net/wp-content/plugins/gpx-filter/Parking.png";
          this.Parking.iconSize = new GSize(16, 16);
          this.Parking.iconAnchor = new GPoint(8, 8);
          this.Parking.infoWindowAnchor = new GPoint(0, 0);
        }
        marker = new GMarker(new GLatLng(lat,lon),this.Parking);
     }
     else
        marker = new GMarker(new GLatLng(lat,lon));
  }
  else 
  	marker = new GMarker(new GLatLng(lat,lon));
  GEvent.addListener(marker, "click",
    function() {
      marker.openInfoWindowHtml(html);
    } );
  this.map.addOverlay(marker);
}


  GPXParser.prototype.AddTrackSegmentToMap = function(trackSegment, segment_no) {
    var trackpoints = trackSegment.getElementsByTagName("trkpt");
    
    if (!this.tracks[segment_no]) this.tracks[segment_no]= new Array();
    if (!this.elevationProfile[segment_no]) this.elevationProfile[segment_no]= new Array();
    
    if (trackpoints.length == 0) {
	    return; //latlngbounds;
    }

    // process first point
    var ele = 0;
    var is=0;
    var j=0;
    var lastlon = 0;
    var lastlat=0;
    var lastele=0;
    var start=0;
    
    start = this.tracks[segment_no].length;
    if (start==0) {
      lastlon = parseFloat(trackpoints[0].getAttribute("lon"));
      lastlat = parseFloat(trackpoints[0].getAttribute("lat"));
      latlng = new GLatLng(parseFloat(trackpoints[0].getAttribute("lat")),parseFloat(trackpoints[0].getAttribute("lon")));
      this.tracks[segment_no][0] = latlng;
      this.elevationProfile[segment_no][0] = new Object();
      if (trackpoints[0].getElementsByTagName("ele").length > 0) 
	ele =  parseFloat(trackpoints[0].getElementsByTagName("ele").item(0).childNodes[0].nodeValue);
      this.elevationProfile[segment_no][0].ele = ele;
      lastele = ele;
      this.elevationProfile[segment_no][0].dist = 0;
      this.elevationProfile[segment_no][0].index = 0;
      this.statistics[segment_no] = new Object();
      this.statistics[segment_no].up = 0;
      this.statistics[segment_no].down = 0;
      this.statistics[segment_no].minh = ele;
      this.statistics[segment_no].maxh = ele;
      is=1;
    } else {
      start--;
      lastele =	this.elevationProfile[segment_no][start].ele;
      lastlon = this.tracks[segment_no][start].x;
      lastlat=  this.tracks[segment_no][start].y;
      j=start;
    }

    for (var i= is; i < trackpoints.length; i++)
    {
      var lon = parseFloat(trackpoints[i].getAttribute("lon"));
      var lat = parseFloat(trackpoints[i].getAttribute("lat"));
      latlng = new GLatLng(lat,lon);

      // Verify that this is far enough away from the last point to be used.
      var dist = this.tracks[segment_no][j].distanceFrom(latlng); 

      if ( dist > this.mintrackpointdelta )
      {
      	j++;
      	// Get Elevation
      	if (trackpoints[i].getElementsByTagName("ele").length > 0) 
      	  ele =  parseFloat(trackpoints[i].getElementsByTagName("ele").item(0).childNodes[0].nodeValue);
      	else ele = 0;
      	  
      	this.tracks[segment_no][j] = latlng;
      	this.elevationProfile[segment_no][j] = new Object();
      	this.elevationProfile[segment_no][j].ele = ele;
      	this.elevationProfile[segment_no][j].dist = dist + this.elevationProfile[segment_no][j-1].dist;
         
      	if (ele > this.statistics[segment_no].maxh) this.statistics[segment_no].maxh = ele;
      	if (ele < this.statistics[segment_no].minh) this.statistics[segment_no].minh = ele;
      
      	del = this.elevationProfile[segment_no][j].ele - lastele;
      	lastele = this.elevationProfile[segment_no][j].ele;
      	 if (del < 0)
      	   this.statistics[segment_no].down += (del * -1);
      	 else 
      	   this.statistics[segment_no].up += del;
      }
    }
  }

GPXParser.prototype.draw = function()
{
  var k = findPos(this.CanvasElem);
  this.hx=k[0];
  this.hy=k[1];
  var track_no=0;  
  
  
  //draw elevation chart
  this.xo = 34;
	var yo = 0;
	
	var mapelem = document.getElementById("gmap"+this.id);
	this.plotw = mapelem.clientWidth-this.xo-1;
	var h = 100
	var ypad = 10;
	
	
	if (this.statistics[track_no] && this.elevationProfile[track_no] ) {
	  var dist = 0;
    for (var k = 0; k<this.elevationProfile.length; k++) {
	     dist += this.elevationProfile[k][this.elevationProfile[k].length-1].dist;
	  }
  	//var scaley = (h-ypad*2) / (this.statistics[track_no].maxh - this.statistics[track_no].minh);
  	this.scalex = (this.plotw-2) / dist;

  	//var y = new Array();
    var lastdist = 0;
    var j =0;
    var i=0;
  	for (j = 0; j<this.elevationProfile.length; j++) {
      for (i = 0;i < this.elevationProfile[j].length; i++) {
    	  //y[i] = Math.round(h-(((this.elevationProfile[j][i].ele - this.statistics[j].minh) * scaley) + ypad + yo));
     	  this.elevationProfile[j][i].x = Math.round((lastdist + this.elevationProfile[j][i].dist) * this.scalex) + 1 + this.xo;      
    	}
    	lastdist += this.elevationProfile[j][i-1].dist;  	
  	}
  	document.getElementById("canvas"+this.id).onmousemove = MouseMoveHandler;
  	}


  // Center around the middle of the points
  if (!this.map)
    this.map = new GMap2(document.getElementById("gmap"+this.id));
  else 
    this.map.checkResize();
	var centerlon = (this.maxlon + this.minlon) / 2;
	var centerlat = (this.maxlat + this.minlat) / 2;
	var bounds = new GLatLngBounds(new GLatLng(this.minlat, this.minlon), new GLatLng(this.maxlat, this.maxlon));
	this.map.setCenter(new GLatLng(centerlat, centerlon), this.map.getBoundsZoomLevel(bounds), this.maptype);

for (var i = 0; i < this.tracks.length; i++) {
  this.map.addOverlay(new GPolyline(this.tracks[i], this.trackcolour, this.trackwidth));
  }
  this.DebugElem.innerHTML = "<div style=\"float:left;font-size:300%;font-weight:bolder;color:silver;margin-top:6px;\">GPX</div>";
  this.DebugElem.innerHTML += "<div style=\"padding-right:10px\">";
  var absl = 0;
  var absu = 0;
  var absd = 0;
  for (var i=0; i < this.elevationProfile.length;i++) {
    absl += this.elevationProfile[i][this.elevationProfile[i].length-1].dist;
    absu += this.statistics[i].up;
    absd += this.statistics[i].down;
  }
  if (this.elevationProfile[0]) this.DebugElem.innerHTML += "<div style=\"text-align:right\">Länge  = " + Math.round(absl / 1000) + " km</div>";
  if (this.elevationProfile[0]) this.DebugElem.innerHTML += "<div style=\"text-align:right\">Aufstieg  = " + Math.round(absu) + " m</div>";
  if (this.elevationProfile[0]) this.DebugElem.innerHTML += "<div style=\"text-align:right\">Abstieg  = " + Math.round(absd) + " m</div>";
  this.DebugElem.innerHTML += "</div>";
}

GPXParser.prototype.AddTrackToMap = function(track, track_no)
{
  var segments = track.getElementsByTagName("trkseg");
	for (var i=0; i < segments.length; i++)
	{
		this.AddTrackSegmentToMap(segments[i], track_no);
	}
	var name = track.getElementsByTagName("name");
  if (name.length > 0) {
    this.statistics[track_no].name = name[0].childNodes[0].nodeValue;
  }
}

function findPos(obj) {
	var curleft = curtop = 0;
  if (obj.offsetParent) {
    do {
			curleft += obj.offsetLeft;
			curtop += obj.offsetTop;
		} while (obj = obj.offsetParent);
	return [curleft,curtop];
	}
}

GPXParser.prototype.findIndexByKm = function(x,k,obj) {
    var end = obj.elevationProfile[k].length-1;
    var beg = 0;
    var mid = 0; 
    while(1) {
        mid = ((end + beg)/2)|0;
        if (beg > end || obj.elevationProfile[k][mid].x == x) return mid;
        if (x < obj.elevationProfile[k][mid].x ) 
          end = mid-1;
        else  
          beg = mid+1;
    }
}

function findPosX(obj)
  {
    var curleft = 0;
    if(obj.offsetParent)
        while(1) 
        {
          curleft += obj.offsetLeft;
          if(!obj.offsetParent)
            break;
          obj = obj.offsetParent;
        }
    else if(obj.x)
        curleft += obj.x;
    return curleft;
  }


function MouseMoveHandler(evt) {
  var id =0;
  if (!evt) var evt = window.event;
  evt.cancelBubble = true;
  if (evt.stopPropagation) evt.stopPropagation();
  var tg = (window.event) ? evt.srcElement : evt.currentTarget;
  id = tg.id.substring(6);
  id *= 1;

  var x = evt.clientX-me[id].hx;
  var line =  document.getElementById("line"+id);


  if ( (x > me[id].xo) && (x < (me[id].xo + me[id].plotw) )) { 
    var k = 0;
    for (k = 0; k < me[id].elevationProfile.length; k++) {
     if (me[id].elevationProfile[k][me[id].elevationProfile[k].length-1].x >= x) break;
    }
    me[id].map.removeOverlay(me[id].eleMarker);
    me[id].eleMarker.setLatLng(me[id].tracks[k][me[id].findIndexByKm(x,k,me[id])]);
    me[id].eleMarker.show();
    me[id].map.addOverlay(me[id].eleMarker);
    line.style.visibility="visible";
    line.style.left=x+"px";
    
    if (me[id].statistics[k].name) {
      document.getElementById("title"+id).innerHTML = me[id].statistics[k].name;
      var t = document.getElementById("text"+id);
      t.innerHTML = "<div style=\"text-align:left\">Länge  = " + Math.round(me[id].elevationProfile[k][me[id].elevationProfile[k].length-1].dist / 1000) + " km</div>";
      t.innerHTML += "<div style=\"text-align:left\">Aufstieg  = " + Math.round(me[id].statistics[k].up) + " m</div>";
      t.innerHTML += "<div style=\"text-align:left\">Abstieg  = " + Math.round(me[id].statistics[k].down) + " m</div>";
    }
  }
  else {
   line.style.visibility="hidden";
   me[id].eleMarker.hide();
   }
  return false;
}

function MouseExitHandler(e) {
  var id=0;
  if (!e) var e = window.event;
  var ct = (window.event) ? e.srcElement : e.currentTarget;
  var tg = (window.event) ? e.srcElement : e.target;
  e.cancelBubble = true;
  if (e.stopPropagation) e.stopPropagation();
  id = ct.id.substring(6);
  if (tg.id != "canvas" + id) return;
  var line =  document.getElementById("line" + id);
  line.style.visibility="hidden";
  me[id].eleMarker.hide();
  return false;
}

GPXParser.prototype.CenterAndZoom = function (trackSegment, maptype)
{
	var pointlist = new Array("trkpt", "wpt");
	this.minlat = 0;
	this.maxlat = 0;
	this.minlon = 0;
	this.maxlon = 0;
	this.maptype = maptype;
	
	var meta = trackSegment.getElementsByTagName("bounds");
	if (meta.length>0) {
	  this.minlat = parseFloat(meta[0].getAttribute("minlat"));
	  this.maxlat = parseFloat(meta[0].getAttribute("maxlat"));
	  this.minlon = parseFloat(meta[0].getAttribute("minlon"));
	  this.maxlon = parseFloat(meta[0].getAttribute("maxlon"));
	} else
	for (var pointtype=0; pointtype < pointlist.length; pointtype++)
	{

		// Center the map and zoom on the given segment.
		var trackpoints = trackSegment.getElementsByTagName(pointlist[pointtype]);

		// If the min and max are uninitialized then initialize them.
		if ( (trackpoints.length > 0) && (this.minlat == this.maxlat) && (this.minlat == 0) )
		{
			this.minlat = parseFloat(trackpoints[0].getAttribute("lat"));
			this.maxlat = parseFloat(trackpoints[0].getAttribute("lat"));
			this.minlon = parseFloat(trackpoints[0].getAttribute("lon"));
			this.maxlon = parseFloat(trackpoints[0].getAttribute("lon"));
		}

		for (var i=1; i < trackpoints.length; i++)
		{
			var lon = parseFloat(trackpoints[i].getAttribute("lon"));
			var lat = parseFloat(trackpoints[i].getAttribute("lat"));

			if (lon < this.minlon) this.minlon = lon;
			if (lon > this.maxlon) this.maxlon = lon;
			if (lat < this.minlat) this.minlat = lat;
			if (lat > this.maxlat) this.maxlat = lat;
		}
	}
}


GPXParser.prototype.AddTrackpointsToMap = function ()
{
	var tracks = this.xmlDoc.documentElement.getElementsByTagName("trk");
	this.DebugElem.innerHTML='';
	for (var i=0; i < tracks.length; i++)
	{
		this.AddTrackToMap(tracks[i], i);
	}
}

GPXParser.prototype.AddWaypointsToMap = function ()
{
	var waypoints = this.xmlDoc.documentElement.getElementsByTagName("wpt");

	for (var i=0; i < waypoints.length; i++)
	{
		this.CreateMarker(waypoints[i]);
	}
}

/* Helper Functions */

function stripHTML(s){
 return s.replace(/\\&/g, '&amp;').replace(/\\</g, '&lt;').replace(/\\>/g, '&gt;').replace(/\\t/g, '&nbsp;&nbsp;&nbsp;').replace(/\\n/g, '<br />');
}
