﻿// GPX2GM;
// Darstellung von GPS-Daten aus einer GPX-Datei in Google Maps
// Version 4.7
// ab 1. 11. 2011 Jürgen Berkemeier
// www.j-berkemeier.de

/*Scaling = {   // nur paarweise verwenden
  hmin:0,hmax:1000,  // Höhenplot
  smin:-30,smax:30,  // Steigungsplot
  vmin:0,vmax:100    // Geschwindigkeitsplot
}*/

( function() { // anonyme sofort ausgeführte Funktion

if(typeof(GPXVIEW_Debuginfo)=="undefined") 
  var GPXVIEW_Debuginfo = (location.search.search("GPXVIEW_Debuginfo")!=-1) 
                       && (location.search.search("GPXVIEW_Debuginfo=false")==-1) ;    
if(GPXVIEW_Debuginfo) var GPXVIEW_Start = (new Date()).getTime();

var GPXVIEW_Path = (function() {
  var scr = document.getElementsByTagName("script");
  var Path = "";
  for(var i=0;i<scr.length;i++) if(scr[i].src && scr[i].src.length) {
    var path = scr[i].src;
    var pos = path.search("GPX2GM.js");
    if(pos!=-1) {
      Path = path.substring(0,pos);
      break;
    }
  } 
  return Path;  
})();

document.write('<script src="'+GPXVIEW_Path+'GPX2GM_Defs.js" type="text/javascript"><\/script>'); 

document.write('<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key='+key+'&sensor=false" type="text/javascript"><\/script>');

(function() {
  var kannCanvas=false,cv,ct;
  cv = document.createElement("canvas");
  if(cv) {
    if (cv.getContext) ct = cv.getContext("2d");
    if(ct) kannCanvas = true;
    if(kannCanvas) {
      if(!ct.fillRect) kannCanvas = false;
      if(!ct.fillText) kannCanvas = false;
    }
    delete ct;
  }
  delete cv;
  document.write('<script src="'+GPXVIEW_Path+(kannCanvas?'gra_canvas.js':'gra_div.js')+'"><\/script>'); 
})();

document.write('<script src="'+GPXVIEW_Path+'plot.js" type="text/javascript"><\/script>'); 

function makeMap(ID) {
  JB_GM_Info(ID,"GPXViewer 4.7",false);
  var largemapcontrol = (typeof(Largemapcontrol)!="undefined") ? Largemapcontrol : false;
  var overviewmapcontrol = (typeof(Overviewmapcontrol)!="undefined") ? Overviewmapcontrol : false;
  var legende = (typeof(Legende)!="undefined") ? Legende : true;
  var legende_fnm = (typeof(Legende_fnm)!="undefined") ? Legende_fnm  : true;
  var legende_rr = (typeof(Legende_rr)!="undefined") ? Legende_rr  : true;
  var legende_trk = (typeof(Legende_trk)!="undefined") ? Legende_trk : true;
  var legende_rte = (typeof(Legende_rte)!="undefined") ? Legende_rte : true;
  var legende_wpt = (typeof(Legende_wpt)!="undefined") ? Legende_wpt : true;
  var tracks_verbinden = (typeof(Tracks_verbinden)!="undefined") ? Tracks_verbinden : false;
  var readspeed = (typeof(Readspeed)!="undefined") ? Readspeed : true;
  var trackover = (typeof(Trackover)!="undefined") ? Trackover : true;
  var shwpcmt = (typeof(Shwpcmt)!="undefined") ? Shwpcmt : true;
  var shwpdesc = (typeof(Shwpdesc)!="undefined") ? Shwpdesc : false;
  var shwptime = (typeof(Shwptime)!="undefined") ? Shwptime : false;
  var shtrcmt = (typeof(Shtrcmt)!="undefined") ? Shtrcmt : false;
  var shtrdesc = (typeof(Shtrdesc)!="undefined") ? Shtrdesc : false;
  var shtrx = (typeof(Shtrx)!="undefined") ? Shtrx : true;
  var shtrt = (typeof(Shtrt)!="undefined") ? Shtrt : true;
  var shtrv = (typeof(Shtrv)!="undefined") ? Shtrv : true;
  var shtrh = (typeof(Shtrh)!="undefined") ? Shtrh : true;
  var shtrs = (typeof(Shtrs)!="undefined") ? Shtrs : true;
  var shtrvmitt = (typeof(Shtrvmitt)!="undefined") ? Shtrvmitt : false;
  var shrtcmt = (typeof(Shrtcmt)!="undefined") ? Shrtcmt : false;
  var shrtdesc = (typeof(Shrtdesc)!="undefined") ? Shrtdesc : false;
  var displaycolor = (typeof(Displaycolor)!="undefined") ? Displaycolor : false;
  var laengen3d = (typeof(Laengen3d)!="undefined") ? Laengen3d : false;
  var hkorr = (typeof(Hkorr)!="undefined") ? Hkorr : true;
  var hglattlaen = (typeof(Hglattlaen)!="undefined") ? Hglattlaen : 500; // in Meter
  var vglattlaen = (typeof(Vglattlaen)!="undefined") ? Vglattlaen : 100; // in Meter
  var vglatt = (typeof(Vglatt)!="undefined") ? Vglatt : false;
  var tdiff = (typeof(Tdiff)!="undefined") ? Tdiff : 0; // in Stunden
  var twidth = 2.0;  // Linienstärke Track
  var rwidth = 2.0;  // Linienstärke Route
  var widtho = 3.0;  // Linienstärke Track und Route bei Mouseover
  var topac = 0.8;   // Transparenz Trackfarbe
  var ropac = 0.8;   // Transparenz Routenfarbe
  var tcols = new Array("#ff0000","#00ff00","#0000ff","#aaaa00","#ff00ff","#00ffff","#000000"); // Trackfarben in #rrggbb für rot grün blau
  var rcols = new Array("#800000","#008000","#000080","#808000","#800080","#008080","#808080"); // Routenfarben
  var colo = "#000000";   // Track- und Routenfarbe bei Mouseover
  var popup_Pars = "width=900,height=790,screenX=970,screenY=0,status=yes,scrollbars=yes";
  
  var hscale=[],sscale=[],vscale=[]
  if(typeof(Scaling)!="undefined") {
    if(typeof(Scaling.hmin)!="undefined" && typeof(Scaling.hmax)!="undefined") hscale = [{x:.0001,h:Scaling.hmin} ,{x:.0002,h:Scaling.hmax}] ;
    if(typeof(Scaling.smin)!="undefined" && typeof(Scaling.smax)!="undefined") sscale = [{x:.0001,s:Scaling.smin} ,{x:.0002,s:Scaling.smax}] ;
    if(typeof(Scaling.vmin)!="undefined" && typeof(Scaling.vmax)!="undefined") vscale = [{x:.0001,v:Scaling.vmin} ,{x:.0002,v:Scaling.vmax}] ;
  }

  var icons = {
    scenic: { image:"scenic.png",iconSize:[21.0,31.0],
              shadow:"shadow.png",shadowSize:[52.0,29.0],
              iconAnchor:[5.0,30.0],infoWindowAnchor:[10.0,0.0] },
    marker: { image:"marker.gif",iconSize:[11.0,11.0],iconAnchor:[5.0,5.0] }
  }
  if(typeof(GPX2GM_Icons)=="object") for(o in GPX2GM_Icons) icons[o] = GPX2GM_Icons[o];
  
  var makeIcon = function(SymName) {
    JB_GM_Info(ID,"makeIcon "+SymName,false);
    SymName = SymName.toLowerCase();
    if(icons[SymName]) {
      var icon = new GIcon();
      if(icons[SymName].image) icon.image = GPXVIEW_Path + icons[SymName].image;
      if(icons[SymName].iconSize) icon.iconSize = new GSize(icons[SymName].iconSize[0],icons[SymName].iconSize[1]);
      if(icons[SymName].shadow) icon.shadow = GPXVIEW_Path + icons[SymName].shadow;
      if(icons[SymName].shadowSize) icon.shadowSize = new GSize(icons[SymName].shadowSize[0],icons[SymName].shadowSize[1]);
      if(icons[SymName].iconAnchor) icon.iconAnchor = new GPoint(icons[SymName].iconAnchor[0],icons[SymName].iconAnchor[1]);
      if(icons[SymName].infoWindowAnchor) 
        icon.infoWindowAnchor = new GPoint(icons[SymName].infoWindowAnchor[0],icons[SymName].infoWindowAnchor[1]);
    }
    else
      var icon = new GIcon(G_DEFAULT_ICON);
    return icon;
  } // makeIcon

  var polylineEncoder = new PolylineEncoder(); 
  var dieses = this;
  var gpxdaten = {tracks:{},routen:{},wegpunkte:{}};
  var id = ID;

  var profil = {hp:{x:"x",y:"h"},hpt:{x:"t",y:"h"},wp:{x:"t",y:"x"},sp:{x:"x",y:"s"},spt:{x:"t",y:"s"},vp:{x:"x",y:"v"},vpt:{x:"t",y:"v"}};
  profil.hpt.ytext = profil.hp.ytext = "H<br />ö<br />h<br />e<br />&nbsp;<br />in<br />&nbsp;<br />m";
  profil.spt.ytext = profil.sp.ytext = "Stg.<br />&nbsp;<br />in<br />&nbsp;<br />%";
  profil.vpt.ytext = profil.vp.ytext = "V<br />&nbsp;<br />in<br />&nbsp;<br />km/h";
  profil.wp.ytext ="x<br />&nbsp;<br />in<br />&nbsp;<br />km";
  profil.hp.xtext = profil.vp.xtext = profil.sp.xtext = "Strecke in km";
  profil.hpt.xtext = profil.vpt.xtext = profil.spt.xtext = profil.wp.xtext = "Zeit in sec"; 
  profil.hpt.scale = profil.hp.scale = hscale;
  profil.spt.scale = profil.sp.scale = sscale;
  profil.vpt.scale = profil.vp.vcale = vscale;
  for(var p in profil) profil[p].id = ID+"_"+p;
  
  var load = false
  var latmin=1000,latmax=-1000,lonmin=1000,lonmax=-1000;
  var zoom = 1;
  var osm_mapnik_map,osm_tah_map,osm_cycle_map;
  var fname,maptype;
  var markericon = makeIcon("marker") ;
  var movemarker;
  var infobox = document.createElement("div");
  infobox.style.position = "absolute";
  infobox.style.visibility = "hidden";
  infobox.style.border = "1px solid black";
  infobox.style.backgroundColor = "white";
  infobox.style.padding = "3px";
  infobox.style.borderRadius  = "5px" ;
  infobox.style.MozBorderRadius  = "5px" ;
  infobox.style.WebkitBorderRadius  = "5px" ;
  infobox.style.fontSize = "0.8em"; 
  infobox.style.lineHeight = "1.2em"; 
  var div = document.getElementById(id);
  var w = div.offsetWidth;
  var h = div.offsetHeight;
  var MapHead = document.createElement("div");
  MapHead.id = "map_head"+id;
  MapHead.style.margin = 0;
  MapHead.style.padding = 0;
//  MapHead.style.fontSize = "0.8em";
//  MapHead.style.lineHeight = "1.5em";
  MapHead.appendChild(document.createTextNode(": "));
  var mapdiv = document.createElement("div");
  mapdiv.id = "map_"+id;
  mapdiv.style.width = w+"px";
  while(div.hasChildNodes()) div.removeChild(div.firstChild);
  if(!legende) MapHead.style.display="none";
  div.appendChild(MapHead);
  div.appendChild(mapdiv);
  if (legende) mapdiv.style.height = h-mapdiv.offsetTop+MapHead.offsetTop+"px";
  else         mapdiv.style.height = h+"px";
  var map = new GMap2(mapdiv);
  if(largemapcontrol) map.addControl(new GLargeMapControl());
  else                map.addControl(new GSmallMapControl());
  if(overviewmapcontrol) map.addControl(new GOverviewMapControl());
  map.addMapType(G_PHYSICAL_MAP);
  var copyrightCollection = new GCopyrightCollection('&copy; 2011 <a href="http://www.openstreetmap.org/">OpenStreetMap</a>');
  copyrightCollection.addCopyright(new GCopyright(1,new GLatLngBounds(new GLatLng(-90,-180),new GLatLng(90,180)),0,'(<a rel="license" href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>)'));
  var tilelayers_mapnik = new Array();
  tilelayers_mapnik[0] = new GTileLayer(copyrightCollection, 0, 18);
  tilelayers_mapnik[0].getTileUrl = function(a, z) { return "http://tile.openstreetmap.org/" + z + "/" + a.x + "/" + a.y + ".png"; };
  tilelayers_mapnik[0].isPng = function() { return true; };
  tilelayers_mapnik[0].getOpacity = function() { return 1.0; };
  osm_mapnik_map = new GMapType(tilelayers_mapnik,new GMercatorProjection(19), "OSM Mapnik",{ urlArg: 'mapnik', linkColor: '#000000' });
  map.addMapType(osm_mapnik_map);
  var tilelayers_tah = new Array();
  tilelayers_tah[0] = new GTileLayer(copyrightCollection, 0, 17);
  tilelayers_tah[0].getTileUrl = function(a, z) { return "http://tah.openstreetmap.org/Tiles/tile/" + z + "/" + a.x + "/" + a.y + ".png"; };
  tilelayers_tah[0].isPng = function() { return true; };
  tilelayers_tah[0].getOpacity = function() { return 1.0; };
  osm_tah_map = new GMapType(tilelayers_tah,new GMercatorProjection(19), "OSM T&H",{ urlArg: 'tah', linkColor: '#000000' });
  map.addMapType(osm_tah_map);
  copyrightCollection = new GCopyrightCollection('&copy; 2011 <a href="http://www.opencyclemap.org/">OpenCycleMap</a> <a href="http://www.openstreetmap.org/">OpenStreetMap</a>');
  copyrightCollection.addCopyright(new GCopyright(1,new GLatLngBounds(new GLatLng(-90,-180),new GLatLng(90,180)),0,'(<a rel="license" href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>)'));
  var tilelayers_cycle = new Array();
  tilelayers_cycle[0] = new GTileLayer(copyrightCollection, 0, 17);
  tilelayers_cycle[0].getTileUrl = function(a, z) { return "http://andy.sandbox.cloudmade.com/tiles/cycle/" + z + "/" + a.x + "/" + a.y + ".png"; };
  tilelayers_cycle[0].isPng = function() { return true; };
  tilelayers_cycle[0].getOpacity = function() { return 1.0; };
  osm_cycle_map = new GMapType(tilelayers_cycle,new GMercatorProjection(19), "OSM Cycle",{ urlArg: 'cycle', linkColor: '#000000' });
  map.addMapType(osm_cycle_map);
  map.addControl(new GMenuMapTypeControl());
//  map.addControl(new GMapTypeControl());
//  map.addControl(new GHierarchicalMapTypeControl());
//  map.addMapType(G_SATELLITE_3D_MAP); // benötigt Plug-In
  map.addControl(new GScaleControl());
  map.enableScrollWheelZoom();
  map.getPane(G_MAP_FLOAT_PANE).appendChild(infobox);
  GEvent.addListener(map,"infowindowopen",function() { map.savePosition(); } );
  GEvent.addListener(map,"infowindowclose",function() { map.returnToSavedPosition(); } );

  for(p in profil) {
    profil[p].ele = document.getElementById(profil[p].id);
    if(profil[p].ele) {
      profil[p].diag = new JB_plot(profil[p].id,profil[p].x,profil[p].y);
      if (profil[p].ele.className &&profil[p].ele.className.search(/(^|\s)no_x(\s|$)/i)!=-1) profil[p].xtext = "";
      JB_GM_Info(id,"Profil, ID: "+profil[p].id+" gefunden",false);
    }
  }
  
  this.Spur = function(fn,mpt) {
    JB_GM_Info(id,"Spur, Filename: "+fn,false);
    if(mpt=="osm_mapnik_map") maptype = osm_mapnik_map;
    else if(mpt=="osm_tah_map") maptype = osm_tah_map;
    else if(mpt=="osm_cycle_map") maptype = osm_cycle_map;
    else maptype = mpt;
    if (fname!=fn) {
      var infodiv = document.createElement("div");
      infodiv.style.position = "relative";
      infodiv.style.width = w/2-40+"px";
      infodiv.style.height = h/2-40+"px";
      infodiv.style.left = w/4+"px";
      infodiv.style.top = -3*h/4+"px";
      infodiv.style.border = "3px solid black";
      infodiv.style.backgroundColor = "white";
      infodiv.style.padding = "30px";
      infodiv.style.borderRadius  = "5px" ;
      infodiv.style.MozBorderRadius  = "5px" ;
      infodiv.style.WebkitBorderRadius  = "5px" ;
      infodiv.style.fontSize = "1.2em"; 
      infodiv.style.lineHeight = "3em"; 
      infodiv.style.zIndex = "1000";
      infodiv.style.textAlign = "center";
      infodiv.innerHTML = "Bitte warten.<br />Daten werden geladen.";
      div.appendChild(infodiv);
      JB_GM_Info(id,"info da",false);
      fname = fn;
      JB_GM_Info(id,"Lade Datei "+fname,false);
      window.setTimeout( function() { GDownloadUrl(fname, function(data, responseCode) {
        if (responseCode != 200 && responseCode != 0 ) {
          JB_GM_Info(id,"Beim Oeffnen der Datei "+fname+" ist der Fehler "+responseCode+" aufgetreten!",true);
          div.removeChild(infodiv);
          JB_GM_Info(id,"info weg",false);
          return;
        }
        dieses.parseGPX(data) ;
        dieses.setMapHead();
        zoom = map.getBoundsZoomLevel(new GLatLngBounds(new GLatLng(latmin,lonmin),new GLatLng(latmax,lonmax))); // sw, ne
        dieses.rescale();
        if (maptype != undefined) map.setMapType(maptype);
        dieses.checkBoxes();
        div.removeChild(infodiv);
        JB_GM_Info(id,"info weg",false);
      } ) }, 1);
    }
    else {
      dieses.checkBoxes()
    }
  } // Spur
  var chkwpt,chktrk,chkrt;
  this.setMapHead = function() {
    JB_GM_Info(id,"setMapHead",false);
    if(legende_fnm) MapHead.innerHTML = fname.replace(/.+\//,"") + ": ";
    else            MapHead.innerHTML = " ";
    if(gpxdaten.wegpunkte.anzahl) {
      if(gpxdaten.wegpunkte.anzahl==1) var texte=new Array("Wegpunkt"+String.fromCharCode(160));
      else if(gpxdaten.wegpunkte.anzahl>1) var texte=new Array("Wegpunkte"+String.fromCharCode(160));
      chkwpt = new JB_CheckBoxGroup(MapHead.id,texte,ID+"_wpt",["black"],legende_wpt,dieses.checkBoxes);
    }
    if(gpxdaten.tracks.anzahl) {
      var texte=new Array()
      if(gpxdaten.tracks.anzahl==1) {
        if(legende_rr) {
          texte[0] = "Track ("+Number(gpxdaten.tracks.track[0].laenge.toPrecision(10).toString(10))+"km";
          if(typeof(gpxdaten.tracks.track[0].rauf)!="undefined") 
            texte[0] += ", +"+gpxdaten.tracks.track[0].rauf+"m, -"+gpxdaten.tracks.track[0].runter+"m) "+String.fromCharCode(160);
          else 
            texte[0] += ") "+String.fromCharCode(160);
        }
        else
          texte[0] = "Track ("+Number(gpxdaten.tracks.track[0].laenge.toPrecision(10).toString(10))+"km) "+String.fromCharCode(160);
      }
      else if(gpxdaten.tracks.anzahl>1) {
        if(legende_rr) {
          var rrflag=true;
          for(var i=0;i<gpxdaten.tracks.anzahl;i++) {
            texte[i+1] = gpxdaten.tracks.track[i].name+" ("+Number(gpxdaten.tracks.track[i].laenge.toPrecision(10).toString(10))+"km";
            if(typeof(gpxdaten.tracks.track[i].rauf)!="undefined") {
              texte[i+1] += " ,"+ gpxdaten.tracks.track[i].rauf +"m, -"+gpxdaten.tracks.track[i].runter+"m)";
            }
            else {
              texte[i+1] += ")";
              rrflag = false;
            }
          }
          texte[0] = "Tracks ("+Number(gpxdaten.tracks.laenge.toPrecision(10).toString(10))+"km"
          if(rrflag) texte[0] += " ,+"+gpxdaten.tracks.rauf+"m, -"+gpxdaten.tracks.runter+"m) "+String.fromCharCode(160);
          else       texte[0] += ") "+String.fromCharCode(160);
        }
        else {
          texte[0] = "Tracks ("+Number(gpxdaten.tracks.laenge.toPrecision.toPrecision(10).toString(10))+"km) "+String.fromCharCode(160);
          for(var i=0;i<tracks.length;i++) texte[i+1] = gpxdaten.tracks.track[i].name+" ("+Number(gpxdaten.tracks.track[i].laenge.toPrecision(10).toString(10))+"km)";
        }
      }
      chktrk = new JB_CheckBoxGroup(MapHead.id,texte,ID+"_trk",tcols,legende_trk,dieses.checkBoxes);
    }
    if(gpxdaten.routen.anzahl) {
      var texte=new Array()
      if(gpxdaten.routen.anzahl==1)
        texte[0] = "Route ("+Number(gpxdaten.routen.route[0].laenge.toPrecision(10).toString(10))+"km) "+String.fromCharCode(160);
      else if(gpxdaten.routen.anzahl>1) {
        texte[0] = "Routen ("+Number(gpxdaten.routen.laenge.toPrecision(10).toString(10))+"km) "+String.fromCharCode(160);
        for(var i=0;i<gpxdaten.routen.anzahl;i++) texte[i+1] = gpxdaten.routen.route[i].name+" ("+Number(gpxdaten.routen.route[i].laenge.toPrecision(10).toString(10))+"km)";
      }
      chkrt = new JB_CheckBoxGroup(MapHead.id,texte,ID+"_rt",rcols,legende_rte,dieses.checkBoxes);
    }
  } // setMapHead
  this.checkBoxes = function(obj,ele) {
    var what="";
    if(chkwpt && chkwpt.status[0]) what += "w";
    if(chktrk && chktrk.status[0]) what += "t";
    if(chkrt  && chkrt.status[0] ) what += "r";
    dieses.show(what);
  }
  this.parseGPX = function(data) {
    JB_GM_Info(id,"parseGPX",false);
    var entf = new this.Entfernung();
    var t0=0;
    gpxdaten.tracks.laenge = 0;
    gpxdaten.tracks.rauf = 0;
    gpxdaten.tracks.runter = 0;
    gpxdaten.tracks.hflag = gpxdaten.tracks.tflag = gpxdaten.tracks.vflag = false;
    gpxdaten.tracks.hflagall = gpxdaten.tracks.tflagall = gpxdaten.tracks.vflagall = true;
    gpxdaten.tracks.track = new Array();
    load = false;
    latmin=1000;latmax=-1000;lonmin=1000;lonmax=-1000;
    var xml = GXml.parse(data);
    
    var trk = xml.documentElement.getElementsByTagName("trk"); // Tracks
    JB_GM_Info(id,trk.length +"Tracks gefunden",false);
    for(var k=0;k<trk.length;k++) {
      var tracki = { latlon:[], daten:[], laenge:0, rauf:0, runter:0, t0:0 ,vmitt:0};
      var trkk = trk[k];
      tracki.name = JB_getTag(trkk,"name","Track "+k,true);
      tracki.cmt = JB_getTag(trkk,"cmt","",true);
      tracki.desc = JB_getTag(trkk,"desc","",true);
      tracki.link = JB_getTag(trkk,"link","",true);
      tracki.farbe = tcols[k%tcols.length];
      if(displaycolor) {
        var ext = trkk.getElementsByTagName("extensions");
        if(ext.length) tracki.farbe = JB_getTagNS(ext[0],"gpxx","DisplayColor",tcols[k%tcols.length],false)
      }
      var trkpts = trkk.getElementsByTagName("trkpt"); // Trackpunkte
      var trkptslen = trkpts.length;
      var daten = new Array();
      var x0=0;
      if(tracks_verbinden && k>0) x0 = gpxdaten.tracks.laenge ;
      var tracklen = 0;
      var hflag=true,tflag=true,vflag=true,h,t,v;
      JB_GM_Info(id,trkptslen+" Trackpunkte in Track "+k+" gefunden",false);
      for(var i=0;i<trkptslen;i++) { // Trackdaten erfassen
        var trkptsi = trkpts[i];
        var lat = parseFloat(trkptsi.getAttribute("lat"));
        var lon = parseFloat(trkptsi.getAttribute("lon"));
        tracki.latlon.push(new GLatLng(lat,lon));
        if(lat<latmin) latmin=lat; if(lat>latmax) latmax=lat;
        if(lon<lonmin) lonmin=lon; if(lon>lonmax) lonmax=lon;
        h = JB_getTag(trkptsi,"ele","nf",false);
        if(h=="nf") hflag = false;
        else h = parseFloat(h.replace(",","."));
        var tt = JB_getTag(trkptsi,"time","nf",false);
        if(tt!="nf") { 
          if( i==0 ) {
            tracki.t0 = JB_utc2sec(tt);
            if (!tracks_verbinden || (tracks_verbinden&&k==0) ) { 
              t = 0; 
              t0 = tracki.t0;
            }              
          }
          else 
            t = JB_utc2sec(tt) - t0;
        }
        else {
          tflag = false;
          t = 0;
        }
        tt = JB_getTag(trkptsi,"speed","nf",false);
        if(vflag && readspeed && tt!="nf")
          v = parseFloat(tt);
        else {
          v = 0;
          vflag = false;
        }
        daten.push({lat:lat,lon:lon,t:t,h:h,v:v});
      }
      var anzfehl=0,nf=false,fehlst_n,fehlst=[];// Höhen korrigieren
      for(var i=0;i<trkptslen;i++) {
        if(daten[i].h == "nf") {              // Fehlstelle?
          anzfehl ++;                         // Zählen
          if(!nf) {                           // erste Fehlstelle im Block
            fehlst_n = {s:i,e:trkptslen-1};
            nf = true;
          }
        }
        else {
          if(nf) {                              // Erster Wert nach Fehlstelle?
            fehlst_n.e = i;                     // Ende Fehlstellenblock
            fehlst.push(fehlst_n);
            nf = false;
          }
        }
      }
      if(nf) {                                // Letzer Punkt im Felstellenblock
        fehlst_n.e = i;                       // Ende Fehlstellenblock
        fehlst.push(fehlst_n);
      }
      JB_GM_Info(id,anzfehl+" Fehlende Höhenwerte in "+fehlst.length+" Blöcken",false);  
      for(var i=0;i<fehlst.length;i++) 
        JB_GM_Info(id,"Fehlerblock Nr. "+i+":"+fehlst[i].s+" - "+fehlst[i].e,false);   
      if(hkorr && anzfehl/trkptslen < 0.3) { // weniger als 30% Fehlstellen
        hflag = true;
        for(i=0;i<fehlst.length;i++) {
          var s = fehlst[i].s, e = fehlst[i].e;
          if(s==0)
            for(var j=s;j<e;j++) daten[j].h = daten[e].h;
          else if(e==trkptslen)
            for(var j=s;j<e;j++) daten[j].h = daten[s-1].h;
          else 
            for(var j=s;j<e;j++) daten[j].h = daten[s-1].h + (daten[e].h-daten[s-1].h)*(j-s)/(e-s);
        }
      }
      if(trkptslen>1) {
        if(hflag && laengen3d) entf.init(daten[0].lat,daten[0].lon,daten[0].h) ;
        else                   entf.init(daten[0].lat,daten[0].lon,0.0) ;
        daten[0].x = tracklen+x0;
        daten[0].dx = 0;
        for(var i=1;i<trkptslen;i++) {
          if(hflag && laengen3d) var dx = entf.rechne(daten[i].lat,daten[i].lon,daten[i].h);
          else                   var dx = entf.rechne(daten[i].lat,daten[i].lon,0.0);
          tracklen += dx;
          daten[i].x = tracklen+x0;
          daten[i].dx = dx;
        }
      }
      else {
        hflag = tflag = vflag = false;
      }
      if(hflag) {
        daten = JB_smooth(daten,"x","h","hs",hglattlaen); //trkptslen/50); 
        daten = JB_diff(daten,"x","hs","s",0.1);
        daten = JB_smooth(daten,"x","s","s",hglattlaen); //trkptslen/50);
      }
      if(tflag && !vflag) {       
        if(vglatt) {
          daten = JB_smooth(daten,"t","x","xs",vglattlaen); //trkptslen/200); 
          daten = JB_diff(daten,"t","xs","v",3600);
          daten = JB_smooth(daten,"x","v","v",vglattlaen); //trkptslen/200);
        }
        else {
          daten = JB_diff(daten,"t","x","v",3600);
        }
      }
      JB_GM_Info(id,""+(hflag?"":"Keine ")+"Höhendaten gefunden",false);
      JB_GM_Info(id,""+(tflag?"":"Keine ")+"Zeitdaten gefunden",false);
      JB_GM_Info(id,""+(vflag?"":"Keine ")+"Geschwindigkeitsdaten gefunden",false);
      if(hflag) {
        var rr = this.rauf_runter(daten);
        tracki.rauf = rr.rauf;
        tracki.runter = rr.runter;
        gpxdaten.tracks.rauf += rr.rauf;      
        gpxdaten.tracks.runter += rr.runter;     
      }
      if(tflag) tracki.vmitt = tracklen/(daten[daten.length-1].t-daten[0].t)*3600;
      tracki.vmitt = Math.round(tracki.vmitt*10)/10;
      tracki.daten = daten;
      tracki.laenge = Math.round(tracklen*10)/10;
      tracki.hflag = hflag;
      tracki.tflag = tflag;
      tracki.vflag = vflag;
      gpxdaten.tracks.hflag |= hflag;
      gpxdaten.tracks.tflag |= tflag;
      gpxdaten.tracks.vflag |= vflag;
      gpxdaten.tracks.hflagall &= hflag;
      gpxdaten.tracks.tflagall &= tflag;
      gpxdaten.tracks.vflagall &= vflag;
      gpxdaten.tracks.track.push(tracki);
      gpxdaten.tracks.laenge += Math.round(tracklen*10)/10;
    }
    gpxdaten.tracks.anzahl = gpxdaten.tracks.track.length;
    gpxdaten.tracks.t0 = gpxdaten.tracks.anzahl ? gpxdaten.tracks.track[0].t0 : 0;

    var rte = xml.documentElement.getElementsByTagName("rte"); // Routen
    JB_GM_Info(id,rte.length +" Routen gefunden",false);
    gpxdaten.routen.laenge = 0;
    gpxdaten.routen.route = new Array();
    for(var j=0;j<rte.length;j++) {
      var rtej = rte[j];
      var rtepts = rtej.getElementsByTagName("rtept");
      JB_GM_Info(id,rtepts.length +" Zwischenziele gefunden",false);
      var routei = { latlon:[], laenge:0, farbe:rcols[j%rcols.length] };
      var route = new Array();
      var routlen = 0;
      routei.name = JB_getTag(rtej,"name","Route "+j,true);
      routei.cmt = JB_getTag(rtej,"cmt","",true);
      routei.desc = JB_getTag(rtej,"desc","",true);
      routei.link = JB_getTag(rtej,"link","",true);
      for(var i=0;i<rtepts.length;i++) { // Zwischenziele
        var rteptsi = rtepts[i];
        var lat = parseFloat(rteptsi.getAttribute("lat"));
        var lon = parseFloat(rteptsi.getAttribute("lon"));
        if(i==0) entf.init(lat,lon,0.0) ;
        else     routlen += entf.rechne(lat,lon,0.0);
        if(lat<latmin) latmin=lat; if(lat>latmax) latmax=lat;
        if(lon<lonmin) lonmin=lon; if(lon>lonmax) lonmax=lon;
        routei.latlon.push(new GLatLng(lat,lon));
        var ext = rteptsi.getElementsByTagName("extensions");
        if(ext.length) {
          var rpts = JB_GetElementsByTagNameNS(ext[0],"gpxx","rpt"); // Routenpunkte
          JB_GM_Info(id,rpts.length +" Routenpunkte (Garmin) gefunden",false);
          for(var k=0;k<rpts.length;k++) {
            var rptsk = rpts[k];
            var lat = parseFloat(rptsk.getAttribute("lat"));
            var lon = parseFloat(rptsk.getAttribute("lon"));
            routlen += entf.rechne(lat,lon,0.0);
            if(lat<latmin) latmin=lat; else if(lat>latmax) latmax=lat;
            if(lon<lonmin) lonmin=lon; else if(lon>lonmax) lonmax=lon;
            routei.latlon.push(new GLatLng(lat,lon));          }
        }
      }
      routei.laenge = Math.round(routlen*10)/10;
      gpxdaten.routen.route.push(routei);
      gpxdaten.routen.laenge += Math.round(routlen*10)/10;
    }
    gpxdaten.routen.anzahl = gpxdaten.routen.route.length;
    
    var wpts = xml.documentElement.getElementsByTagName("wpt"); // Waypoints
    JB_GM_Info(id,wpts.length +" Wegpunkte gefunden",false);
    gpxdaten.wegpunkte.wegpunkt = new Array();
    for(var i=0;i<wpts.length;i++) { // Wegpunktdaten
      var wpt = wpts[i];
      var lat = parseFloat(wpt.getAttribute("lat"));
      var lon = parseFloat(wpt.getAttribute("lon"));
      if(lat<latmin) latmin=lat; if(lat>latmax) latmax=lat;
      if(lon<lonmin) lonmin=lon; if(lon>lonmax) lonmax=lon;
      var waypoint = new Object();
      waypoint.lat = lat;
      waypoint.lon = lon;
      waypoint.name = JB_getTag(wpt,"name","",false);
      waypoint.cmt = JB_getTag(wpt,"cmt","",false);
      waypoint.desc =JB_getTag(wpt,"desc","",false);
      waypoint.link = JB_getTag(wpt,"link","",false);
      waypoint.sym = JB_getTag(wpt,"sym","default",false);
      waypoint.time = JB_utc2sec(JB_getTag(wpt,"time",0,false));
      gpxdaten.wegpunkte.wegpunkt.push(waypoint);
    }
    gpxdaten.wegpunkte.anzahl = gpxdaten.wegpunkte.wegpunkt.length;
    load = true;
  } // parseGPX
  this.showWpts = function() {
    JB_GM_Info(id,"showWpts",false);
    if (load) {
      for(var i=0;i<gpxdaten.wegpunkte.anzahl;i++) {
        var waypoint = gpxdaten.wegpunkte.wegpunkt[i];
        if(checkImageName(waypoint.name))
          map.addOverlay(createImgMarker(waypoint));
        else if (waypoint.link && waypoint.link.length)
          map.addOverlay(createLnkMarker(waypoint));
        else if (waypoint.name.length || waypoint.cmt.length || waypoint.desc.length)
          map.addOverlay(createTxtMarker(waypoint));
        else
          map.addOverlay(new GMarker(new GLatLng(waypoint.lat,waypoint.lon),{icon:makeIcon(waypoint.sym)}));
      }
    }
  } // showWpts
  this.showTracks = function() {
    JB_GM_Info(id,"showTracks",false);
    if (load) {
      for(var i=0;i<gpxdaten.tracks.anzahl;i++) {
        var tracki = gpxdaten.tracks.track[i];
        var daten = tracki.daten;
        profil.hp.pflag = profil.sp.pflag = tracki.hflag;
        profil.hpt.pflag = profil.spt.pflag = tracki.hflag && tracki.tflag;
        profil.vpt.pflag = profil.vp.pflag = tracki.tflag;
        profil.wp.pflag = tracki.tflag;
        if(daten.length>1 && chktrk.status[i==0?0:i+1]) {
          for(p in profil) {
            pr = profil[p];
            if(pr.ele /*&& pr.pflag*/) {
              if(pr.scale && pr.scale.length==2) {
                pr.scale[0][pr.x] = daten[0][pr.x];
                pr.scale[1][pr.x] = daten[daten.length-1][pr.x];
                pr.diag.scale(pr.scale);
              }
              else
                 pr.diag.scale(daten);
            }
          }
        }
      } 
      profil.hp.pflag = profil.sp.pflag = gpxdaten.tracks.hflag;
      profil.hpt.pflag = profil.spt.pflag = gpxdaten.tracks.hflag && gpxdaten.tracks.tflag;
      profil.vpt.pflag = profil.vp.pflag = gpxdaten.tracks.tflag;
      profil.wp.pflag = gpxdaten.tracks.tflag;
      for(p in profil) {
        var pr = profil[p];
        if(pr.ele /*&& pr.pflag*/) pr.diag.frame(50,35,pr.xtext,pr.ytext);
      }
      for(var i=0;i<gpxdaten.tracks.anzahl;i++) if(chktrk.status[gpxdaten.tracks.anzahl==1?0:i+1]) {
        var tracki = gpxdaten.tracks.track[i];
        var info = "<strong>"+tracki.name+"</strong>";
        if(shtrx) 
          info += "<br />Länge:&nbsp;"+Number(tracki.laenge.toPrecision(10).toString(10))+"&nbsp;km";
        if(shtrs && typeof(tracki.rauf)!="undefined" ) 
          info += "<br /><span style='white-space:nowrap;'>Höhenmeter: +"+tracki.rauf+" m / -"+tracki.runter+" m</span>";
        if(shtrt && tracki.t0>0) 
          info += "<br />Startzeit:  <span style='white-space:nowrap;'>" + JB_sec2string(tracki.t0,tdiff) + "</span>"; 
        if(shtrvmitt && tracki.vmitt>0)
          info += "<br /><span style='white-space:nowrap;'>V<sub>m</sub> = " + tracki.vmitt + " km/h</span>"
        if(shtrcmt) info += "<br />"+tracki.cmt;
        if(shtrdesc) info += "<br />"+tracki.desc;
        this.makePolyline(map,tracki,twidth,topac,widtho,colo,info);
        if(tracki.daten.length>1) {
          profil.hp.pflag = profil.sp.pflag = tracki.hflag;
          profil.hpt.pflag = profil.spt.pflag = tracki.hflag && tracki.tflag;
          profil.vpt.pflag = profil.vp.pflag = tracki.tflag;
          profil.wp.pflag = tracki.tflag;
          for(var p in profil) {
            var pr = profil[p];
            if(pr.ele && pr.pflag) pr.diag.plot(tracki.daten,tracki.farbe);
          }
        }
      }
      var ct=0;
      for(var i=0;i<chktrk.status.length;i++) {
        if(chktrk.status[i]) ct++;
        if(ct>1) break;
      }
      if(tracks_verbinden || ct==1) {
        var d_t = new Array();
        profil.hp.pflag = profil.sp.pflag = ct==1?gpxdaten.tracks.hflag:gpxdaten.tracks.hflagall;
        profil.hpt.pflag = profil.spt.pflag = 
         ct==1?gpxdaten.tracks.hflagall&&gpxdaten.tracks.tflag:gpxdaten.tracks.hflagall&&gpxdaten.tracks.tflagall;
        profil.vpt.pflag = profil.vp.pflag = ct==1?gpxdaten.tracks.tflag:gpxdaten.tracks.tflagall;
        profil.wp.pflag = ct==1?gpxdaten.tracks.tflag:gpxdaten.tracks.tflagall;
        if(gpxdaten.tracks.anzahl==1) 
          d_t = d_t.concat(gpxdaten.tracks.track[0].daten);
        else
          for(var i=0;i<gpxdaten.tracks.anzahl;i++) if(chktrk.status[i+1]) d_t = d_t.concat(gpxdaten.tracks.track[i].daten);
          if(d_t.length) {
            for(var p in profil) {
              var pr = profil[p];
              if(pr.ele && pr.pflag) pr.diag.markeron(d_t,dieses.markerstart,dieses.markerstop,dieses.markermove,"Linie") ;
            }
          }
      }
    }
  } // showTracks
  this.showRoutes = function() {
    JB_GM_Info(id,"showRoutes",false);
    if (load) {
      for(var i=0;i<gpxdaten.routen.anzahl;i++) if(chkrt.status[gpxdaten.routen.anzahl==1?0:i+1]) {
        var info = "<strong>"+gpxdaten.routen.route[i].name+"</strong>";
        if(shtrx)
          info += "<br />Länge:&nbsp;"+Number(gpxdaten.routen.route[i].laenge.toPrecision(10).toString(10))+"&nbsp;km";
        if(shrtcmt) info += "<br />"+gpxdaten.routen.route[i].cmt;
        if(shrtdesc) info += "<br />"+gpxdaten.routen.route[i].desc;
        this.makePolyline(map,gpxdaten.routen.route[i],rwidth,ropac,widtho,colo,info);
      }
    }
  } // showRoutes
  this.show = function(what) {
    JB_GM_Info(id,"show "+what,false);
    map.clearOverlays() ;
    profil.hp.pflag = profil.sp.pflag = gpxdaten.tracks.hflag;
    profil.hpt.pflag = profil.spt.pflag = gpxdaten.tracks.hflag && gpxdaten.tracks.tflag;
    profil.vpt.pflag = profil.vp.pflag = gpxdaten.tracks.tflag;
    profil.wp.pflag = gpxdaten.tracks.tflag;
    for(p in profil) {
      var pr = profil[p];
      if(pr.ele && pr.pflag) pr.diag.clear();
    }
    if (what.search("w") != -1 ) dieses.showWpts();
    if (what.search("t") != -1 ) dieses.showTracks();
    if (what.search("r") != -1 ) dieses.showRoutes();
  } // show
  this.rescale = function() {
    JB_GM_Info(id,"rescale",false);
    if(load) {
      map.setCenter(new GLatLng((latmax+latmin)/2,(lonmax+lonmin)/2), zoom);
    }
  } // rescale
  this.Entfernung = function() {
    var ls,bs,hs;
    this.init = function(b,l,h) {
      ls = l;
      bs = b;
      hs = h;
    }
    this.rechne = function(b,l,h) {
      var e2 = new GLatLng(bs,ls).distanceFrom(new GLatLng(b,l));
      var dh = h-hs;
      var e = (dh==0)? e2 : Math.sqrt(e2*e2+dh*dh);
      ls = l;
      bs = b;
      hs = h;
      return e/1000;
    }
  } // Entfernung
  this.rauf_runter = function(t) {
    var l=t.length;
    if(l<2) return { rauf:0, runter:0 } ;   
    var hs=t[0].h,he=t[l-1].h;
    var hmin=hs,hmax=hs;
    var h = hs;
    var ext = h;
    var rr = t[1].h>ext?1:-1;
    var rauf = 0;
    var runter = 0;
    var dmin = 10;
    var d;
    for(var i=1;i<l-1;i++) {
      var hm = h;
      h = t[i].h;
      if(h<hmin) hmin = h; if(h>hmax) hmax = h;
      var dh = h - hm;
      if(dh>0 && rr<0) { //  Min durchlaufen
        rr = 0;
        d = - (h - ext);
        if(d > dmin) { //  genügend Gefälle
          runter += d;
          ext = h;
       }
      }
      else if(dh<0 && rr>0) { //  Max durchlaufen
        rr = 0;
        d = h - ext;
        if(d > dmin) { //  genügend Steigung
          rauf += d;
          ext = h;
        }
      }
      else if(rr==0) {
        if(dh>0) rr = 1;
        else if(dh<0) rr = -1;
      }
    } 
    d = he - ext;
    if(d>0) rauf += d;
    else    runter -= d;
    var ramin = (hs<he)?hmax-hmin:hmax-hmin+he-hs;
    var rumin = (he<hs)?hmax-hmin:hmax-hmin+hs-he;
    if(rauf<ramin) rauf = ramin;
    if(runter<rumin) runter = rumin;
    runter += (he-hs) - (rauf-runter) ;
    rauf = Math.round(rauf);
    runter = Math.round(runter);
    JB_GM_Info(id,"Rauf: "+rauf+"   Runter: "+runter,false);
    return { rauf:rauf, runter:runter } ;    
  } // rauf_runter
  this.showtrackmarker = function() {
    JB_GM_Info(id,"showtrackmarker",false);
    movemarker = new GMarker(gpxdaten.tracks.track[0].latlon[0],markericon);
    map.addOverlay(movemarker);
    infobox.style.visibility = "visible";
  } // showtrackmarker
  this.hidetrackmarker = function() {
    map.removeOverlay(movemarker);
    infobox.style.visibility = "hidden";
  } // hidetrackmarker
  this.settrackmarker = function(a) {
    map.removeOverlay(movemarker);
    movemarker = new GMarker(new GLatLng(a.lat,a.lon),markericon);
    map.addOverlay(movemarker);
    var point=map.getCurrentMapType().getProjection().fromLatLngToPixel(map.fromDivPixelToLatLng(new GPoint(0,0),true),map.getZoom());
    var offset=map.getCurrentMapType().getProjection().fromLatLngToPixel(movemarker.getPoint(),map.getZoom());
    var anchor=movemarker.getIcon().iconAnchor;
    var width=movemarker.getIcon().iconSize.width;
    var height=infobox.clientHeight;
    var pos = new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(offset.x - point.x - anchor.x + width, offset.y - point.y -anchor.y -height));
    pos.apply(infobox);
  } //  movetrackmarker
  this.markerstart = function() {
    JB_GM_Info(id,"markerstart",false);
    dieses.showtrackmarker();
    profil.hp.pflag = profil.sp.pflag = gpxdaten.tracks.hflag;
    profil.hpt.pflag = profil.spt.pflag = gpxdaten.tracks.hflag && gpxdaten.tracks.tflag;
    profil.vpt.pflag = profil.vp.pflag = gpxdaten.tracks.tflag;
    profil.wp.pflag = gpxdaten.tracks.tflag;
    for(p in profil) {
      var pr = profil[p];
      if(pr.ele && pr.pflag) pr.diag.showmarker("Linie");
    }
  } // markerstart
  this.markerstop = function() {
    dieses.hidetrackmarker();
    JB_GM_Info(id,"markerstop",false);
    profil.hp.pflag = profil.sp.pflag = gpxdaten.tracks.hflag;
    profil.hpt.pflag = profil.spt.pflag = gpxdaten.tracks.hflag && gpxdaten.tracks.tflag;
    profil.vpt.pflag = profil.vp.pflag = gpxdaten.tracks.tflag;
    profil.wp.pflag = gpxdaten.tracks.tflag;
    for(p in profil) {
      var pr = profil[p];
      if(pr.ele && pr.pflag) pr.diag.hidemarker();
    }
  } // markerstop
  this.markermove = function(p,a) {
    var info = "";
    if(shtrx)                              info += "Strecke:&nbsp;"+a.x.toFixed(1)+"km";
    if(shtrh && typeof a.h != "undefined") info += "<br />Höhe:&nbsp;"+Math.round(a.h)+"m";
    if(shtrv && typeof a.v != "undefined") info += "<br />Geschw.:&nbsp;"+Math.round(a.v)+"km/h";
    if(shtrs && typeof a.s != "undefined") info += "<br />Stg.:&nbsp;"+Math.round(a.s)+"%";
    if(shtrt && typeof a.t != "undefined") info += "<br />Zeit:&nbsp;"+JB_Zeitstring(a.t);
    profil.hp.pflag = profil.sp.pflag = gpxdaten.tracks.hflag;
    profil.hpt.pflag = profil.spt.pflag = gpxdaten.tracks.hflag && gpxdaten.tracks.tflag ;
    profil.vpt.pflag = profil.vp.pflag = gpxdaten.tracks.tflag;
    profil.wp.pflag = gpxdaten.tracks.tflag;
    for(p in profil) {
      var pr = profil[p];
      if(pr.ele && pr.pflag) pr.diag.setmarker(a,"Linie");
    }
    if(info.length) infobox.innerHTML = info;
    else infobox.style.visibility = "hidden";
    dieses.settrackmarker(a);
  } // markermove
  var createImgMarker = function(waypoint) { 
    var marker = new GMarker(new GLatLng(waypoint.lat,waypoint.lon),{icon:makeIcon("scenic")});
    GEvent.addListener(marker, "click", function() {
      var bild = new Image(); 
      bild.onload = function() {
        var text = (shwpcmt?waypoint.cmt:"") + (shwpcmt&&shwpdesc?"<br />":"") + (shwpdesc?waypoint.desc:"");
        if(shwptime && waypoint.time>0) text += "<br /><span style='white-space:nowrap;'>("
                                              + JB_sec2string(waypoint.time,tdiff) +")</span>"; 
        marker.openInfoWindowHtml("<img src='"+waypoint.name+"'\/><br\/>"+text);
      };
      bild.src = waypoint.name;
    });
    GEvent.addListener(marker, "mouseout", function() { 
	    infobox.innerHTML = ""; 
		  infobox.style.visibility = "hidden";
	  });
    GEvent.addListener(marker, "mouseover", function() {
      var bild = new Image(); 
      bild.onload = function() {
        infobox.innerHTML = "<img width=50 src='"+waypoint.name+"'\/>";
        infobox.style.visibility = "visible";
        var point=map.getCurrentMapType().getProjection().fromLatLngToPixel(map.fromDivPixelToLatLng(new GPoint(0,0),true),map.getZoom());
        var offset=map.getCurrentMapType().getProjection().fromLatLngToPixel(marker.getPoint(),map.getZoom());
        var anchor=marker.getIcon().iconAnchor;
        var width=marker.getIcon().iconSize.width;
        var height=infobox.clientHeight;
        var pos = new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(offset.x - point.x - anchor.x + width, offset.y - point.y -anchor.y -height));
        pos.apply(infobox);
      };
      bild.src = waypoint.name;
    });
    return marker;
  } // createImgMarker
  var createTxtMarker = function(waypoint) {
    var marker = new GMarker(new GLatLng(waypoint.lat,waypoint.lon),{title:waypoint.name,icon:makeIcon(waypoint.sym)});
    GEvent.addListener(marker, "click", function() {
      var text = (shwpcmt?waypoint.cmt:"") + (shwpcmt&&shwpdesc?"<br />":"") + (shwpdesc?waypoint.desc:"");
      if(shwptime && waypoint.time>0) text += "<br /><span style='white-space:nowrap;'>("
                                            + JB_sec2string(waypoint.time,tdiff) +")</span>"; 
      marker.openInfoWindowHtml("<strong>"+waypoint.name+"<\/strong><br\/>"+text);
    });
    return marker;
  } // createTxtMarker
  var createLnkMarker = function(waypoint) {
    var marker = new GMarker(new GLatLng(waypoint.lat,waypoint.lon),{title:waypoint.name,icon:makeIcon(waypoint.sym)});
    if(waypoint.link.search("~")==0){ 
      GEvent.addListener(marker, "click", function() {
        window.location.href = waypoint.link.substr(1);
      });
    }
    else {
      GEvent.addListener(marker, "click", function() {
        window.open(waypoint.link,"",popup_Pars);
      });
    }
    return marker;
  } // createLnkMarker
  var checkImageName = function(url) {
    var ext = url.substr(url.lastIndexOf(".")+1).toLowerCase();
    return (ext=="jpg" || ext=="jpeg" || ext=="png" || ext=="gif") ;
  } //  checkImageName                 
  this.makePolyline = function(map,gpxdaten,width,opac,widtho,colo,info) {                     
  JB_GM_Info(id,"makePolyline, "+gpxdaten.latlon.length+" Punkte",false);
  if(gpxdaten.latlon.length<2) return;
    // var PL = new GPolyline(gpxdaten.latlon,gpxdaten.farbe,width,opac);
    var PL = polylineEncoder.dpEncodeToGPolyline(gpxdaten.latlon,gpxdaten.farbe,width,opac);
    if(trackover) {
      GEvent.addListener(PL,"mouseover",function() {
        JB_GM_Info("","Line over, "+this.getVertexCount()+" Punkte",false);
        this.setStrokeStyle({color:colo,weight:widtho});
        infobox.innerHTML = info;
        var bounds = map.getBounds();
        var latmax = bounds.getNorthEast().lat();
        var lngmin = bounds.getSouthWest().lng();
        var lo = map.getCurrentMapType().getProjection().fromLatLngToPixel(new GLatLng(latmax,lngmin),map.getZoom()); 
        var of = map.getCurrentMapType().getProjection().fromLatLngToPixel(map.fromDivPixelToLatLng(new GPoint(0,0),true),map.getZoom());
        var leftoff = largemapcontrol?70:50;
        var pos = new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(leftoff+lo.x-of.x, 10+lo.y-of.y)); 
        pos.apply(infobox);
        infobox.style.visibility = "visible";
      });
      GEvent.addListener(PL,"mouseout", function() {
        JB_GM_Info("","Line out",false);
        this.setStrokeStyle({color:gpxdaten.farbe,weight:width}); 
        infobox.innerHTML = "";
        infobox.style.visibility = "hidden";
      });
      GEvent.addListener(PL,"click", function(overlaylatlng) {
        JB_GM_Info("","Line click "+overlaylatlng+"  ",false);
        if(gpxdaten.link) {
          if(gpxdaten.link.search("~")==0)
            window.location.href = gpxdaten.link.substr(1);
          else
            window.open(gpxdaten.link,"",popup_Pars);
          }
        else
          map.openInfoWindow(overlaylatlng,info);
      });
    }
    map.addOverlay(PL);
  } // makePolyline
} // makeMap

function JB_GM_Info(id,Infotext,alertflag) {
  if(GPXVIEW_Debuginfo) {
    var dt = ((new Date()).getTime()-GPXVIEW_Start).toString(10);
    while(dt.length<6) dt = "0"+dt;
    GLog.write(dt+" Map "+id+": "+Infotext);
  }
  if(alertflag) alert(Infotext);
} // GM_Info

function JB_GetElementsByTagNameNS(ele,namespace,name) {
  var alltags = ele.getElementsByTagName("*");
  var tagname = namespace.toLowerCase()+":"+name.toLowerCase()
  var tags = new Array();
  for(var i=0;i<alltags.length;i++) if(alltags[i].nodeName.toLowerCase()==tagname) tags.push(alltags[i]);
  return tags;
} // JB_GetElementsByTagNameNS(ele,namespace,name)

function JB_getTag(ele,tagname,defval,child) {
  var tag = ele.getElementsByTagName(tagname), val=defval, tag0;
  if( tag && tag.length ) {
    tag0 = tag[0];
    if( tag0.firstChild && tag0.firstChild.length && (child?(tag0.parentNode==ele):true) )
      val = tag0.firstChild.data;
  }
  return val;
} // JB_getTag

function JB_getTagNS(ele,namespace,tagname,defval,child) {
  var tag = JB_GetElementsByTagNameNS(ele,namespace,tagname), val=defval, tag0;
  if( tag && tag.length) {
    tag0 = tag[0];
    if( tag0.firstChild && tag0.firstChild.length && (child?(tag0.parentNode==ele):true) )
      val = tag0.firstChild.data;
  }
  return val;
} // JB_getTagNS

function JB_CheckBoxGroup(id,Texte,Label,Farbe,def_stat,clickFunc) {
  var dieses = this;
  var nbx = Texte.length;
  this.nboxen = nbx;
  this.status = new Array(nbx); for(var i=0;i<nbx;i++) this.status[i] = def_stat ;
  var ele;
  var box=document.createElement("div");
  box.style.position = "absolute";
  box.style.display = "inline";
  box.style.height = "1.4em";
  box.style.overflow = "hidden";
  box.style.backgroundColor = "";
  box.style.zIndex = 1000;
  box.style.margin = "0";
  box.style.padding = "0";
  box.onmouseover = function() {
    this.style.height = "";
    this.style.overflow = "";
    this.style.backgroundColor = "white";
    this.style.paddingRight = "0.3em";
    this.style.paddingBottom = "0.2em";
  };
  box.onmouseout  = function() {
    this.style.height = "1.4em";
    this.style.overflow = "hidden";
    this.style.backgroundColor = "";
    this.style.paddingRight = "";
    this.style.paddingBottom = "";
  };
  for(var i=0;i<nbx;i++) {
    ele = document.createElement("input");
    ele.type = "checkbox";
    ele.id = Label + i;
    ele.nr = i;
    if(i==0) ele.onclick = function() {
      var l = nbx;
      var n = Label;
      var status = this.checked;
      dieses.status[this.nr] = status;
      for(var j=1;j<l;j++) {
        document.getElementById(n+j).checked=status;
        dieses.status[j] = status;
      }
      clickFunc(dieses,this);
    };
    else     ele.onclick = function() {
      var l = nbx;
      var n = Label;
      var status = false;
      for(var j=1;j<l;j++) status |= document.getElementById(n+j).checked;
      document.getElementById(n+"0").checked = status;
      dieses.status[0] = status==true;
      dieses.status[this.nr] = this.checked;
      clickFunc(dieses,this);
    };
    box.appendChild(ele);
    ele.checked = def_stat;
    ele=document.createElement("span");
    if(i==0 && nbx==1) ele.style.color=Farbe[0];
    else if(i) ele.style.color=Farbe[(i-1)%Farbe.length];
    ele.appendChild(document.createTextNode(Texte[i]));
    box.appendChild(ele);
    if(i<Texte.length-1) box.appendChild(document.createElement("br"));
  }
  ele=document.getElementById(id);
  ele.appendChild(box);
  var spn=document.createElement("span"); // Platzhalter
  spn.appendChild(document.createTextNode("xX"+Texte[0]+"x"));
  spn.style.visibility="hidden";
  ele.appendChild(spn);
} // JB_CheckBoxGroup

function JB_addEvent (obj, type, fn) {
   if (obj.addEventListener) {
      obj.addEventListener(type, fn, false);
   } else if (obj.attachEvent) {
      obj.attachEvent('on' + type, function () {
         return fn.call(obj, window.event);
      });
   }
} // JB_addEvent

function JB_utc2sec(utcdate) {
  var jahr = utcdate.substr(0,4);
  var monat = utcdate.substr(5,2)*1-1;
  var tag = utcdate.substr(8,2);
  var stunde = utcdate.substr(11,2);
  var minute = utcdate.substr(14,2);
  var sekunde = utcdate.substr(17,2);
  return Date.UTC(jahr,monat,tag,stunde,minute,sekunde)/1000;
} // utc2sec

function JB_sec2string(sec,off) {
  var d = new Date(sec*1000 + off*3600000);
  return d.getDate()+". "+(d.getMonth()+1)+". "+d.getFullYear()+", "+d.getHours()+":"+(d.getMinutes()<10?"0":"")+d.getMinutes(); 
} // sec2string

function JB_Zeitstring(sekunden) {
  var h=0,m=0,s=sekunden;
  m = Math.floor(s/60);
  s = s%60; if(s<10) s = "0"+s;
  h = Math.floor(m/60)
  m = m%60; if(m<10) m = "0"+m;
  return h+":"+m+":"+s+"h"; 
}

function JB_smooth(a,x,y,ys,range) {   
  var l=a.length;
  var t = new Array(l); for(var i=0;i<l;i++) { t[i] = {}; t[i][ys]=a[i][y]; for(var o in a[i]) t[i][o] = a[i][o]; }
  var x0 = a[0][x];
  var xl = a[l-1][x];
  var fak,faksum,sum,xi,xmin,xmax,xj,i,j;
  range /= 2000;
  if(range>(xl-x0)/4 || range==0) return t;
  for(i=0;i<l;i++) {
    xi = a[i][x];
    xmin = xi - range;
    xmax = xi + range;
    sum = a[i][y] * range;
    faksum = range;
    j = i - 1;
    if(j>=0) {
      xj = a[j][x];
      while(xj>xmin) {
        fak = range - xi + xj;
        sum += a[j][y]*fak;
        faksum += fak;
        j--;
        if(j<0) break;
        xj = a[j][x];
      }
    }
    j = i + 1;
    if(j<l) {
      xj = a[j][x];
      while(xj<xmax) {
        fak = range + xi - xj;
        sum += a[j][y]*fak;
        faksum += fak;
        j++;
        if(j>=l) break;
        xj = a[j][x];
      }
    }
    t[i][ys] = sum/faksum;
  }
  return t;
} // smooth

function JB_diff(a,x,y,d,fak) {
  var l=a.length,l1=l-1;
  if(l<3) { for(var i=0;i<l;i++) a[i][d] = 0; return a; }
  var dx,dy;
  dx = a[1][x]-a[0][x];
  dy = a[1][y]-a[0][y];
  if(dx==0) a[0][d] = 0;
  else      a[0][d] = fak*dy/dx;
  for(var i=1;i<l1;i++) {
    dx = a[i+1][x]-a[i-1][x];
    dy = a[i+1][y]-a[i-1][y];
    if(dx==0) a[i][d] = a[i-1][d];
    else      a[i][d] = fak*dy/dx;
  }
  dx = a[l1-1][x]-a[l1][x];
  dy = a[l1-1][y]-a[l1][y] ;
  if(dx==0) a[l1][d] = a[l1-1][d];
  else      a[l1][d] = fak*dy/dx;
  return a;
} // diff

// PolylineEncoder.js copyright Mark McClure  April/May 2007
// V 2.1  July 2007
// http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/PolylineEncoderClass.html
// Geringfügig angepasst von JB
var PolylineEncoder = function() {
  this.numLevels = 18;
  this.zoomFactor = 2;
  this.verySmall = 0.000005;
  this.forceEndpoints = true;
  this.zoomLevelBreaks = new Array(this.numLevels);
  for(var i = 0; i < this.numLevels; i++) {
    this.zoomLevelBreaks[i] = this.verySmall*Math.pow(this.zoomFactor, this.numLevels-i-1);
  }
} 
PolylineEncoder.prototype.dpEncode = function(points) {
  var absMaxDist = 0;
  var stack = [];
  var dists = new Array(points.length);
  var maxDist, maxLoc, temp, first, last, current;
  var i, encodedPoints, encodedLevels;
  var segmentLength;
  if(points.length > 2) {
    stack.push([0, points.length-1]);
    while(stack.length > 0) {
      current = stack.pop();
      maxDist = 0;
      segmentLength = Math.pow(points[current[1]].lat()-points[current[0]].lat(),2) +
        Math.pow(points[current[1]].lng()-points[current[0]].lng(),2);
      for(i = current[0]+1; i < current[1]; i++) {
        temp = this.distance(points[i],
          points[current[0]], points[current[1]],
          segmentLength);
        if(temp > maxDist) {
          maxDist = temp;
          maxLoc = i;
          if(maxDist > absMaxDist) {
            absMaxDist = maxDist;
          }
        }
      }
      if(maxDist > this.verySmall) {
        dists[maxLoc] = maxDist;
        stack.push([current[0], maxLoc]);
        stack.push([maxLoc, current[1]]);
      }
    }
  }
  encodedPoints = this.createEncodings(points, dists);
  encodedLevels = this.encodeLevels(points, dists, absMaxDist);
  return {
    encodedPoints: encodedPoints,
    encodedLevels: encodedLevels,
    encodedPointsLiteral: encodedPoints.replace(/\\/g,"\\\\")
  }
}
PolylineEncoder.prototype.dpEncodeToJSON = function(points,
  color, weight, opacity) {
  var result;
  if(!opacity) {
    opacity = 0.9;
  }
  if(!weight) {
    weight = 3;
  }
  if(!color) {
    color = "#0000ff";
  }
  result = this.dpEncode(points);
  return {
    color: color,
    weight: weight,
    opacity: opacity,
    points: result.encodedPoints,
    levels: result.encodedLevels,
    numLevels: this.numLevels,
    zoomFactor: this.zoomFactor
  }
}
PolylineEncoder.prototype.dpEncodeToGPolyline = function(points,
  color, weight, opacity) {
  if(!opacity) {
    opacity = 0.9;
  }
  if(!weight) {
    weight = 3;
  }
  if(!color) {
    color = "#0000ff";
  }
  return new GPolyline.fromEncoded(
    this.dpEncodeToJSON(points, color, weight, opacity));
}
PolylineEncoder.prototype.distance = function(p0, p1, p2, segLength) {
  var u, out;
  if(p1.lat() === p2.lat() && p1.lng() === p2.lng()) {
    out = Math.sqrt(Math.pow(p2.lat()-p0.lat(),2) + Math.pow(p2.lng()-p0.lng(),2));
  }
  else {
    u = ((p0.lat()-p1.lat())*(p2.lat()-p1.lat())+(p0.lng()-p1.lng())*(p2.lng()-p1.lng()))/
      segLength;

    if(u <= 0) {
      out = Math.sqrt(Math.pow(p0.lat() - p1.lat(),2) + Math.pow(p0.lng() - p1.lng(),2));
    }
    if(u >= 1) {
      out = Math.sqrt(Math.pow(p0.lat() - p2.lat(),2) + Math.pow(p0.lng() - p2.lng(),2));
    }
    if(0 < u && u < 1) {
      out = Math.sqrt(Math.pow(p0.lat()-p1.lat()-u*(p2.lat()-p1.lat()),2) +
        Math.pow(p0.lng()-p1.lng()-u*(p2.lng()-p1.lng()),2));
    }
  }
  return out;
}
PolylineEncoder.prototype.createEncodings = function(points, dists) {
  var i, dlat, dlng;
  var plat = 0;
  var plng = 0;
  var encoded_points = "";
  for(i = 0; i < points.length; i++) {
    if(dists[i] != undefined || i == 0 || i == points.length-1) {
      var point = points[i];
      var lat = point.lat();
      var lng = point.lng();
      var late5 = Math.floor(lat * 1e5);
      var lnge5 = Math.floor(lng * 1e5);
      dlat = late5 - plat;
      dlng = lnge5 - plng;
      plat = late5;
      plng = lnge5;
      encoded_points += this.encodeSignedNumber(dlat) +
        this.encodeSignedNumber(dlng);
    }
  }
  return encoded_points;
}
PolylineEncoder.prototype.computeLevel = function(dd) {
  var lev=0;
  if(dd > this.verySmall) {
    while(dd < this.zoomLevelBreaks[lev]) {
      lev++;
    }
  }
  return lev;
}
PolylineEncoder.prototype.encodeLevels = function(points, dists, absMaxDist) {
  var i;
  var encoded_levels = "";
  if(this.forceEndpoints) {
    encoded_levels += this.encodeNumber(this.numLevels-1)
  } else {
    encoded_levels += this.encodeNumber(
      this.numLevels-this.computeLevel(absMaxDist)-1)
  }
  for(i=1; i < points.length-1; i++) {
    if(dists[i] != undefined) {
      encoded_levels += this.encodeNumber(
        this.numLevels-this.computeLevel(dists[i])-1);
    }
  }
  if(this.forceEndpoints) {
    encoded_levels += this.encodeNumber(this.numLevels-1)
  } else {
    encoded_levels += this.encodeNumber(
      this.numLevels-this.computeLevel(absMaxDist)-1)
  }
  return encoded_levels;
}
PolylineEncoder.prototype.encodeNumber = function(num) {
  var encodeString = "";
  var nextValue, finalValue;
  while (num >= 0x20) {
    nextValue = (0x20 | (num & 0x1f)) + 63;
    encodeString += (String.fromCharCode(nextValue));
    num >>= 5;
  }
  finalValue = num + 63;
  encodeString += (String.fromCharCode(finalValue));
  return encodeString;
}
PolylineEncoder.prototype.encodeSignedNumber = function(num) {
  var sgn_num = num << 1;
  if (num < 0) {
    sgn_num = ~(sgn_num);
  }
  return(this.encodeNumber(sgn_num));
}
PolylineEncoder.latLng = function(y, x) {
        this.y = y;
        this.x = x;
}
PolylineEncoder.latLng.prototype.lat = function() {
        return this.y;
}
PolylineEncoder.latLng.prototype.lng = function() {
        return this.x;
}
PolylineEncoder.pointsToLatLngs = function(points) {
        var i, latLngs;
        latLngs = new Array(0);
        for(i=0; i<points.length; i++) {
                latLngs.push(new PolylineEncoder.latLng(points[i][0], points[i][1]));
        }
        return latLngs;
}
PolylineEncoder.pointsToGLatLngs = function(points) {
        var i, gLatLngs;
        gLatLngs = new Array(0);
        for(i=0; i<points.length; i++) {
                gLatLngs.push(new GLatLng(points[i][0], points[i][1]));
        }
        return gLatLngs;
}  //  PolylineEncoder

JB_addEvent(window,"load",function() {
  if(document.getElementsByTagName && GBrowserIsCompatible()) {
    var Map_Nr=0;
    var chkTyp = function(typString) {
      if(typString=="Karte") return G_NORMAL_MAP ;
      if(typString=="Satellit") return G_SATELLITE_MAP ;
      if(typString=="Hybrid") return G_HYBRID_MAP ;
      if(typString=="Oberflaeche") return G_PHYSICAL_MAP ;
      if(typString=="OSM" || typString=="OSM_Mapnik") return "osm_mapnik_map";
      if(typString=="OSM_TaH" ) return "osm_tah_map";
      if(typString=="OSM_Cycle" ) return "osm_cycle_map";
      return G_SATELLITE_MAP;
    } // chkTyp
    var divs = document.getElementsByTagName("div");
    var typ = G_SATELLITE_MAP;
    var maps=[];
    for(var i=0;i<divs.length;i++) {
      var div = divs[i];
      if(div.className) {
        var Klasse = div.className;
        var CN = Klasse.search(/(^|\s)gpxview/i);
        if(CN>-1) {
          if(div.id) var Id = div.id;
          else {
            var Id = "map"+(Map_Nr++);
            div.id = Id;
          }
          var GPX = Klasse.substring(CN).split()[0];
          GPX = GPX.split(":") ;
          if(GPX.length==3) {
            typ = chkTyp(GPX[2]);
          }
          if(GPX[1].length) {
            maps["Karte_"+Id] = div.makeMap = new makeMap(Id);
            maps["Karte_"+Id].Spur(GPX[1],typ);
          }
        }
      }
    }
    var buttons = document.getElementsByTagName("button");
    for(var i=0;i<buttons.length;i++) {
      var button = buttons[i];
      if(button.className) {
        var Klasse = button.className;
        var CN = Klasse.search(/(^|\s)gpxview/i);
        if(CN>-1) {
          var cmd = Klasse.substring(CN).split()[0];
          cmd = cmd.split(":") ;
          if(cmd.length>2) {
            var Id = cmd[1];
            switch(cmd[2]) {
              case "skaliere":
                ( function() {
                  var mapid = "Karte_"+Id;
                  button.onclick = function(){maps[mapid].rescale()};
                } )();
                break;
              case "lade":
                if(cmd.length>3) {
                  if(cmd.length>4) typ = chkTyp(cmd[4]);
                  else typ = undefined; //G_SATELLITE_MAP;
                  ( function() {
                    var fn = cmd[3];
                    var mapid = "Karte_"+Id;
                    var tp = typ;
                    button.onclick = function(){maps[mapid].Spur(fn,tp)};
                  } )();
                }
                break;
              default:
                break;
            }
          }
        }
      }
    }
    JB_addEvent(window,"unload",GUnload);
  }
  else  JB_GM_Info("","Ihr Browser unterstützt nicht die benötigten Methoden!",true);
}); // addEvent("load")

})(); // anonyme sofort ausgeführte Funktion

