Kampis Elektroecke

Google Maps + KML Dateien

Raspberry Pi

In einem früheren Artikel habe ich euch bereits gezeigt wie ein GPS Modul an den Raspberry Pi angeschlossen und ausgelesen werden kann. Die ausgelesenen Koordinaten sollen nun in eine KML-Datei geschrieben werden, die dann in Google Maps importiert werden kann.

Was ist eine KML-Datei?:

Schauen wir uns erst einmal an, wie die KML-Datei erstellt wird. Bei einer KML-Datei handelt es sich im Grunde um eine einfache XML-Datei, welche Koordinatenpunkte oder komplette Strecken als Daten beinhaltet. Diese KML-Dateien können dann z. B. in Google-Maps oder Google-Earth importiert werden um die darin gespeicherten Koordinaten auf einer Karte sichtbar zu machen. Eine einfache KML-Datei, welche nur einen Punkt auf der Karte markiert, sieht z. B. folgendermaßen aus:

<?xml version="1.0" ?>
<kml xmls="http://earth.google.com/kml/2.2">
	<Document>
		<Placemark>
			<name>Zuhause</name>
			<Point>
				<coordinates>6.90344,51.09809,0.0</coordinates>
			</Point>
		</Placemark>
	</Document>
</kml>

Jeder Koordinateneintrag wird dabei durch ein Placemark-Tag eingeleitet, welches seinerseits aus einem name-Tag, welches den Namen des Eintrages enthält, und aus einem Point– oder einem LineStringTag besteht.
Möchte man einen Koordinatenpunkt speichern, so nimmt man ein Point-Tag und dessen coordinates-Tag beinhaltet die Koordinaten des Punktes (Länge, Breite, Höhe). Soll hingegen eine Strecke gezeichnet werden, so nimmt man ein LineString-Tag und innerhalb dieses Tags werden die Koordinaten innerhalb eines coordinates-Tag untereinander übermittelt:

<?xml version="1.0" ?>
<kml xmls="http://earth.google.com/kml/2.2"">
	<Document>
		<Placemark>
			<name>MeineStrecke</name>
			<LineString>
				<coordinates>
					6.90340,51.09809,0.0
					6.90350,51.09809,0.0
					6.90360,51.09809,0.0
				</coordinates>
			</LineString>
		</Placemark>
	</Document>
</kml>

Achtung:

Ein GPS Modul gibt die Koordinaten im falschen Format aus. Für eine KML-Datei werden die geografische Länge und Breite, sowie die Höhe über dem Meeresspiegel benötigt. Die meisten GPS-Module geben die Daten aber nur in Winkelminuten aus. Diese Koordinaten müssen dem entsprechend umgerechnet werden.


Das Python Programm:

Bevor ich mit den Anpassungen des GPS-Programms beginne, erkläre ich das Python Programm, welches die Koordinaten in das KML-File übertragen soll. Das fertige Programm besteht eigentlich nur aus drei Unterprogrammen:

  • Ein Unterprogramm um den Dokumentkopf zu erzeugen
  • Ein Unterprogramm um einen Punkt einzutragen
  • Ein Unterprogramm um eine Strecke einzutragen

Für die Erzeugung der XML Struktur habe ich das Python-Modul minidom verwendet. In diesem Modul wird Stück für Stück ein XML-Baum aufgebaut:

  1. Als erstes definiert man das Grundgerüst, welches als Basis für das gesamte XML Dokument gilt
  2. Dann kann man ein Element definieren und es dem Baum zuordnen. Das Ergebnis sieht dann so aus:
    <Baum>
    	<Element>
    	</Element>
    </Baum>
  3. Diesem Element kann man nun Attribute oder weitere Elemente zuordnen. Auf diese Weise entsteht im Laufe des Programms ein XML-Baum, welcher dann am Ende des Programms einfach in eine Datei geschrieben werden kann.

Schauen wir uns das ganze jetzt mal in der Software an…

Das Unterprogramm CreateHead dient zur Erzeugung des Dokumentkopfs und der notwendigen Objekte zum Erstellen eines XML-Baums. Es gibt als Rückgabewert ein XML-Baum Objekt und ein Objekt für das Wurzelelement, also das höchste Element, zurück (bei den beiden oberen Beispielen wäre <Document> </Document> das Wurzelelement, da die beiden anderen Elemente nur Definitionen für Google Maps sind):

(Mein_Baum, Meine_Wurzel) = CreateHead()

Der Name des Wurzelelements ist wichtig, da dieses Element als Ausgangsbasis dient und dort die Punkte oder Strecken „angeknüpft“ werden. Der komplette Baum ist anschließend in der Variable Mein_Baum gespeichert.


Wichtig:

Jedes Unterprogramm zum Erzeugen eines Punktes oder einer Linie gibt den aktualisierten Baum zurück. Einerseits muss der vorhandene XML-Baum in die Unterprogramme übergeben werden und auf der anderen Seite muss der vorhandene XML-Baum mit dem zurück gegebenen Baum der Unterprogramme aktualisiert werden, da sonst die Punkte oder Strecken nicht gespeichert werden.


Wenn man mit der Erschaffung seines XML-Baums fertig ist, kann der Baum in eine Datei geschrieben werden.

Als erstes wird mit der Zeile

Baum = Document()

der eigentliche XML-Baum mit dem Namen Baum erzeugt. Dieser XML-Baum dient ab jetzt als Basis für das komplette KML-File. Damit eine KML-Datei richtig erkannt wird, wird die Zeile:

<kml xmls="http://www.opengis.net/kml/2.2">

bzw. die Zeile

<kml xmls="http://earth.google.com/kml/2.2">

benötigt. Um diese Zeile zu erzeugen, erstelle ich ein neues Element mit dem Namen Attr. Dieses Element soll dann später den Namen kml besitzen:

Attr = Baum.createElement("kml")

Der Ausdruck xmls des Elements ist eine Variable, welches die URL als Inhalt besitzt. Um eine Variable, bzw. genauer gesagt ein Attribut, einem Element zuzuordnen wird die Methode setAttribute(Variable, Inhalt) benutzt.
Diese Methode ist ein Objekt des Elements dem die Variable zugeordnet werden soll. Da die Variable dem Element Attr zugeordnet werden soll, lautet die komplette Zeile also wie folgt:

Attr.setAttribute("xmls",http://earth.google.com/kml/2.2")

Jetzt kann das fertige Element mit der Variable an ein übergeordnetes Objekt angehängt werden. Da dieses Element das Format der XML-Datei bestimmt, muss dieses Element an den XML-Baum angehängt werden (nur so bekommt das Element die höchste Position). Um ein Element an ein Objekt anzuhängen wird die Methode
appendChild(Element) benutzt. Diese Methode gehört zu dem Objekt wo das Element angehängt werden soll, sprich um das Element Attr an den XML-Baum anzuhängen, muss die Zeile so geschrieben werden:

Baum.appendChild(Attr)

Jetzt wird das Element Attr an den XML-Baum Baum angehängt und ihm untergeordnet. Der fertige XML-Baum nimmt nun bereits diese Form an:

<?xml version="1.0" ?>
<kml xmls="http://www.opengis.net/kml/2.2">
</kml>

Genau nach diesem Schema wird jetzt das Wurzelelement, also das Element wo später die ganzen Punkte und/oder Strecken angefügt werden, erzeugt. Als erstes wird wieder ein neues Element, welches den Namen Document bekommt, erzeugt. Das Objekt nenne ich Wurzel :

Wurzel = Baum.createElement("Document")

Jetzt muss dieses Element (bzw. das Wurzelelement) noch in dem XML-Baum angeordnet werden. Durch die Zeile

Attr.appendChild(Wurzel)

ordne ich das Element unter dem Element namens Attr an, sodass der XML-Baum nun folgende Gestalt besitzt:

<?xml version="1.0" ?>
<kml xmls="http://www.opengis.net/kml/2.2">
	<Document>
	</Document>
</kml>

Zum Schluss übergebe ich den bisherigen XML-Baum und das Wurzelelement (also das Element namens Document) an das Hauptprogramm, damit auf mit dieser Grundlage weiter gearbeitet werden kann. Da das Grundgerüst für die KML-Datei nun steht, können nun Punkte und Strecken in die Datei eingetragen werden. 

Die Unterprogramme WritePoint und WriteLine unterscheiden sich nur durch die eingebaute Schleife um mehrere Koordinatenpunkte zu speichern. Wenn man sich die obigen Beispiele für eine KML-Datei anschaut, stellt man fest, dass ein Punkt bzw. eine Strecke immer mit einem Placemark-Element eingeleitet wird, welches unter dem Wurzelelement Document angeordnet ist.

Dem Placemark-Element sind wiederum die Elemente name und Point bzw. LineString untergeordnet.
Dabei gibt es für jeden Punkt und jede Linie ein einzelnes Placemark-Element, sprich wenn eine KML-Datei zwei Punkte und eine Strecke speichern soll, werden drei Placemark-Elemente benötigt. Daher macht es Sinn, für die Erzeugung dieser Elemente neutrale Funktionen zu verwenden, da man so nur einmal die Funktion zur Erzeugung definieren muss. Die fertige Funktion kann man dann einfach mit verschiedenen Parametern aufrufen und das entsprechende Element erzeugen. Das Unterprogramm zum Erzeugen eines Punktes oder einer Strecke erwarten folgende Übergabeparameter:

  • Den XML-Baum, der erweitert werden soll
  • Das Wurzel Element, an dem die Punkte oder Strecken angehängt werden sollen
  • Ein Name für den Eintrag
  • Bei Punkten: Die drei Koordinaten des Punktes
    Bei Strecken: Eine dreidimensionale Liste, welche jeden Punkt einer Strecke mit seinen drei Koordinatenpunkten beinhaltet

Nach dem Aufruf des Unterprogramms wird als erstes ein neues Element namens Placemark erzeugt und dem Wurzelelement angefügt:

Placemark = Baum.createElement("Placemark")
Wurzel.appendChild(Placemark)

Dadurch nimmt der XML-Baum folgende Gestalt an:

<?xml version="1.0" ?>
<kml xmls="http://www.opengis.net/kml/2.2">
	<Document>
		<Placemark>
		</Placemark>
	</Document>
</kml>

Dem Placemark-Element können nun die Informationen zu dem Punkt oder der Strecke angehängt werden.
Dafür wird als erstes der Name abgelegt. Hierfür wird ein neues Element mit dem Namen name geschaffen:

Name = Baum.createElement("name")

Dieses Element wird gleich dem Placemark-Element untergeordnet. Dieses Element soll jetzt aber Informationen speichern. Damit das Element Daten speichern kann, muss ein Unterelement, welches die Daten, also den Namen und später die Koordinaten, enthält. Den Namen habe ich aus dem Hauptprogramm in das Unterprogramm übergeben, wo er unter der Variable Mein_Name verfügbar ist.

Inhalt = Baum.createTextNode(Mein_Name)

Das Element ist nun unter dem Namen Inhalt verfügbar und kann nun einem Element untergeordnet werden. Da das Element dem Element name untergeordnet werden soll, sieht die Codezeile folgendermaßen aus:

Name.appendChild(Inhalt)

Das name-Element wird jetzt an das Placemark-Element angehängt:

Placemark.appendChild(Name)

Nach dieser Zeile sieht der XML-Baum bereits so aus:

<?xml version="1.0" ?>
<kml xmls="http://www.opengis.net/kml/2.2">
	<Document>
		<Placemark>
			<name>Mein Name</name>
		</Placemark>
	</Document>
</kml>

Das selbe vorgehen wird nun verwendet um die Koordinaten zu speichern. Als erstes wird das Element Point erstellt und dem PlacemarkElement angehängt:

Point = Baum.createElement("Point")
Placemark.appendChild(Point)

Falls ihr eine Strecke speichern wollt, so heißt das Element nicht Point sondern LineString. Jetzt wird das coordinates-Element erzeugt:

Coordinates = Baum.createElement(coordinates)

Danach wird das Element mit den Koordinaten erzeugt und dem Element coordinates angehängt:

Inhalt = Baum.createTextNode(....)
Coordinates.appendChild(Inhalt)

Die Koordinaten wurden dabei ebenfalls vom Hauptprogramm in das Unterprogramm übergeben und werden als Text dem Element zugeordnet. Das Element mit den Koordinaten wird nun dem coordinates-Element angehängt:

Point.appendChild(Coordinates)

Wichtig:

Bei einer Strecke werden alle Koordinaten in einem coordinates-Element gespeichert!


Am Ende des Unterprogramms wird der aktualisierte XML-Baum wieder zurück in das Hauptprogramm übergeben. Er ist nun komplett und sieht so aus:

<?xml version="1.0" ?>
<kml xmls="http://www.opengis.net/kml/2.2">
	<Document>
		<Placemark>
			<name>Mein Punkt</name>
			<Point>
				<coordinates>
					Laenge,Breite,Hoehe
				</coordinates>
			</Point>
		</Placemark>
	</Document>
</kml>

Jetzt muss der XML-Baum nur noch in eine Datei geschrieben werden. Dies geschieht über eine einfache Datei Ein- und Ausgabe:

Datei = open("Geo.xml", "wb")
Mein_Baum.writexml(Datei, "", "\t", "\n")
Datei.close()

Die Methode writeXML schreibt den XML-Baum, welcher unter dem Objekt Mein_Baum gespeichert ist, in die Datei Datei. Als Resultat erhält man eine fertige KML-Datei, die direkt in Google Maps importieren werden kann:

Das komplette Programm steht in meinem GitHub-Repository zum Download bereit.

Ein Kommentar

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert