Mapillary-Overlay für Garmin-Geräte

Wenn ich mit der Kamera am Fahrrad unterwegs bin, um Bildsequenzen für Mapillary aufzunehmen, ist es oft hilfreich zu wissen, für welche Straßenabschnitte schon aktuelle Bilder auf Mapillary vorhanden sind. Ich kann dann die Fahrtstrecke so anpassen, dass ich bevorzugt Straßen und Wege abfahre, für die noch keine aktuellen Aufnahmen existieren. Dafür nutze ich eine Overlay-Karte auf meinem Garmin Etrex 30s, auf der alle Mapillary-Sequenzen als grüne Linie angezeigt werden, die ab einem bestimmten Datum erzeugt wurden. Abgesehen davon, dass dabei die Aufnahmewinkel nicht angezeigt werden, hat sich dieses Overlay in der Praxis gut bewährt. Mit QMapShack kann ich das Overlay auch für die Planung am PC nutzen. Nachfolgend beschreibe ich, wie ich das Overlay erstelle.

Die Karte wird für ein geografisches Rechteck (Bounding Box, BBox) erzeugt, das durch die Koordinaten (in Dezimalgrad) der linken unteren und der rechten oberen Ecke definiert ist. Verwendet werden Kommandozeilen-Tools unter Linux/Bash. Die Karte wird in drei Schritten erstellt:

  • Herunterladen der Bildsequenzen von mapillary.com
  • Erzeugen einer OSM-XML-Datei
  • Erstellen der Garmin-Karte

Herunterladen der Bildsequenzen

Seit der Umstellung auf die API-Version V4 können die Daten der Bildsequenzen als Vektorkacheln im Mapbox Vector Tile Format herunter geladen werden. Die hier verwendeten Coverage Tiles enthalten eine Ebene image mit den einzelnen Bildpunkten und deren Daten und eine Ebene sequence mit den Bildsequenzen als Polygonzüge. Diese Ebene wird im Folgenden verwendet, um die Sequenzen als Linien über einer Grundkarte darzustellen.

Die Kacheln sind für die Zoomstufen 6 bis 14 verfügbar, aber nur in der Stufe 14 sind die exakten Positionen aller Fotos enthalten. Die niedrigeren Zoomstufen enthalten eine vereinfachte Repräsentation der Sequenz. Das bedeutet zwar weniger Präzision aber auch weniger Downloadvolumen und weniger Speicher für die Garmin-Karte. Die folgende Anleitung bezieht sich auf Zoomstufe 12, funktioniert aber analog auch für andere Stufen.

Für den Zugriff auf das API wird ein Access Token benötigt, das man sich in den Mapillary-Account-Einstellungen unter “Developers” einrichten kann.

Die Kacheln werden über die üblichen XYZ-Kachelkoordinaten adressiert. Zunächst werden daher aus den BBox-Koordinaten die minimalen und maximalen x- und y-Werte der Kacheln des Bereichs ermittelt. Für die Umrechnung kann man, angelehnt an diese Seite im OSM-Wiki, diese beiden Bash-Funktionen verwenden:

long2xtile(){ 
 long=$1
 zoom=$2
 echo -n "${long} ${zoom}" | awk '{ xtile = ($1 + 180.0) / 360 * 2.0^$2; 
  printf("%d", xtile ) }'
}

lat2ytile() { 
 lat=$1;
 zoom=$2;
 ytile=`echo "${lat} ${zoom}" | awk -v PI=3.14159265358979323846 '{ 
   tan_x=sin($1 * PI / 180.0)/cos($1 * PI / 180.0);
   ytile = (1 - log(tan_x + 1/cos($1 * PI/ 180))/PI)/2 * 2.0^$2; 
   printf("%d", ytile ) }'`;
 echo -n "${ytile}";
}

Für die BBox (9.54, 48.2, 10.42, 48,7) in Zoomstufe 12 ergibt sich damit:

user$ XMIN=$(long2xtile 9.54 12)
user$ XMAX=$(long2xtile 10.43 12)
user$ YMIN=$(lat2ytile 48.7 12)
user$ YMAX=$(lat2ytile 48.2 12)
user$ echo $XMIN $YMIN $XMAX $YMAX
2156 1411 2166 1420

Die einzelnen Kacheln können nun mit curl heruntergeladen werden. Das Format des URL einer Kachel ist:

https://tiles.mapillary.com/maps/vtp/mly1_public/2/{z}/{x}/{y}?access_token=XXX

Damit ergibt sich diese Schleife:

Z=12
for (( X=$XMIN; X<=$XMAX; X++ ));do  
   for (( Y=$YMIN; Y<=$YMAX; Y++ ));do
      MVTFILE=sequence_${X}_${Y}.mvt  
      URL="https://tiles.mapillary.com/maps/vtp/mly1_public/2/$Z/$X/$Y?access_token=$TOKEN"
      curl -s -w "%{http_code}" $URL --max-time 120 --connect-timeout 60 -o $MVTFILE
   done
done

Erzeugen einer OSM-XML-Datei

Da ich kein Programm gefunden habe, mit dem man Vektorkacheln direkt in das OSM-XML-Format umwandeln kann, erstelle ich zunächst mit ogr2ogr und dem GDAL-Treiber für Mapbox-Kacheln eine GPKG-Datei. Mit der Option -where werden dabei die Sequenzen nach dem Aufnahmedatum gefiltert. Datumswerte werden dabei im Unixformat als Sekunden seit 01.01.1970 angegeben. Zum Ermitteln des Werts kann man den date-Befehl verwenden, z.B.:

STARTDATE=$(date -u -d2019-01-01 +%s)
OGR="ogr2ogr"
for (( X=$XMIN; X<=$XMAX; X++ ));do  
   for (( Y=$YMIN; Y<=$YMAX; Y++ ));do
      $OGR -oo X=$X -oo Y=$Y -oo Z=$Z -where "captured_at>$STARTDATE" sequence.gpkg sequence_${X}_${Y}.mvt sequence -nlt MULTILINESTRING
      OGR="ogr2ogr -append -update"
   done
done

Die GPKG-Datei kann nun mit ogr2osm in das OSM-XML-Format umgewandelt werden:

ogr2osm.py sequence.gpkg

Erstellen der Garmin-Karte

Aus der im vorangehenden Schritt erzeugten OSM-Datei sequence.osm kann jetzt mit mkgmap eine Garmin-IMG-Datei erzeugt werden. Die OSM-Datei enthält für jede Sequenz einen Polygonzug mit den aus der Kacheldatei übernommenen Attributen. Der mkgmap-Stil besteht aus einer einfachen lines-Datei, mit der allen Linien derselbe Garmin-Typ zugewiesen wird:

is_pano=* [0x30 level 5]

Der Typ 0x30 wird verwendet, weil dieser mit der vorhandenen TYP-Datei als durchgezogene grüne Linie dargestellt wird. Der Aufruf von mkgmap kann dann so aussehen:

/usr/bin/mkgmap --description=DE_Sued_Mapillary\
            --country-name=Germany --country-abbr=DE\
            --series-name=Mapillary --family-name=Mapillary\
            --area-name=DE_Sued\
            --style-file=mkgmap-style-mapillary/\
            --family-id=1310 --product-id=1\
            --gmapsupp\
            --index\
            --draw-priority=26 --latin1\
            --remove-short-arcs\
            --transparent\
    M0001310.TYP sequence.osm

Dies erzeugt eine Datei gmapsupp.img, welche auf Garmin-Geräten oder in Programmen verwendet werden kann, die dieses Format unterstützen.

Beispiel in QMapShack :

Beispiel QMapShack