Webkarte mit GPS-Track vom Sport


Zur zeit erfreuen sich Smartphone-Anwendungen zur Aufzeichnung von sportlichen Aktivitäten mittels GPS größter Beliebtheit. Diese tracken die zurückgelegte Strecke inklusive Höhenangaben und Laufzeit zum Besipiel beim Joggen oder Fahrradfahren. Bei vielen dieser Apps können nach der Sportaktivität statistische Auswertungen wie Höhenprofile oder Durchschnittsgeschwindigkeit abgerufen werden. Auch eine Visualisierung der zurückgelegten Strecke auf einer Karte wird oftmals angeboten. ABER: In den seltesten Fällen kann auf die Positionsdaten selbst zugegriffen werden, um eigene Auswertungen durchzuführen, wie zum Beispiel die GPS-Tracks auf eine eigene Webkarte zu bringen. Und dies ohne sich zu bei einem Anbieter zu registrieren und seine Daten aus der Hand zu geben.

Im folgenden Blogpost wird beschrieben wie GPS-Tracks einer Skitour mit dem Open Source Webmapping-Framework LeafletJS auf eine Webkarte gebracht werden können. Vorweg hier das Ergebnis:

Vollbild-Version anzeigen
Nun aber Schritt für Schritt:

Schritt 1: Aufzeichnung der Daten

Zunächst muss während der sportlichen Aktivität eine GPS-Aufzeichnung laufen. Dies kann mit einem speziellen GPS-Handheld oder mit dem Smartphone und einer App geschehen. Für Android gibt es dafür zum Beispiel den Open GPS Tracker. Dieser trackt die eigene Bewegung, zeigt diese dabei auf einer Karte an, bietet Zugriff auf aktuelle Statistiken und ermöglich das Versenden der GPS-Daten im GPX– oder KML-Format. Wer möchte kann sogar Notizen, Fotos oder Sprachaufzeichnungen an die eigene Position hängen.

Schritt 2: Raus in die Berge, in den Wald, auf den Sportplatz oder auf die Straße!

winter-mountain

Schritt 3: Basiskarte erzeugen

Nachdem nun (hoffentlich) Bewegungsdaten vorhanden sind, kann die Karte erstellt werden: Für interaktive Webkarten gibt es mittlerweile eine Fülle von sehr guten Open Source Frameworks (OpenLayers 2, OpenLayers 3, LeafletJS, etc…), welche alle problemlos für solche Zwecke eingesetzt werden können. Hier kommt LeafletJS zum Einsatz, um die Basiskarte zu erzeugen:

1
2
3
4
5
6
7
var map = new L.Map('map', {
  crs: L.CRS.EPSG900913,
  continuousWorld: true,
  worldCopyJump: false,
  center: [47.128257, 10.223236],
  zoom: 13
});
var map = new L.Map('map', {
  crs: L.CRS.EPSG900913,
  continuousWorld: true,
  worldCopyJump: false,
  center: [47.128257, 10.223236],
  zoom: 13
});

Um einen guten Eindruck zu erhalten, wo man selbst gejoggt, gefahren, gerudert oder geskatet ist, braucht es eine gute und schnelle Hintergrundkarte. Hierzu kann beispielsweise der neue freie OpenStreetMap-WMS von terrestris eingesetzt werden:

1
2
3
4
5
6
7
var osmWms = L.tileLayer.wms("http://ows.terrestris.de/osm/service", {
  layers: 'OSM-WMS',
  format: 'image/png',
  transparent: true,
  attribution: '© terrestris GmbH &amp; Co. KG, Data © OpenStreetMap <a href="http://www.openstreetmap.org/copyright">contributors</a>'
});
osmWms.addTo(map);
var osmWms = L.tileLayer.wms("http://ows.terrestris.de/osm/service", {
  layers: 'OSM-WMS',
  format: 'image/png',
  transparent: true,
  attribution: '© terrestris GmbH &amp; Co. KG, Data © OpenStreetMap <a href="http://www.openstreetmap.org/copyright">contributors</a>'
});
osmWms.addTo(map);

Da es sich bei den GPS-Tracks um eine Skitour handelt, macht sich ein Schummerungslayer zur Veranschaulichung des Geländes immer gut. Auch hier gibt es freie Alternativen, im gezeigten Beispiel wird der Hillshade-WMS des Projektes www.osm-wms.org eingesetzt und mit hoher Transparenz über den Hintergrundlayer gelegt:

1
2
3
4
5
6
7
8
9
var osmWmsWorldHillshade = L.tileLayer.wms("http://129.206.228.72/cached/hillshade", {
  layers: 'europe_wms:hs_srtm_europa',
  format: 'image/png',
  transparent: true,
  crs: L.CRS.EPSG900913,
  opacity: 0.1,
  attribution: '&amp;copy; www.osm-wms.de, Data © OpenStreetMap &lt;a href="http://www.openstreetmap.org/copyright"&gt;contributors&lt;/a&gt;'
});
osmWmsWorldHillshade.addTo(map);
var osmWmsWorldHillshade = L.tileLayer.wms("http://129.206.228.72/cached/hillshade", {
  layers: 'europe_wms:hs_srtm_europa',
  format: 'image/png',
  transparent: true,
  crs: L.CRS.EPSG900913,
  opacity: 0.1,
  attribution: '&amp;copy; www.osm-wms.de, Data © OpenStreetMap &lt;a href="http://www.openstreetmap.org/copyright"&gt;contributors&lt;/a&gt;'
});
osmWmsWorldHillshade.addTo(map);

Schritt 4: GPS-Daten auf die Karte bringen

Nun zum eigentlichen Part, die Visualisierung der GPS-Daten auf der Hintergrundkarte. LeafletJS bringt selbst keinen Parser oder einen Layer-Typ zur direkten Visualisierung von für GPX-Daten mit. Dafür gibt es aber einige Plugins. Leaflet GPX von Maxime Petazzoni ist genau auf die Visualisierung von GPS-Tracks sportlicher Aktivitäten ausgelegt:

GPX layer, targeted at sporting activities by providing access to information such as distance, moving time, pace, elevation, heart rate, etc.

und glänzt durch Einfachheit und einer gut dokumentierten API. Dadurch kann mit wenigen Zeilen Code ein GPX-Track auf eine Leaflet-Karte gebracht werden:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var gpxTrack = new L.GPX(
  pathGpxTrack, 
  {
      async: true,
      // styling of the GPX track on the map
      polyline_options: {
          color: '#aa1111'
      },
      // icons for the marker
      marker_options: {
          startIconUrl: '../libs/leaflet-gpx-master/pin-icon-start.png',
          endIconUrl: '../libs/leaflet-gpx-master/pin-icon-end.png',
          shadowUrl: '../libs/leaflet-gpx-master/pin-shadow.png'
      }
  }
);
// add the gpx layer to the map
gpxTrack.addTo(map);
var gpxTrack = new L.GPX(
  pathGpxTrack, 
  {
      async: true,
      // styling of the GPX track on the map
      polyline_options: {
          color: '#aa1111'
      },
      // icons for the marker
      marker_options: {
          startIconUrl: '../libs/leaflet-gpx-master/pin-icon-start.png',
          endIconUrl: '../libs/leaflet-gpx-master/pin-icon-end.png',
          shadowUrl: '../libs/leaflet-gpx-master/pin-shadow.png'
      }
  }
);
// add the gpx layer to the map
gpxTrack.addTo(map);

Schritt 5: Zusätzliche Informationen in einem Popup darstellen

Neben der Anzeige auf der Karte bringt das Leaflet GPX Plugin auch Zugriffsmöglichkeiten auf statistische Werte der GPX-Tracks mit sich. Diese machen wir uns zu Nutze und zeigen diese bei einem Klick auf den Track in einem Popup an:

winter-mountain

Dazu muss gewartet werden bis der  „loaded“-Event geworfen wird, um sichzustellen, dass die GPX-Daten vollständig geladen sind. Anschließend kann im Eventhandler auf die zusätzlichen Informationen des GPX-Objektes (e.target) mittels Funktionen zugegriffen werden:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
gpxTrack.on('loaded', function(e) {
  var gpx = e.target,
    distM = gpx.get_distance(),
    distKm = distM / 1000,
    distKmRnd = distKm.toFixed(1),
    eleGain = gpx.get_elevation_gain().toFixed(3),
    eleLoss = gpx.get_elevation_loss().toFixed(3);
 
  // register popup on click
  gpx.getLayers()[0].bindPopup(
    "Distance " + distKmRnd + " km </br>" +
    "Elevation Gain " + eleGain + " m </br>" +
    "Elevation Loss " + eleLoss + " m"
  )
});
gpxTrack.on('loaded', function(e) {
  var gpx = e.target,
    distM = gpx.get_distance(),
    distKm = distM / 1000,
    distKmRnd = distKm.toFixed(1),
    eleGain = gpx.get_elevation_gain().toFixed(3),
    eleLoss = gpx.get_elevation_loss().toFixed(3);

  // register popup on click
  gpx.getLayers()[0].bindPopup(
    "Distance " + distKmRnd + " km </br>" +
    "Elevation Gain " + eleGain + " m </br>" +
    "Elevation Loss " + eleLoss + " m"
  )
});

Schritt 6: Zentrieren der Karte

Das GPX-Objekt bietet neben den statistischen Werten auch Zugriff auf die BBOX des Tracks, sodass im „loaded“-Event-Handler die Karte direkt auf den GPX-Track zentriert werden kann:

1
map.fitBounds(gpx.getBounds());
map.fitBounds(gpx.getBounds());

Somit ergibt sich folgender Eventhandler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// register popups on click
// set initial zoom
gpxTrack.on('loaded', function(e) {
  var gpx = e.target,
      distM = gpx.get_distance(),
      distKm = distM / 1000,
      distKmRnd = distKm.toFixed(1),
      eleGain = gpx.get_elevation_gain().toFixed(3),
      eleLoss = gpx.get_elevation_loss().toFixed(3);
 
  gpx.getLayers()[0].bindPopup(
      "Distance " + distKmRnd + " km </br>" +
      "Elevation Gain " + eleGain + " m </br>" +
      "Elevation Loss " + eleLoss + " m"
  );
  // zoom to GPX track
  map.fitBounds(gpx.getBounds());
});
// register popups on click
// set initial zoom
gpxTrack.on('loaded', function(e) {
  var gpx = e.target,
      distM = gpx.get_distance(),
      distKm = distM / 1000,
      distKmRnd = distKm.toFixed(1),
      eleGain = gpx.get_elevation_gain().toFixed(3),
      eleLoss = gpx.get_elevation_loss().toFixed(3);

  gpx.getLayers()[0].bindPopup(
      "Distance " + distKmRnd + " km </br>" +
      "Elevation Gain " + eleGain + " m </br>" +
      "Elevation Loss " + eleLoss + " m"
  );
  // zoom to GPX track
  map.fitBounds(gpx.getBounds());
});

Gar nicht so aufwändig und schwer, oder? Wer nun Blut geleckt hat und seine eigenen Aktivitäten auf eine Karte ins Netz bringen möchte, kann die lauffähige HTML-Datei hier herunterladen und als Startpunkt für die eigene Karte nutzen. Viel Spaß damit wünscht meggsimum.de…