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")