Trekking-Karte mit PyGMT und Python zeichnen

Einfache Karte mit GPS-Track in Python und PyGMT am Beispiel der Tour du Mont Blanc.

Karte der Tour du Mont Blanc
Karte der Tour du Mont Blanc, erstellt mit PyGMT

Mithilfe von PyGMT ist es ziemlich einfach, gut aussehende Karten zu zeichnen. Als Beispiel zeige ich hier, wie eine einfache Trekking-Karte der Tour du Mont Blanc erstellt werden kann. Im Detail ist die Syntax etwas esoterisch und geht auf die Syntax des General Mapping Tools zurück (siehe Docs von PyGMT).

import pygmt
import geopandas as gpd
import pandas as pd

Wenn der gesamte GPS-Track eine einzige Datei ist, können wir ihn mit dem folgenden Befehl in einen GeoDataFrame einlesen. Handelt es sich um viele Dateien, siehe GPS track (GPX-Datei) in Geopandas öffnen.

track = gpd.read_file('gpx/tmb.gpx', layer='tracks')

Wir können mit track.plot() einen ersten Eindruck des Tracks bekommen, und track.total_bounds gibt die maximalen/minimalen Werte von Länge und Breite.

Mit pygmt.datasets.load_earth_relief lade ich die Topo-Daten (der NASA) aus dem Web. Für diesen Maßstab ist resolution="01s" (1 Bogensekunde) geeignet, bei Übersichtskarten (ganze Regionen, Kontinente, Welt) muss unbedingt eine andere Auflösung gewählt werden. Die Topodaten werden mit grdimage gezeichnet. Die Funktion coast kann nicht nur Küsten, sondern auch große Flüsse und Grenzen zeichnen. Die Funktion plot zeichnet die Geometrie des Tracks.

lat_min, long_min, lat_max, long_max = track.total_bounds

# Add some margin
lat_min -= 0.1
lat_max += 0.1
long_max += 0.06
long_min -= 0.05

region = [lat_min, lat_max, long_min, long_max]

grid = pygmt.datasets.load_earth_relief(resolution="01s", region=region)

fig = pygmt.Figure()
fig.grdimage(grid=grid, projection="M15c", cmap="dem2", shading=True)
#fig.colorbar(frame=["a1000", "x+lElevation", "y+lm"])

# Plot borders
fig.coast(borders=["1/0.5p,red,..-"], resolution="f")

# Plot track
fig.plot(data=track, pen="1.5p,white")
fig.plot(data=track, pen="0.8p,blue")
fig.show()

So sieht die Karte schon ganz gut aus, es fehlen aber noch die Orte und wichtige Gipfel. Ich fange mit einer einfachen Liste an und versuche, die Koordinaten per Geocoding zu bekommen.

peaks_list = [ 'Mont Blanc', 'Grandes Jorasses', 'Mont Dolent']
towns_list = ['Chamonix', 'Les Houches', 'Les Contamines', 'Courmayeur', 'Praz de Fort', 'Champex', 'Martigny']

# Get coordinates with gpd.tools.geocode
# (requires GeoPy)

peaks = gpd.tools.geocode(peaks_list)
peaks['label'] = peaks_list

towns = gpd.tools.geocode(towns_list)
towns['label'] = towns_list

Mit dem Code unten können diese Orte über die obige Karte geplottet werden. Allerdings war Les Contamines auf der Karte nicht zu sehen, weil die von geocode zurückgegebenen Koordinaten nicht korrekt waren. Ich bereite mit einer Liste einen weiteren Geodataframe vor und hänge ihn an. Außerdem definiere ich weitere „places“.

correct_towns = [['Les Contamines', 45.822746, 6.726553]]

correct_towns = pd.DataFrame(correct_towns, columns = ['label', 'latitude', 'longitude'])
correct_towns = gpd.GeoDataFrame(
    correct_towns, geometry=gpd.points_from_xy(correct_towns.longitude, correct_towns.latitude))

towns = towns.append(correct_towns)

# These places did not get usable coordinates
# with geocoding
# and I also want different markers
places = [['Col de la Forclaz', 46.059, 7.0022],
         ['Flégère', 45.9604, 6.8865]]
 
places = pd.DataFrame(places, columns = ['label', 'latitude', 'longitude'])

Jetzt können wir die Karte vervollständigen. Mit plot zeichnen wir verschiedene Markierungen für Städte, Gipfel und andere Orte; text schreibt das Label daneben (x mit kleinem Abstand).

fig.plot(data=towns, style="c0.2c", color="white", pen="black")
fig.text(text=towns.label, x=towns.geometry.x + 0.005, y=towns.geometry.y, justify="ML", font="10p")

fig.plot(x=places.longitude, y=places.latitude, style="c0.15c", color="black")
fig.text(text=places.label, x=places.longitude + 0.005, y=places.latitude, justify="ML", font="8p")

fig.plot(data=peaks, style="t0.2c", color="black")
fig.text(text=peaks.label, x=peaks.geometry.x + 0.005, y=peaks.geometry.y, justify="ML", font="10p")

fig.show()

Leider sind die Topo-Daten nicht immer ganz fehlerfrei, mit virtuellen Gipfeln und Löchern. Und die Küsten von Seen sind bei diesem Maßstab ziemlich ungenau. Die Idee, eine Karte des Torres del Paine Cirquit zu zeichnen, habe ich daher verworfen.