Dieses Projekt zeigt, wie man Geo-Daten auf einer interaktiven Landkarte als Marker mithilfe von Python visualisiert.
Die Projektidee
Die Idee für dieses Projekt entsprang aus meinem Interesse an der mittelalterlichen Geschichte Spaniens bzw., wie es historisch korrekter ist, der mittelalterlichen Geschichte Iberiens und seiner Königreiche. Denn Spanien existierte damals noch nicht wie wir es heute kennen. Diese Geschichte ist bekanntlich vor allem deswegen sehr besonders und faszinierend, weil ein Großteil dieser Region für mehrere Jahrhunderte unter islamischer Herrschaft gestanden hatte, bevor es den christlichen Königreichen des Nordens gelingen sollte, das Territorium schrittweise zurückzuerobern. Dieser jahrhundertelange Prozess, den Historiker*innen später Reconquista, d.h. Rückeroberung, taufen sollten, endete mit dem berühmten Jahr 1492, in dem nicht nur Christopher Columbus Fuß auf die westindischen Inseln setzte, sondern auch die „Katholischen Könige“, Isabella von Kastilien und Ferdinand von Aragon, Granada erobern und damit das Ende islamischer Herrschaft Spaniens besiegeln sollten.

Jedenfalls ist ein Resultat dieser historischen Entwicklung, dass Spanien noch heute über sehr viele mittelalterliche Burgen und Burgruinen verfügt. Über diese Situation wollte ich mir ein genaueres Bild verschaffen. Eine interaktive Karte, wie man sie etwa von GoogleMap (und OpenStreetView) her kennt, schien mir dafür besonders geeignet. Nur: Wie kommt man an die dazu nötigen geographischen Daten zur Lokalisierung der Burgen auf einer Karte? Hier zeigt sich, wie stark Methoden von Web-Scraping mit Techniken der Datenvisualisierung Hand in Hand gehen können. Denn nicht immer steht eine fertige Datenbank zur Verfügung, die man bloß downloaden muss, um sie weiterzuverwenden. Manchmal muss man die Datenbank erst einmal herstellen. Und hier kommt Web-Scraping als ein Werkzeug ins Spiel, mit dem man Daten aus dem Internet sammeln und in strukturierte Datenformate überführen kann. Besonders nützlich für die Weiterverarbeitung ist das CSV-Format, mit dem sich tabellarische Daten platzsparend als formalisierte Textdateien speichern lassen und welches sich als Standard für die Datenübertragung etabliert hat. Pythons Pandas-Modul macht es relativ leicht, Daten tabellarisch in einem sogenannten Dataframe zu organisieren und in einer CSV-Datei abzuspeichern.
Schritt 1: Datenquellen recherchieren und Linkliste anlegen
Am Anfang steht jedoch erst einmal ausgiebige Recherchearbeit, um geeignete Datenquellen zu finden, die man durch Scraping herauslesen und durch Pandas organisieren kann. In diesem Fall war der Weg verhältnismäßig einfach. Die spanischsprachige Wikipedia bietet zu jeder ihrer sogenannten Communidades autónomas (autonomen Gemeinschaften), die ungefähr unseren Bundesländern entsprechen, eine Liste aller dort vorhandenen Burgen und Burgruinen an (https://es.wikipedia.org/wiki/Anexo:Castillos_de_Espa%C3%B1a). Es gibt hier jedoch ein paar Haken. So wurde in der Liste bei den HTML-Tags nicht immer eine einheitliche Struktur eingehalten. In der Regel, wie bei der Communidad Autónoma von Andalusien, wird zuerst der Name der Provinz genannt (als <h3>) und danach folgen Links zu den Burgen als Listeneinträge (<li>) unter dem gruppierenden <ul>-Tag. Als untergeordnete Headline (<h4>) folgt dann eine eigenständige Liste (wieder unter einem <ul>-Tag) anderer „ummauerter Gelände“ (Recintos amurallados), die keine Burgen im eigentlichen Sinne darstellen.

Gleichwohl lassen die Daten sich relativ leicht automatisch durch Anwendung von Requests und BeautifulSoup herausziehen. Dabei erreicht man die einzelnen relevanten Elemente am besten durch ein sukzessives Abarbeiten mit dem Befehl „soup.next_element“, wenn soup den mit BeautifulSoup geparsten Text bezeichnet. Komfortabler wäre das Scrapen geworden, wenn jeder Textabschnitt durch ein eigenes <div>-Tag identifiziert worden wäre. Daneben gibt es Sonderfälle, die mit der gewöhnlichen Struktur brechen. Die Communidades der Balearischen Inseln (Islas Baleares) oder Kantabrien (Cantabria) etwa sind nicht in Provinzen unterteilt; es gibt also keinen <h3>-Tag als Anhaltspunkt. Andere, wie Madrid, weisen dagegen eine vollständig andere Struktur auf. Diese Abweichungen mussten im Skript aufgefangen werden durch besondere Befehlsblöcke mit Anweisungen für die Sonderfälle.
Nach einiger Programmierarbeit erhält man so am Ende eine CSV-Datei mit strukturierten Daten. Aber brauchbare Geodaten hat man damit noch nicht, sondern lediglich eine Liste von Links, die zu weiteren Wikipedia-Seiten führen. Die meisten dieser Seiten wiederum enthalten Tabellen mit Zusatzinformationen zu jeder Burg, vor allem auch die uns interessierenden Geodaten. Allerdings sind einige Links auch leer, was in der Liste auf Wikipedia durch die rote Farbe der Links hervorgehoben wird. Für diese Fälle müsste man eine andere Datenquelle finden. Für Demonstrationszwecke sollen hier aber die funktionalen Wikipedia-Links genügen, womit wir immer noch eine hohe Anzahl von über 1.000 Gebäuden scrapen werden.
Schritt 2: Die Geodaten scrapen
In einem Loop wird mit Requests nun jeder Link gefolgt, um die Informationen in den Tabellen auf der Wikipedia-Seiten der Burgen in einem Dataframe zu sammeln. Da die Tabellen verschiedener Burgen unterschiedliche Kategorien aufweisen können und daher nicht alle deckungsgleich sind, ist das Script so geschrieben, dass es diese Kategorien unvoreingenommen aufnimmt und in Kolumnentitel umwandelt. In einigen Fällen sammelt man auch Werte ein, die keinen eigenen Kategoriennamen haben. Diese erhalten die Hilfsbezeichnung „Additional_Information_“ mit einer Zahl als Spaltenüberschrift („Additional_Information_1“, „Additional_Information_2“ etc.). Außerdem wird in der Überschrift das Suffix „_Link“ angefügt, wenn zum Wert außerdem ein Link gescrapt wird. Natürlich wird die Datentabelle auf diese Weise sehr groß und enthält viele Merkmale und wird dadurch unübersichtlich. Da wir aber nicht von Vorherein wissen können, welche Informationen uns zu einem späteren Zeitpunkt alle interessieren können und wie die Kategorienbezeichnung der Informationen, die uns interessieren, jeweils genannt wurden, ist wichtig, dass erst einmal keine Information verlorengeht. Überflüssiges kann im Nachgang, in der Datenreinigungsphase, immer noch gelöscht werden; oder man fasst zusammengehörende Merkmale später in einer Spalte zusammen, was man dann händisch oder durch ein eigenes Skript erledigen kann.

Nach dem zweiten Scraping-Vorgang hat man zwar nun auch die Geo-Daten der Burgen, doch kann man sie im gescrapten Format noch nicht verwenden. Sie haben die Form „40°57′09″N 4°07′57″O“ oder, in Dezimalschreibweise: „40.952573°, -4.132538°“. Wir brauchen die Dezimalform, müssen beide Teile jedoch beim Komma trennen, in eigene Spalten als Längen- und Breitengrade eintragen, und zwar als Floats, die mit dem geodätischen Referenzsystem WGS 84 kompatibel sind. Dazu müssen unnötige Zeichen, wie das Gradzeichen, entfernt werden. Für das Parsen gibt es verschiedene Methoden; ich habe eine Mischung aus Regular Expressions, Split- und Replace-Funktion verwendet sowie Pandas, um neue Spalten zu generieren und diesen die richtigen Angaben zuzuweisen. Jetzt erst ist unsere Datentabelle ausreichend für die Visualisierung vorbereitet.
Visualisierung von Geodaten
Sobald die Geodaten in üblicher Form, getrennt nach Längen- und Breitengraden, vorliegen, ist die Visualisierung relativ leicht. Herausfordernder war, an brauchbare Daten heranzukommen. Für das Web Mapping entscheide ich mich der Einfachheit halber für das Python-kompatible Folium-Paket. Zunächst laden wir die CSV mit den Daten zu den Burgen Spaniens mit Pandas in einen Dataframe (mit pd.read_csv). Eine vorab zu klärende Frage ist, worauf die Landkarte beim Öffnen zentriert werden soll. Da Madrid ziemlich genau im Zentrum Spaniens liegt, entscheide ich mich dafür; im geodätischen Referenzsystem WGS 84 lauten die Koordinaten der Hauptstadt Spaniens (40.4125, -3.703889). Ein Vorteil von Folium ist, dass sich Marker mit Python einfach umsetzen lassen. Man muss lediglich ein Tupel aus Längen- und Breitengrad als Location übergeben; den Rest erledigt Folium und fügt an diese Positionen einen Marker hinzu. Zunächst entschied ich mich für einfache Marker, doch da wir es mit sehr vielen Positionen zu tun haben, schienen mir geclusterte Marker geeigneter, da dies aufgeräumter wirkt. Außerdem lassen sich Cluster mit vielen Burgen leichter ausmachen (und die Anzahl auf einem Blick erfassen). Ich loope also durch den Dataframe, füge die Position der Burgen in jedem Datensatz dabei sukzessive in die Landkarte ein. Zusätzlich erhält jeder Marker Informationen aus dem Datensatz als Popup-Text, der erscheint, wenn man auf den Marker klickt. Der Einfachheit halber habe ich mich für den Namen der Burg und ihre Zugehörigkeit zu einer Comunidad Autónoma und Provinz entschieden. Man könnte aber auch andere Informationen ergänzen. Nicht unerwähnt lassen möchte ich, dass man als Zwischenschritt den Pandas-Dataframe auch in GeoJSON hätte konvertieren können, um es direkt mit Leaflet zu verwenden, aber das war aus meiner Sicht für diese Vorgehensweise nicht nötig, da die Daten ohnehin in Python vorlagen. Zum Schluss habe ich noch eine Minimap zur besseren Orientierung hinzugefügt.
Link zur interaktiven Karte.
