KML mit GoogleEarth Placemarks in Geopandas öffnen

Alle Placemarks in allen Ordnern einer KML-Datei in einen GeoDataFrame mit minidom importieren (Python)

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