CSV-Dateien in KML konvertieren

Mano Marks, Google Geo APIs Team
März 2008

Ziel

In dieser Anleitung werden die Grundlagen der Erstellung von KML aus CSV-Daten (Comma Separated Value) mit Python beschrieben. CSV-Daten sind eines der am häufigsten verwendeten Dateiformate. Die meisten Tabellenkalkulations- und Datenbankprogramme können CSV-Dateien sowohl lesen als auch schreiben. Das einfache Format kann in einem Texteditor bearbeitet werden. Viele Programmiersprachen, z. B. Python, haben spezielle Bibliotheken zum Lesen und Schreiben von CSV-Dateien. Daher eignet es sich hervorragend für den Austausch großer Datenmengen.

Die Codebeispiele in dieser Anleitung sind in Python geschrieben, können aber an die meisten anderen Programmiersprachen angepasst werden. In dieser Anleitung wird Code aus Geocoding Addresses for Use in KML verwendet, um eine Adresse in Längen- und Breitenkoordinaten umzuwandeln. Außerdem wird das neue <ExtendedData>-Element von KML 2.2 verwendet und die in Benutzerdefinierte Daten hinzufügen beschriebene Ballonvorlage genutzt. Daher wird die erstellte KML-Datei derzeit nicht in Google Maps oder anderen KML-kompatiblen Anwendungen unterstützt. Der Code kann jedoch angepasst werden, um eine mit Google Maps kompatible KML-Datei zu erstellen.

Beispieldaten

Verwenden Sie für diese Anleitung die Datei google-addresses.csv als CSV-Beispieldatei. Diese Datei enthält alle Adressen, Telefonnummern und Faxnummern der verschiedenen Google-Büros in den USA. Hier ist der Text der Datei:

Office,Address1,Address2,Address3,City,State,Zip,Phone,Fax
Headquarters,1600 Amphitheatre Parkway,,,Mountain View,CA,94043,650-253-0000,650-253-0001
New York Sales & Engineering Office,76 Ninth Avenue,,,New York,NY,10011,212-565-0000,212-565-0001
Ann Arbor Sales Office,201 South Division Street,,,Ann Arbor,MI,48104,734-332-6500,734-332-6501
Atlanta Sales & Engineering Office,10 10th Street NE,,,Atlanta,GA,30309,404-487-9000,404-487-9001
Boulder Sales & Engineering Office,2590 Pearl St.,,,Boulder,CO,80302,303-245-0086,303-535-5592
Cambridge Sales & Engineering Office,5 Cambridge Center,,,Cambridge,MA,02142,617-682-3635,617-249-0199
Chicago Sales & Engineering Office,20 West Kinzie St.,,,Chicago,IL,60610,312-840-4100,312-840-4101
Coppell Sales Office,701 Canyon Drive,,,Coppell,TX,75019,214-451-4000,214-451-4001
Detroit Sales Office,114 Willits Street,,,Birmingham,MI,48009,248-351-6220,248-351-6227
Irvine Sales & Engineering Office,19540 Jamboree Road,,,Irvine,CA,92612,949-794-1600,949-794-1601
Pittsburgh Engineering Office,4720 Forbes Avenue,,,Pittsburgh,PA,15213,,
Santa Monica Sales & Engineering Office,604 Arizona Avenue,,,Santa Monica,CA,90401,310-460-4000,310-309-6840
Seattle Engineering Office,720 4th Avenue,,,Kirkland,WA,98033,425-739-5600,425-739-5601
Seattle Sales Office,501 N. 34th Street,,,Seattle,WA,98103,206-876-1500,206-876-1501
Washington D.C. Public Policy Office,1001 Pennsylvania Avenue NW,,,Washington,DC,20004,202-742-6520,

Jede Zeile besteht aus einer Reihe von durch Kommas getrennten Textstrings. Jedes Komma trennt ein Feld. Jede Zeile hat dieselbe Anzahl von Kommas. Die erste Zeile enthält die Namen der Felder in der richtigen Reihenfolge. Der erste Textblock in jeder Zeile ist beispielsweise das Feld „Office“, der zweite „Address1“ usw. Python kann daraus eine Sammlung von dicts erstellen, die als DictReader bezeichnet wird und mit der Sie jede Zeile durchlaufen können. In diesem Codebeispiel wird davon ausgegangen, dass Sie die Struktur Ihrer Daten im Voraus kennen. Sie könnten jedoch einige einfache Handler hinzufügen, um die Feldstruktur dynamisch zu übergeben.

CSV-Datei parsen

Das xml.dom.minidom-Modul von Python bietet hervorragende Tools zum Erstellen von XML-Dokumenten. Da KML XML ist, werden Sie es in diesem Tutorial häufig verwenden. Sie erstellen ein Element mit createElement oder createElementNS und hängen es mit appendChild an ein anderes Element an. So parsen Sie die CSV-Datei und erstellen eine KML-Datei:

  1. Importieren Sie „geocoding_for_kml.py“ in Ihr Modul.
  2. Erstellen Sie ein DictReader für die CSV-Dateien. Das DictReader ist eine Sammlung von dicts, eines für jede Zeile.
  3. Erstellen Sie das Dokument mit xml.dom.minidom.Document() von Python.
  4. Erstellen Sie das <kml>-Rootelement mit createElementNS..
  5. Hängen Sie sie an das Dokument an.
  6. Erstellen Sie mit createElement ein <Document>-Element.
  7. Hängen Sie es mit appendChild an das <kml>-Element an.
  8. Erstellen Sie für jede Zeile ein <Placemark>-Element und hängen Sie es an das <Document>-Element an.
  9. Erstellen Sie für jede Spalte in jeder Zeile ein <ExtendedData>-Element und hängen Sie es an das <Placemark>-Element an, das Sie in Schritt 8 erstellt haben.
  10. Erstellen Sie ein <Data>-Element und hängen Sie es an das <ExtendedData>-Element an. Weisen Sie dem <Data>-Element ein Attribut mit dem Namen „name“ zu und weisen Sie ihm mit setAttribute den Wert des Spaltennamens zu.
  11. Erstellen Sie ein <value>-Element und hängen Sie es an das <Data>-Element an. Erstellen Sie einen Textknoten und weisen Sie ihm mit createTextNode den Wert der Spalte zu. Hängen Sie den Textknoten an das <value>-Element an.
  12. Erstellen Sie ein <Point>-Element und hängen Sie es an das <Placemark>-Element an. Erstellen Sie ein <coordinates>-Element und hängen Sie es an das <Point>-Element an.
  13. Extrahieren Sie die Adresse aus der Zeile, sodass sie ein einzelner String in diesem Format ist: Address1,Address2,City,State,Zip. Die erste Zeile wäre also 1600 Amphitheater Parkway,,Mountain View,CA,94043. Es ist in Ordnung, wenn Kommas nebeneinander stehen. Dazu sind Vorkenntnisse der Struktur der CSV-Datei und der Spalten, aus denen die Adresse besteht, erforderlich.
  14. Geocoden Sie die Adresse mit dem Code geocoding_for_kml.py, der unter Geocoding von Adressen zur Verwendung in KML beschrieben wird. Dadurch wird ein String zurückgegeben, der den Längen- und Breitengrad des Standorts enthält.
  15. Erstellen Sie einen Textknoten und weisen Sie ihm den Wert der Koordinaten aus Schritt 14 zu. Hängen Sie ihn dann an das <coordinates>-Element an.
  16. Schreiben Sie das KML-Dokument in eine Datei.
  17. Wenn Sie eine Liste mit Spaltennamen als Argumente an das Script übergeben, werden die Elemente in dieser Reihenfolge hinzugefügt. Wenn uns die Reihenfolge der Elemente egal wäre, könnten wir dict.keys() verwenden, um eine list zu erstellen. Bei dict.keys() wird die ursprüngliche Reihenfolge aus dem Dokument jedoch nicht beibehalten. Wenn Sie dieses Argument verwenden möchten, übergeben Sie die Liste der Feldnamen als durch Kommas getrennte Liste, z. B.:
    python csvtokml.py Office,Address1,Address2,Address3,City,State,Zip,Phone,Fax

Python-Beispielcode

Unten sehen Sie ein Beispiel für Code zum Erstellen einer KML-Datei aus einer CSV-Datei mit Python 2.2. Sie können sie auch hier herunterladen.


import geocoding_for_kml
import csv
import xml
.dom.minidom
import sys


def extractAddress
(row):
  # This extracts an address from a row and returns it as a string. This requires knowing
  # ahead of time what the columns are that hold the address information.
  return '%s,%s,%s,%s,%s' % (row['Address1'], row['Address2'], row['City'], row['State'], row['Zip'])

def createPlacemark(kmlDoc, row, order):
  # This creates a  element for a row of data.
  # A row is a dict.
  placemarkElement = kmlDoc.createElement('Placemark')
  extElement = kmlDoc.createElement('ExtendedData')
  placemarkElement.appendChild(extElement)
  
  # Loop through the columns and create a  element for every field that has a value.
  for key in order:
    if row[key]:
      dataElement = kmlDoc.createElement('Data')
      dataElement.setAttribute('name', key)
      valueElement = kmlDoc.createElement('value')
      dataElement.appendChild(valueElement)
      valueText = kmlDoc.createTextNode(row[key])
      valueElement.appendChild(valueText)
      extElement.appendChild(dataElement)
  
  pointElement = kmlDoc.createElement('Point')
  placemarkElement.appendChild(pointElement)
  coordinates = geocoding_for_kml.geocode(extractAddress(row))
  coorElement = kmlDoc.createElement('coordinates')
  coorElement.appendChild(kmlDoc.createTextNode(coordinates))
  pointElement.appendChild(coorElement)
  return placemarkElement

def createKML(csvReader, fileName, order):
  # This constructs the KML document from the CSV file.
  kmlDoc = xml.dom.minidom.Document()
  
  kmlElement = kmlDoc.createElementNS('http://earth.google.com/kml/2.2', 'kml')
  kmlElement.setAttribute('xmlns','http://earth.google.com/kml/2.2')
  kmlElement = kmlDoc.appendChild(kmlElement)
  documentElement = kmlDoc.createElement('Document')
  documentElement = kmlElement.appendChild(documentElement)

  # Skip the header line.
  csvReader.next()
  
  for row in csvReader:
    placemarkElement = createPlacemark(kmlDoc, row, order)
    documentElement.appendChild(placemarkElement)
  kmlFile = open(fileName, 'w')
  kmlFile.write(kmlDoc.toprettyxml('  ', newl = '\n', encoding = 'utf-8'))

def main():
  # This reader opens up 'google-addresses.csv', which should be replaced with your own.
  # It creates a KML file called 'google.kml'.
  
  # If an argument was passed to the script, it splits the argument on a comma
  # and uses the resulting list to specify an order for when columns get added.
  # Otherwise, it defaults to the order used in the sample.
  
  if len(sys.argv) >1: order = sys.argv[1].split(',')
  else: order = ['Office','Address1','Address2','Address3','City','State','Zip','Phone','Fax']
  csvreader = csv.DictReader(open('google-addresses.csv'),order)
  kml = createKML(csvreader, 'google-addresses.kml', order)
if __name__ == '__main__':
  main()

Beispiel-KML-Datei erstellt

Unten sehen Sie ein Beispiel für das KML, das von diesem Skript erstellt wird. Beachten Sie, dass einige<value>-Elemente nur Leerzeichen enthalten. Das liegt daran, dass das Feld keine Daten enthielt. Das vollständige Beispiel können Sie hier herunterladen.

<?xml version="1.0" encoding="utf-8"?>
<kml xmlns="http://earth.google.com/kml/2.2">
  <Document>
    <Placemark>
      <ExtendedData>
        <Data name="Office">
          <value>
            Headquarters
          </value>
        </Data>
        <Data name="Address1">
          <value>
            1600 Amphitheater Parkway
          </value>
        </Data>
        <Data name="City">
          <value>
            Mountain View
          </value>
        </Data>
        <Data name="State">
          <value>
            CA
          </value>
        </Data>
        <Data name="Zip">
          <value>
            94043
          </value>
        </Data>
        <Data name="Phone">
          <value>
            650-253-0000
          </value>
        </Data>
        <Data name="Fax">
          <value>
            650-253-0001
          </value>
        </Data>
      </ExtendedData>
      <Point>
        <coordinates>
          -122.081783,37.423111
        </coordinates>
      </Point>
    </Placemark>
    ...

Screen shot

Unten sehen Sie einen Screenshot, wie diese KML-Datei in Google Earth aussieht. Da jedes <Placemark>-Element kein <BalloonStyle><text>- und kein <description>-Element hat, wird für die Sprechblase standardmäßig ein Tabellenstil verwendet, der auf den <Data>-Elementen basiert.

Screenshot der von diesem Skript erstellten KML-Datei

Geocoding-Überlegungen

Das wurde bereits im Abschnitt „Adressen für die Verwendung in KML geocodieren“ erwähnt, aber es ist wichtig, es noch einmal zu betonen. Für Ihre Geocoding-Anfragen gilt die maximale Abfragerate des Geocoders und ein Limit von 15.000 Abfragen pro Tag basierend auf Ihrer IP-Adresse. Außerdem gibt der Geocoder den Statuscode 620 zurück, wenn Sie ihn schneller abfragen, als er verarbeiten kann. Eine vollständige Liste der Statuscodes finden Sie hier. Damit Sie nicht zu schnell Anfragen an den Geocoder senden, können Sie eine Verzögerung zwischen den einzelnen Geocode-Anfragen festlegen. Sie können diese Verzögerung jedes Mal erhöhen, wenn Sie den Status 620 erhalten, und eine while-Schleife verwenden, um sicherzustellen, dass Sie eine Adresse erfolgreich geocodiert haben, bevor Sie zur nächsten Adresse wechseln. Wenn Ihre CSV-Datei sehr groß ist, müssen Sie möglicherweise den Geocoding-Code ändern oder darauf achten, wie schnell Sie Ortsmarken erstellen, und die Geschwindigkeit verringern, wenn Sie zu schnell vorgehen.

Fazit

Jetzt können Sie mit Python eine KML-Datei aus einer CSV-Datei erstellen. Mit dem bereitgestellten Code funktioniert die KML-Datei nur in Google Earth. Sie können sie so anpassen, dass sie sowohl in Maps als auch in Earth funktioniert, indem Sie <description> anstelle von <ExtendedData> verwenden. Dieses Codebeispiel lässt sich auch ganz einfach in andere Programmiersprachen konvertieren, die XML unterstützen.

Nachdem Sie alle CSV-Dateien in KML konvertiert haben, können Sie sich weitere KML-Artikel ansehen, z. B. KML mit PHP und MySQL erstellen und den Google Developers-Artikel zu ExtendedData, Benutzerdefinierte Daten hinzufügen.

Nach oben