Update: Für eine aktuellere Version siehe mein Jupyter Notebook
Beim Spielen mit Python und Geopandas wollte ich meine GoogleEarth-Ortsmarken in einen Geopandas GeoDataFrame importieren. Das war komplizierter als erwartet. Geopandas kann theoretisch KML (das von GoogleEarth verwendete Format) öffnen und parsen:
geopandas.read_file()
Es verwendet die Bibliothek fiona dafür. Diese importiert jedoch nur den ersten Ordner und meine Ortsmarken sind in vielen Ordnern organisiert. Nachdem ich mehrere auf KML spezialisierte Module ausprobiert hatte, stellte sich heraus, dass es einfacher ist, das KML mit dem Standard-Python-Modul minidom zu parsen. KML ist schließlich XML. Ich habe auch eine Spalte mit dem Pfad des Ordners erstellt (die meisten meiner Ortsmarken sind unbenannt, die Ordnernamen sind die nützlichsten Informationen). Das ist nicht wirklich schnell, aber es funktioniert.
import pandas as pd
import geopandas as gpd
import matplotlib.pyplot as plt
from xml.dom.minidom import *
# KML-Datei öffnen
dom = parse('travel.kml')
# Definiere eine Funktion, um den Pfad einer Ortsmarke zu bekommen
def subfolders(node):
if node.parentNode == dom.documentElement:
return ""
else:
foldername = node.getElementsByTagName("name")[0].firstChild.data
path = subfolders(node.parentNode) + "/" + foldername
return path
# Parse das DOM der KML-Datei
# Für jedes Placemark ein Tupel mit Name, lat, long, Ordnername, Pfad
# Hänge das Tupel an eine Liste von Tupeln an
entries = []
placemarks = dom.getElementsByTagName("Placemark")
for i in placemarks:
longitude = i.getElementsByTagName("longitude")[0].firstChild.data
latitude = i.getElementsByTagName("latitude")[0].firstChild.data
try:
name = i.getElementsByTagName("name")[0].firstChild.data
except:
name = ""
parent = i.parentNode
foldername = parent.getElementsByTagName("name")[0].firstChild.data
path = subfolders(parent)
entries.append((name, latitude, longitude, foldername, path)) # Liste von Tupeln
df = pd.DataFrame(entries, columns=('name', 'latitude', 'longitude', 'folder', 'path'))
gdf = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(df.longitude, df.latitude, crs="EPSG:4326"))
Nun können wir mit gdf.head() einen Blick auf unseren GeoDataFrame werfen, ihn als CSV speichern mit gdf.to_csv("travel.csv") und das CSV wieder öffnen mit gdf = gpd.read_file("travel.csv").
Jetzt könnten wir die Placemarks zum Beispiel auf einer einfachen Weltkarte darstellen und auch eine konvexe Hülle („Bounding Box“) um sie herum zeichnen.
# Verwende natural earth dataset als Grundkarte
natworld = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
# Für die convexe Hülle brauchen wir eine Geomtrie mit allen Placemarks
combined = gdf.dissolve()
# Plot
fig, ax = plt.subplots(figsize=(10,5))
natworld.plot(ax=ax, color="darkgrey", edgecolor="lightgrey")
gdf.plot(ax=ax, color="blue", marker=".")
combined.convex_hull.plot(ax=ax, color="none", edgecolor="red")
Ergebnis:

Grafik speichern:
fig.savefig("bounding-box.png")